completed the xml tests - and lots of them run successfully
This commit is contained in:
parent
f9542b90db
commit
f932f560bd
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -433,7 +433,8 @@ module.exports = function() {
|
||||
position: this.getPosition(),
|
||||
object: this.parent.getCustomType(),
|
||||
length: 1,
|
||||
changedBy: o.uid.creator
|
||||
changedBy: o.uid.creator,
|
||||
oldValue: this.val()
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
@ -1,50 +1,11 @@
|
||||
var dont_proxy, json_types_uninitialized, proxy_token, _proxy,
|
||||
var json_types_uninitialized,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
__hasProp = {}.hasOwnProperty;
|
||||
|
||||
json_types_uninitialized = require("./JsonTypes");
|
||||
|
||||
proxy_token = false;
|
||||
|
||||
dont_proxy = function(f) {
|
||||
var e;
|
||||
proxy_token = true;
|
||||
try {
|
||||
f();
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
proxy_token = false;
|
||||
throw new Error(e);
|
||||
}
|
||||
return proxy_token = false;
|
||||
};
|
||||
|
||||
_proxy = function(f_name, f) {
|
||||
var old_f;
|
||||
old_f = this[f_name];
|
||||
if (old_f != null) {
|
||||
return this[f_name] = function() {
|
||||
var args, that, _ref;
|
||||
if (!proxy_token && !((_ref = this._y) != null ? _ref.isDeleted() : void 0)) {
|
||||
that = this;
|
||||
args = arguments;
|
||||
return dont_proxy(function() {
|
||||
f.apply(that, args);
|
||||
return old_f.apply(that, args);
|
||||
});
|
||||
} else {
|
||||
return old_f.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof Element !== "undefined" && Element !== null) {
|
||||
Element.prototype._proxy = _proxy;
|
||||
}
|
||||
|
||||
module.exports = function(HB) {
|
||||
var TextNodeType, XmlType, json_types, parser, types;
|
||||
var XmlType, json_types, parser, types;
|
||||
json_types = json_types_uninitialized(HB);
|
||||
types = json_types.types;
|
||||
parser = json_types.parser;
|
||||
@ -52,234 +13,11 @@ module.exports = function(HB) {
|
||||
__extends(XmlType, _super);
|
||||
|
||||
function XmlType(uid, _at_tagname, attributes, elements, _at_xml) {
|
||||
var attr, d, element, i, n, word, _i, _j, _len, _ref, _ref1, _ref2;
|
||||
this.tagname = _at_tagname;
|
||||
this.xml = _at_xml;
|
||||
|
||||
/* In case you make this instanceof Insert again
|
||||
if prev? and (not next?) and prev.type?
|
||||
* adjust what you actually mean. you want to insert after prev, then
|
||||
* next is not defined. but we only insert after non-deleted elements.
|
||||
* This is also handled in TextInsert.
|
||||
while prev.isDeleted()
|
||||
prev = prev.prev_cl
|
||||
next = prev.next_cl
|
||||
*/
|
||||
XmlType.__super__.constructor.call(this, uid);
|
||||
if (((_ref = this.xml) != null ? _ref._y : void 0) != null) {
|
||||
d = new types.Delete(void 0, this.xml._y);
|
||||
HB.addOperation(d).execute();
|
||||
this.xml._y = null;
|
||||
}
|
||||
if ((attributes != null) && (elements != null)) {
|
||||
this.saveOperation('attributes', attributes);
|
||||
this.saveOperation('elements', elements);
|
||||
} else if ((attributes == null) && (elements == null)) {
|
||||
this.attributes = new types.JsonType();
|
||||
this.attributes.setMutableDefault('immutable');
|
||||
HB.addOperation(this.attributes).execute();
|
||||
this.elements = new types.WordType();
|
||||
this.elements.parent = this;
|
||||
HB.addOperation(this.elements).execute();
|
||||
} else {
|
||||
throw new Error("Either define attribute and elements both, or none of them");
|
||||
}
|
||||
if (this.xml != null) {
|
||||
this.tagname = this.xml.tagName;
|
||||
for (i = _i = 0, _ref1 = this.xml.attributes.length; 0 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 0 <= _ref1 ? ++_i : --_i) {
|
||||
attr = xml.attributes[i];
|
||||
this.attributes.val(attr.name, attr.value);
|
||||
}
|
||||
_ref2 = this.xml.childNodes;
|
||||
for (_j = 0, _len = _ref2.length; _j < _len; _j++) {
|
||||
n = _ref2[_j];
|
||||
if (n.nodeType === n.TEXT_NODE) {
|
||||
word = new TextNodeType(void 0, n);
|
||||
HB.addOperation(word).execute();
|
||||
this.elements.push(word);
|
||||
} else if (n.nodeType === n.ELEMENT_NODE) {
|
||||
element = new XmlType(void 0, void 0, void 0, void 0, n);
|
||||
HB.addOperation(element).execute();
|
||||
this.elements.push(element);
|
||||
} else {
|
||||
throw new Error("I don't know Node-type " + n.nodeType + "!!");
|
||||
}
|
||||
}
|
||||
this.setXmlProxy();
|
||||
}
|
||||
void 0;
|
||||
}
|
||||
|
||||
XmlType.prototype.type = "XmlType";
|
||||
|
||||
XmlType.prototype.applyDelete = function(op) {
|
||||
if ((this.insert_parent != null) && !this.insert_parent.isDeleted()) {
|
||||
return this.insert_parent.applyDelete(op);
|
||||
} else {
|
||||
this.attributes.applyDelete();
|
||||
this.elements.applyDelete();
|
||||
return XmlType.__super__.applyDelete.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
XmlType.prototype.cleanup = function() {
|
||||
return XmlType.__super__.cleanup.call(this);
|
||||
};
|
||||
|
||||
XmlType.prototype.setXmlProxy = function() {
|
||||
var findNode, insertBefore, removeChild, renewClassList, that;
|
||||
this.xml._y = this;
|
||||
that = this;
|
||||
this.elements.on('insert', function(event, op) {
|
||||
var newNode, right, rightNode;
|
||||
if (op.creator !== HB.getUserId() && this === that.elements) {
|
||||
newNode = op.content.val();
|
||||
right = op.next_cl;
|
||||
while ((right != null) && right.isDeleted()) {
|
||||
right = right.next_cl;
|
||||
}
|
||||
rightNode = null;
|
||||
if (right.type !== 'Delimiter') {
|
||||
rightNode = right.val().val();
|
||||
}
|
||||
return dont_proxy(function() {
|
||||
return that.xml.insertBefore(newNode, rightNode);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.elements.on('delete', function(event, op) {
|
||||
var del_op, deleted;
|
||||
del_op = op.deleted_by[0];
|
||||
if ((del_op != null) && del_op.creator !== HB.getUserId() && this === that.elements) {
|
||||
deleted = op.content.val();
|
||||
return dont_proxy(function() {
|
||||
return that.xml.removeChild(deleted);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.attributes.on(['add', 'update'], function(event, property_name, op) {
|
||||
if (op.creator !== HB.getUserId() && this === that.attributes) {
|
||||
return dont_proxy(function() {
|
||||
var newval;
|
||||
newval = op.val().val();
|
||||
if (newval != null) {
|
||||
return that.xml.setAttribute(property_name, op.val().val());
|
||||
} else {
|
||||
return that.xml.removeAttribute(property_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
findNode = function(child) {
|
||||
var elem;
|
||||
if (child == null) {
|
||||
throw new Error("you must specify a parameter!");
|
||||
}
|
||||
child = child._y;
|
||||
elem = that.elements.beginning.next_cl;
|
||||
while (elem.type !== 'Delimiter' && elem.content !== child) {
|
||||
elem = elem.next_cl;
|
||||
}
|
||||
if (elem.type === 'Delimiter') {
|
||||
return false;
|
||||
} else {
|
||||
return elem;
|
||||
}
|
||||
};
|
||||
insertBefore = function(insertedNode_s, adjacentNode) {
|
||||
var child, element, inserted_nodes, next, prev, _results;
|
||||
next = null;
|
||||
if (adjacentNode != null) {
|
||||
next = findNode(adjacentNode);
|
||||
}
|
||||
prev = null;
|
||||
if (next) {
|
||||
prev = next.prev_cl;
|
||||
} else {
|
||||
prev = this._y.elements.end.prev_cl;
|
||||
while (prev.isDeleted()) {
|
||||
prev = prev.prev_cl;
|
||||
}
|
||||
}
|
||||
inserted_nodes = null;
|
||||
if (insertedNode_s.nodeType === insertedNode_s.DOCUMENT_FRAGMENT_NODE) {
|
||||
child = insertedNode_s.lastChild;
|
||||
_results = [];
|
||||
while (child != null) {
|
||||
element = new XmlType(void 0, void 0, void 0, void 0, child);
|
||||
HB.addOperation(element).execute();
|
||||
that.elements.insertAfter(prev, element);
|
||||
_results.push(child = child.previousSibling);
|
||||
}
|
||||
return _results;
|
||||
} else {
|
||||
element = new XmlType(void 0, void 0, void 0, void 0, insertedNode_s);
|
||||
HB.addOperation(element).execute();
|
||||
return that.elements.insertAfter(prev, element);
|
||||
}
|
||||
};
|
||||
this.xml._proxy('insertBefore', insertBefore);
|
||||
this.xml._proxy('appendChild', insertBefore);
|
||||
this.xml._proxy('removeAttribute', function(name) {
|
||||
return that.attributes.val(name, void 0);
|
||||
});
|
||||
this.xml._proxy('setAttribute', function(name, value) {
|
||||
return that.attributes.val(name, value);
|
||||
});
|
||||
renewClassList = function(newclass) {
|
||||
var dont_do_it, elem, value, _i, _len;
|
||||
dont_do_it = false;
|
||||
if (newclass != null) {
|
||||
for (_i = 0, _len = this.length; _i < _len; _i++) {
|
||||
elem = this[_i];
|
||||
if (newclass === elem) {
|
||||
dont_do_it = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
value = Array.prototype.join.call(this, " ");
|
||||
if ((newclass != null) && !dont_do_it) {
|
||||
value += " " + newclass;
|
||||
}
|
||||
return that.attributes.val('class', value);
|
||||
};
|
||||
_proxy.call(this.xml.classList, 'add', renewClassList);
|
||||
_proxy.call(this.xml.classList, 'remove', renewClassList);
|
||||
this.xml.__defineSetter__('className', function(val) {
|
||||
return this.setAttribute('class', val);
|
||||
});
|
||||
this.xml.__defineGetter__('className', function() {
|
||||
return that.attributes.val('class');
|
||||
});
|
||||
this.xml.__defineSetter__('textContent', function(val) {
|
||||
var elem, remove, text_node;
|
||||
elem = that.xml.firstChild;
|
||||
while (elem != null) {
|
||||
remove = elem;
|
||||
elem = elem.nextSibling;
|
||||
that.xml.removeChild(remove);
|
||||
}
|
||||
if (val !== "") {
|
||||
text_node = document.createTextNode(val);
|
||||
return that.xml.appendChild(text_node);
|
||||
}
|
||||
});
|
||||
removeChild = function(node) {
|
||||
var d, elem;
|
||||
elem = findNode(node);
|
||||
if (!elem) {
|
||||
throw new Error("You are only allowed to delete existing (direct) child elements!");
|
||||
}
|
||||
d = new types.Delete(void 0, elem);
|
||||
HB.addOperation(d).execute();
|
||||
return node._y = null;
|
||||
};
|
||||
this.xml._proxy('removeChild', removeChild);
|
||||
return this.xml._proxy('replaceChild', function(insertedNode, replacedNode) {
|
||||
insertBefore.call(this, insertedNode, replacedNode);
|
||||
return removeChild.call(this, replacedNode);
|
||||
});
|
||||
};
|
||||
XmlType.prototype.setXmlProxy = function() {};
|
||||
|
||||
XmlType.prototype.val = function(enforce) {
|
||||
var a, attr, attr_name, e, n, text_node, value;
|
||||
@ -351,54 +89,9 @@ module.exports = function(HB) {
|
||||
return XmlType;
|
||||
|
||||
})(types.Insert);
|
||||
parser['XmlType'] = function(json) {
|
||||
return parser['XmlType'] = function(json) {
|
||||
var attributes, elements, tagname, uid;
|
||||
uid = json['uid'], attributes = json['attributes'], elements = json['elements'], tagname = json['tagname'];
|
||||
return new XmlType(uid, tagname, attributes, elements, void 0);
|
||||
};
|
||||
TextNodeType = (function(_super) {
|
||||
__extends(TextNodeType, _super);
|
||||
|
||||
function TextNodeType(uid, content) {
|
||||
var d;
|
||||
if (content._y != null) {
|
||||
d = new types.Delete(void 0, content._y);
|
||||
HB.addOperation(d).execute();
|
||||
content._y = null;
|
||||
}
|
||||
content._y = this;
|
||||
TextNodeType.__super__.constructor.call(this, uid, content);
|
||||
}
|
||||
|
||||
TextNodeType.prototype.applyDelete = function(op) {
|
||||
if ((this.insert_parent != null) && !this.insert_parent.isDeleted()) {
|
||||
return this.insert_parent.applyDelete(op);
|
||||
} else {
|
||||
return TextNodeType.__super__.applyDelete.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
TextNodeType.prototype.type = "TextNodeType";
|
||||
|
||||
TextNodeType.prototype._encode = function() {
|
||||
var json;
|
||||
json = {
|
||||
'type': this.type,
|
||||
'uid': this.getUid(),
|
||||
'content': this.content.textContent
|
||||
};
|
||||
return json;
|
||||
};
|
||||
|
||||
return TextNodeType;
|
||||
|
||||
})(types.ImmutableObject);
|
||||
parser['TextNodeType'] = function(json) {
|
||||
var content, textnode, uid;
|
||||
uid = json['uid'], content = json['content'];
|
||||
textnode = document.createTextNode(content);
|
||||
return new TextNodeType(uid, textnode);
|
||||
};
|
||||
types['XmlType'] = XmlType;
|
||||
return json_types;
|
||||
};
|
||||
|
@ -1,78 +1,131 @@
|
||||
var YXml;
|
||||
var YXml, dont_proxy, initialize_proxies, proxies_are_initialized, proxy_token;
|
||||
|
||||
YXml = (function() {
|
||||
function YXml(tagname, attributes) {
|
||||
var a, a_name, c, c_name, _classes, _i, _len, _ref;
|
||||
function YXml(tag_or_dom, attributes) {
|
||||
var a, a_name, c, c_name, tagname, _classes, _i, _len, _ref;
|
||||
if (attributes == null) {
|
||||
attributes = {};
|
||||
}
|
||||
this._xml = {};
|
||||
this._xml.tagname = tagname;
|
||||
if (attributes.constructor !== Object) {
|
||||
throw new Error("The attributes must be specified as a Object");
|
||||
}
|
||||
for (a_name in attributes) {
|
||||
a = attributes[a_name];
|
||||
if (a.constructor !== String) {
|
||||
throw new Error("The attributes must be of type String!");
|
||||
if (tag_or_dom == null) {
|
||||
|
||||
} else if (tag_or_dom.constructor === String) {
|
||||
tagname = tag_or_dom;
|
||||
this._xml = {};
|
||||
this._xml.children = [];
|
||||
this._xml.tagname = tagname;
|
||||
if (attributes.constructor !== Object) {
|
||||
throw new Error("The attributes must be specified as a Object");
|
||||
}
|
||||
}
|
||||
this._xml.attributes = attributes;
|
||||
this._xml.classes = {};
|
||||
_classes = this._xml.attributes["class"];
|
||||
delete this._xml.attributes["class"];
|
||||
if (_classes != null) {
|
||||
_ref = _classes.split(" ");
|
||||
for (c = _i = 0, _len = _ref.length; _i < _len; c = ++_i) {
|
||||
c_name = _ref[c];
|
||||
if (c.length > 0) {
|
||||
this._xml.classes[c_name] = c;
|
||||
for (a_name in attributes) {
|
||||
a = attributes[a_name];
|
||||
if (a.constructor !== String) {
|
||||
throw new Error("The attributes must be of type String!");
|
||||
}
|
||||
}
|
||||
this._xml.attributes = attributes;
|
||||
this._xml.classes = {};
|
||||
_classes = this._xml.attributes["class"];
|
||||
delete this._xml.attributes["class"];
|
||||
if (_classes != null) {
|
||||
_ref = _classes.split(" ");
|
||||
for (c = _i = 0, _len = _ref.length; _i < _len; c = ++_i) {
|
||||
c_name = _ref[c];
|
||||
if (c.length > 0) {
|
||||
this._xml.classes[c_name] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
void 0;
|
||||
} else if (tag_or_dom instanceof Element) {
|
||||
this._dom = tag_or_dom;
|
||||
this._xml = {};
|
||||
}
|
||||
void 0;
|
||||
}
|
||||
|
||||
YXml.prototype._name = "Xml";
|
||||
|
||||
YXml.prototype._getModel = function(Y, ops) {
|
||||
var attribute, c, child, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
|
||||
if (this._model == null) {
|
||||
if (this._dom != null) {
|
||||
this._xml.tagname = this._dom.tagName.toLowerCase();
|
||||
this._xml.attributes = {};
|
||||
this._xml.classes = {};
|
||||
_ref = this._dom.attributes;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
attribute = _ref[_i];
|
||||
if (attribute.name === "class") {
|
||||
_ref1 = attribute.value.split(" ");
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
c = _ref1[_j];
|
||||
this._xml.classes[c] = true;
|
||||
}
|
||||
} else {
|
||||
this._xml.attributes[attribute.name] = attribute.value;
|
||||
}
|
||||
}
|
||||
this._xml.children = [];
|
||||
_ref2 = this._dom.childNodes;
|
||||
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
|
||||
child = _ref2[_k];
|
||||
if (child.nodeType === child.TEXT_NODE) {
|
||||
this._xml.children.push(child.textContent);
|
||||
} else {
|
||||
this._xml.children.push(new YXml(child));
|
||||
}
|
||||
}
|
||||
}
|
||||
this._model = new ops.MapManager(this).execute();
|
||||
this._model.val("attributes", new Y.Object(this._xml.attributes));
|
||||
this._model.val("classes", new Y.Object(this._xml.classes));
|
||||
this._model.val("tagname", this._xml.tagname);
|
||||
this._model.val("children", new Y.List());
|
||||
this._model.val("children", new Y.List(this._xml.children));
|
||||
if (this._xml.parent != null) {
|
||||
this._model.val("parent", this._xml.parent);
|
||||
}
|
||||
if (this._dom != null) {
|
||||
this.getDom();
|
||||
}
|
||||
this._setModel(this._model);
|
||||
}
|
||||
this._setModel(this._model);
|
||||
return this._model;
|
||||
};
|
||||
|
||||
YXml.prototype._setModel = function(_at__model) {
|
||||
this._model = _at__model;
|
||||
delete this._xml;
|
||||
return this._model.observe(function(events) {
|
||||
var c, children, event, i, parent, _i, _j, _len, _len1, _ref;
|
||||
this._model.observe(function(events) {
|
||||
var c, children, event, i, parent, _i, _len, _ref, _results;
|
||||
_results = [];
|
||||
for (_i = 0, _len = events.length; _i < _len; _i++) {
|
||||
event = events[_i];
|
||||
if (event.name === "parent" && event.type !== "add") {
|
||||
parent = event.oldValue;
|
||||
children = (_ref = parent._model.val("children")) != null ? _ref.val() : void 0;
|
||||
if (children != null) {
|
||||
for (i = _j = 0, _len1 = children.length; _j < _len1; i = ++_j) {
|
||||
c = children[i];
|
||||
if (c === this) {
|
||||
parent._model.val("children")["delete"](i);
|
||||
break;
|
||||
_results.push((function() {
|
||||
var _j, _len1, _results1;
|
||||
_results1 = [];
|
||||
for (i = _j = 0, _len1 = children.length; _j < _len1; i = ++_j) {
|
||||
c = children[i];
|
||||
if (c === this) {
|
||||
parent._model.val("children")["delete"](i);
|
||||
break;
|
||||
} else {
|
||||
_results1.push(void 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _results1;
|
||||
}).call(this));
|
||||
} else {
|
||||
_results.push(void 0);
|
||||
}
|
||||
} else {
|
||||
_results.push(void 0);
|
||||
}
|
||||
}
|
||||
return void 0;
|
||||
return _results;
|
||||
});
|
||||
return delete this._xml;
|
||||
};
|
||||
|
||||
YXml.prototype._setParent = function(parent) {
|
||||
@ -294,10 +347,225 @@ YXml = (function() {
|
||||
return this._model.val("children").val();
|
||||
};
|
||||
|
||||
YXml.prototype.getPosition = function() {
|
||||
var c, i, parent, _i, _len, _ref;
|
||||
parent = this._model.val("parent");
|
||||
if (parent != null) {
|
||||
_ref = parent._model.val("children").val();
|
||||
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
||||
c = _ref[i];
|
||||
if (c === this) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new Error("This is not a child of its parent (should not happen in Y.Xml!)");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
YXml.prototype.getDom = function() {
|
||||
var attr_name, attr_value, child, dom, i, that, _i, _len, _ref, _ref1;
|
||||
if (this._dom == null) {
|
||||
this._dom = document.createElement(this._model.val("tagname"));
|
||||
_ref = this.attr();
|
||||
for (attr_name in _ref) {
|
||||
attr_value = _ref[attr_name];
|
||||
this._dom.setAttribute(attr_name, attr_value);
|
||||
}
|
||||
_ref1 = this.getChildren();
|
||||
for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) {
|
||||
child = _ref1[i];
|
||||
if (child.constructor === String) {
|
||||
dom = document.createTextNode(child);
|
||||
} else {
|
||||
dom = child.getDom();
|
||||
}
|
||||
this._dom.insertBefore(dom);
|
||||
}
|
||||
}
|
||||
that = this;
|
||||
if (this._dom._y_xml == null) {
|
||||
this._dom._y_xml = this;
|
||||
initialize_proxies.call(this);
|
||||
this._model.val("children").observe(function(events) {
|
||||
var children, deleted, event, newNode, rightNode, _j, _len1, _results;
|
||||
_results = [];
|
||||
for (_j = 0, _len1 = events.length; _j < _len1; _j++) {
|
||||
event = events[_j];
|
||||
if (event.type === "insert") {
|
||||
newNode = event.value.getDom();
|
||||
children = that._dom.childNodes;
|
||||
if (children.length > 0) {
|
||||
rightNode = children[0];
|
||||
} else {
|
||||
rightNode = null;
|
||||
}
|
||||
event.value._setParent(that);
|
||||
_results.push(dont_proxy(function() {
|
||||
return that._dom.insertBefore(newNode, rightNode);
|
||||
}));
|
||||
} else if (event.type === "delete") {
|
||||
deleted = event.oldValue.getDom();
|
||||
_results.push(dont_proxy(function() {
|
||||
return that._dom.removeChild(deleted);
|
||||
}));
|
||||
} else {
|
||||
_results.push(void 0);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
this._model.val("attributes").observe(function(events) {
|
||||
var event, newval, _j, _len1, _results;
|
||||
_results = [];
|
||||
for (_j = 0, _len1 = events.length; _j < _len1; _j++) {
|
||||
event = events[_j];
|
||||
if (event.type === "add" || event.type === "update") {
|
||||
newval = event.object.val(event.name);
|
||||
_results.push(dont_proxy(function() {
|
||||
return that._dom.setAttribute(event.name, newval);
|
||||
}));
|
||||
} else if (event.type === "delete") {
|
||||
_results.push(dont_proxy(function() {
|
||||
return that._dom.removeAttribute(event.name);
|
||||
}));
|
||||
} else {
|
||||
_results.push(void 0);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
this._model.val("classes").observe(function(events) {
|
||||
var event, _j, _len1, _results;
|
||||
_results = [];
|
||||
for (_j = 0, _len1 = events.length; _j < _len1; _j++) {
|
||||
event = events[_j];
|
||||
if (event.type === "add" || event.type === "update") {
|
||||
_results.push(dont_proxy(function() {
|
||||
return that._dom.classList.add(event.name);
|
||||
}));
|
||||
} else if (event.type === "delete") {
|
||||
_results.push(dont_proxy(function() {
|
||||
return that._dom.classList.remove(event.name);
|
||||
}));
|
||||
} else {
|
||||
_results.push(void 0);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
});
|
||||
}
|
||||
return this._dom;
|
||||
};
|
||||
|
||||
return YXml;
|
||||
|
||||
})();
|
||||
|
||||
proxies_are_initialized = false;
|
||||
|
||||
proxy_token = false;
|
||||
|
||||
dont_proxy = function(f) {
|
||||
var e;
|
||||
proxy_token = true;
|
||||
try {
|
||||
f();
|
||||
} catch (_error) {
|
||||
e = _error;
|
||||
proxy_token = false;
|
||||
throw new Error(e);
|
||||
}
|
||||
return proxy_token = false;
|
||||
};
|
||||
|
||||
initialize_proxies = function() {
|
||||
var insertBefore, removeChild, replaceChild, that, _proxy;
|
||||
_proxy = function(f_name, f, source) {
|
||||
var old_f;
|
||||
if (source == null) {
|
||||
source = Element.prototype;
|
||||
}
|
||||
old_f = source[f_name];
|
||||
return source[f_name] = function() {
|
||||
if ((this._y_xml == null) || proxy_token) {
|
||||
return old_f.apply(this, arguments);
|
||||
} else {
|
||||
return f.apply(this._y_xml, arguments);
|
||||
}
|
||||
};
|
||||
};
|
||||
that = this;
|
||||
this._dom.classList.add = function(c) {
|
||||
return that.addClass(c);
|
||||
};
|
||||
this._dom.classList.remove = function(c) {
|
||||
return that.removeClass(c);
|
||||
};
|
||||
this._dom.__defineSetter__('className', function(val) {
|
||||
return that.attr('class', val);
|
||||
});
|
||||
this._dom.__defineGetter__('className', function() {
|
||||
return that.attr('class');
|
||||
});
|
||||
this._dom.__defineSetter__('textContent', function(val) {
|
||||
that.empty();
|
||||
if (val !== "") {
|
||||
return that.append(val);
|
||||
}
|
||||
});
|
||||
if (proxies_are_initialized) {
|
||||
return;
|
||||
}
|
||||
proxies_are_initialized = true;
|
||||
insertBefore = function(insertedNode_s, adjacentNode) {
|
||||
var child, new_childs, pos;
|
||||
if (adjacentNode != null) {
|
||||
pos = adjacentNode._y_xml.getPosition();
|
||||
} else {
|
||||
pos = this.getChildren().length;
|
||||
}
|
||||
new_childs = [];
|
||||
if (insertedNode_s.nodeType === insertedNode_s.DOCUMENT_FRAGMENT_NODE) {
|
||||
child = insertedNode_s.firstChild;
|
||||
while (child != null) {
|
||||
new_childs.push(child);
|
||||
child = child.nextSibling;
|
||||
}
|
||||
} else {
|
||||
new_childs.push(insertedNode_s);
|
||||
}
|
||||
new_childs = new_childs.map(function(child) {
|
||||
if (child._y_xml != null) {
|
||||
return child._y_xml;
|
||||
} else if (child.nodeType === child.TEXT_NODE) {
|
||||
return child.textContent;
|
||||
} else {
|
||||
return new YXml(child);
|
||||
}
|
||||
});
|
||||
return this._model.val("children").insertContents(pos, new_childs);
|
||||
};
|
||||
_proxy('insertBefore', insertBefore);
|
||||
_proxy('appendChild', insertBefore);
|
||||
_proxy('removeAttribute', function(name) {
|
||||
return this.removeAttr(name);
|
||||
});
|
||||
_proxy('setAttribute', function(name, value) {
|
||||
return this.attr(name, value);
|
||||
});
|
||||
removeChild = function(node) {
|
||||
return node._y_xml.remove();
|
||||
};
|
||||
_proxy('removeChild', removeChild, this._dom);
|
||||
replaceChild = function(insertedNode, replacedNode) {
|
||||
insertBefore.call(this, insertedNode, replacedNode);
|
||||
return removeChild.call(this, replacedNode);
|
||||
};
|
||||
return _proxy('replaceChild', replaceChild, this._dom);
|
||||
};
|
||||
|
||||
if (typeof window !== "undefined" && window !== null) {
|
||||
if (window.Y != null) {
|
||||
window.Y.Xml = YXml;
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test Yjs!</title>
|
||||
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css" />
|
||||
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
|
@ -487,6 +487,7 @@ module.exports = ()->
|
||||
object: @parent.getCustomType() # TODO: You can combine getPosition + getParent in a more efficient manner! (only left Delimiter will hold @parent)
|
||||
length: 1
|
||||
changedBy: o.uid.creator
|
||||
oldValue: @val()
|
||||
]
|
||||
|
||||
#
|
||||
|
@ -1,34 +1,7 @@
|
||||
|
||||
json_types_uninitialized = require "./JsonTypes"
|
||||
|
||||
# some dom implementations may call another dom.method that simulates the behavior of another.
|
||||
# For example xml.insertChild(dom) , wich inserts an element at the end, and xml.insertAfter(dom,null) wich does the same
|
||||
# But Y's proxy may be called only once!
|
||||
proxy_token = false
|
||||
dont_proxy = (f)->
|
||||
proxy_token = true
|
||||
try
|
||||
f()
|
||||
catch e
|
||||
proxy_token = false
|
||||
throw new Error e
|
||||
proxy_token = false
|
||||
|
||||
_proxy = (f_name, f)->
|
||||
old_f = @[f_name]
|
||||
if old_f?
|
||||
@[f_name] = ()->
|
||||
if not proxy_token and not @_y?.isDeleted()
|
||||
that = this
|
||||
args = arguments
|
||||
dont_proxy ()->
|
||||
f.apply that, args
|
||||
old_f.apply that, args
|
||||
else
|
||||
old_f.apply this, arguments
|
||||
#else
|
||||
# @[f_name] = f
|
||||
Element?.prototype._proxy = _proxy
|
||||
|
||||
|
||||
module.exports = (HB)->
|
||||
@ -46,202 +19,10 @@ module.exports = (HB)->
|
||||
class XmlType extends types.Insert
|
||||
|
||||
constructor: (uid, @tagname, attributes, elements, @xml)->
|
||||
### In case you make this instanceof Insert again
|
||||
if prev? and (not next?) and prev.type?
|
||||
# adjust what you actually mean. you want to insert after prev, then
|
||||
# next is not defined. but we only insert after non-deleted elements.
|
||||
# This is also handled in TextInsert.
|
||||
while prev.isDeleted()
|
||||
prev = prev.prev_cl
|
||||
next = prev.next_cl
|
||||
###
|
||||
|
||||
super(uid)
|
||||
|
||||
|
||||
if @xml?._y?
|
||||
d = new types.Delete undefined, @xml._y
|
||||
HB.addOperation(d).execute()
|
||||
@xml._y = null
|
||||
|
||||
if attributes? and elements?
|
||||
@saveOperation 'attributes', attributes
|
||||
@saveOperation 'elements', elements
|
||||
else if (not attributes?) and (not elements?)
|
||||
@attributes = new types.JsonType()
|
||||
@attributes.setMutableDefault 'immutable'
|
||||
HB.addOperation(@attributes).execute()
|
||||
@elements = new types.WordType()
|
||||
@elements.parent = @
|
||||
HB.addOperation(@elements).execute()
|
||||
else
|
||||
throw new Error "Either define attribute and elements both, or none of them"
|
||||
|
||||
if @xml?
|
||||
@tagname = @xml.tagName
|
||||
for i in [0...@xml.attributes.length]
|
||||
attr = xml.attributes[i]
|
||||
@attributes.val(attr.name, attr.value)
|
||||
for n in @xml.childNodes
|
||||
if n.nodeType is n.TEXT_NODE
|
||||
word = new TextNodeType(undefined, n)
|
||||
HB.addOperation(word).execute()
|
||||
@elements.push word
|
||||
else if n.nodeType is n.ELEMENT_NODE
|
||||
element = new XmlType undefined, undefined, undefined, undefined, n
|
||||
HB.addOperation(element).execute()
|
||||
@elements.push element
|
||||
else
|
||||
throw new Error "I don't know Node-type #{n.nodeType}!!"
|
||||
@setXmlProxy()
|
||||
undefined
|
||||
|
||||
#
|
||||
# Identifies this class.
|
||||
# Use it in order to check whether this is an xml-type or something else.
|
||||
#
|
||||
type: "XmlType"
|
||||
|
||||
applyDelete: (op)->
|
||||
if @insert_parent? and not @insert_parent.isDeleted()
|
||||
@insert_parent.applyDelete op
|
||||
else
|
||||
@attributes.applyDelete()
|
||||
@elements.applyDelete()
|
||||
super
|
||||
|
||||
cleanup: ()->
|
||||
super()
|
||||
|
||||
setXmlProxy: ()->
|
||||
@xml._y = @
|
||||
that = @
|
||||
|
||||
@elements.on 'insert', (event, op)->
|
||||
if op.creator isnt HB.getUserId() and this is that.elements
|
||||
newNode = op.content.val()
|
||||
right = op.next_cl
|
||||
while right? and right.isDeleted()
|
||||
right = right.next_cl
|
||||
rightNode = null
|
||||
if right.type isnt 'Delimiter'
|
||||
rightNode = right.val().val()
|
||||
dont_proxy ()->
|
||||
that.xml.insertBefore newNode, rightNode
|
||||
@elements.on 'delete', (event, op)->
|
||||
del_op = op.deleted_by[0]
|
||||
if del_op? and del_op.creator isnt HB.getUserId() and this is that.elements
|
||||
deleted = op.content.val()
|
||||
dont_proxy ()->
|
||||
that.xml.removeChild deleted
|
||||
|
||||
@attributes.on ['add', 'update'], (event, property_name, op)->
|
||||
if op.creator isnt HB.getUserId() and this is that.attributes
|
||||
dont_proxy ()->
|
||||
newval = op.val().val()
|
||||
if newval?
|
||||
that.xml.setAttribute(property_name, op.val().val())
|
||||
else
|
||||
that.xml.removeAttribute(property_name)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Here are all methods that proxy the behavior of the xml
|
||||
|
||||
# you want to find a specific child element. Since they are carried by an Insert-Type, you want to find that Insert-Operation.
|
||||
# @param child {DomElement} Dom element.
|
||||
# @return {InsertType} This carries the XmlType that represents the DomElement (child). false if i couldn't find it.
|
||||
#
|
||||
findNode = (child)->
|
||||
if not child?
|
||||
throw new Error "you must specify a parameter!"
|
||||
child = child._y
|
||||
elem = that.elements.beginning.next_cl
|
||||
while elem.type isnt 'Delimiter' and elem.content isnt child
|
||||
elem = elem.next_cl
|
||||
if elem.type is 'Delimiter'
|
||||
false
|
||||
else
|
||||
elem
|
||||
|
||||
insertBefore = (insertedNode_s, adjacentNode)->
|
||||
next = null
|
||||
if adjacentNode?
|
||||
next = findNode adjacentNode
|
||||
prev = null
|
||||
if next
|
||||
prev = next.prev_cl
|
||||
else
|
||||
prev = @_y.elements.end.prev_cl
|
||||
while prev.isDeleted()
|
||||
prev = prev.prev_cl
|
||||
inserted_nodes = null
|
||||
if insertedNode_s.nodeType is insertedNode_s.DOCUMENT_FRAGMENT_NODE
|
||||
child = insertedNode_s.lastChild
|
||||
while child?
|
||||
element = new XmlType undefined, undefined, undefined, undefined, child
|
||||
HB.addOperation(element).execute()
|
||||
that.elements.insertAfter prev, element
|
||||
child = child.previousSibling
|
||||
else
|
||||
element = new XmlType undefined, undefined, undefined, undefined, insertedNode_s
|
||||
HB.addOperation(element).execute()
|
||||
that.elements.insertAfter prev, element
|
||||
|
||||
@xml._proxy 'insertBefore', insertBefore
|
||||
@xml._proxy 'appendChild', insertBefore
|
||||
@xml._proxy 'removeAttribute', (name)->
|
||||
that.attributes.val(name, undefined)
|
||||
@xml._proxy 'setAttribute', (name, value)->
|
||||
that.attributes.val name, value
|
||||
|
||||
renewClassList = (newclass)->
|
||||
dont_do_it = false
|
||||
if newclass?
|
||||
for elem in this
|
||||
if newclass is elem
|
||||
dont_do_it = true
|
||||
value = Array.prototype.join.call this, " "
|
||||
if newclass? and not dont_do_it
|
||||
value += " "+newclass
|
||||
that.attributes.val('class', value )
|
||||
_proxy.call @xml.classList, 'add', renewClassList
|
||||
_proxy.call @xml.classList, 'remove', renewClassList
|
||||
@xml.__defineSetter__ 'className', (val)->
|
||||
@setAttribute('class', val)
|
||||
@xml.__defineGetter__ 'className', ()->
|
||||
that.attributes.val('class')
|
||||
@xml.__defineSetter__ 'textContent', (val)->
|
||||
# remove all nodes
|
||||
elem = that.xml.firstChild
|
||||
while elem?
|
||||
remove = elem
|
||||
elem = elem.nextSibling
|
||||
that.xml.removeChild remove
|
||||
|
||||
# insert word content
|
||||
if val isnt ""
|
||||
text_node = document.createTextNode val
|
||||
that.xml.appendChild text_node
|
||||
|
||||
removeChild = (node)->
|
||||
elem = findNode node
|
||||
if not elem
|
||||
throw new Error "You are only allowed to delete existing (direct) child elements!"
|
||||
d = new types.Delete undefined, elem
|
||||
HB.addOperation(d).execute()
|
||||
node._y = null
|
||||
@xml._proxy 'removeChild', removeChild
|
||||
@xml._proxy 'replaceChild', (insertedNode, replacedNode)->
|
||||
insertBefore.call this, insertedNode, replacedNode
|
||||
removeChild.call this, replacedNode
|
||||
|
||||
|
||||
|
||||
val: (enforce = false)->
|
||||
if document?
|
||||
@ -314,53 +95,3 @@ module.exports = (HB)->
|
||||
} = json
|
||||
|
||||
new XmlType uid, tagname, attributes, elements, undefined
|
||||
|
||||
#
|
||||
# @nodoc
|
||||
# Defines an object that is cannot be changed. You can use this to set an immutable string, or a number.
|
||||
#
|
||||
class TextNodeType extends types.ImmutableObject
|
||||
|
||||
#
|
||||
# @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created.
|
||||
# @param {Object} content
|
||||
#
|
||||
constructor: (uid, content)->
|
||||
if content._y?
|
||||
d = new types.Delete undefined, content._y
|
||||
HB.addOperation(d).execute()
|
||||
content._y = null
|
||||
content._y = @
|
||||
super uid, content
|
||||
|
||||
applyDelete: (op)->
|
||||
if @insert_parent? and not @insert_parent.isDeleted()
|
||||
@insert_parent.applyDelete op
|
||||
else
|
||||
super
|
||||
|
||||
|
||||
type: "TextNodeType"
|
||||
|
||||
#
|
||||
# Encode this operation in such a way that it can be parsed by remote peers.
|
||||
#
|
||||
_encode: ()->
|
||||
json = {
|
||||
'type': @type
|
||||
'uid' : @getUid()
|
||||
'content' : @content.textContent
|
||||
}
|
||||
json
|
||||
|
||||
parser['TextNodeType'] = (json)->
|
||||
{
|
||||
'uid' : uid
|
||||
'content' : content
|
||||
} = json
|
||||
textnode = document.createTextNode content
|
||||
new TextNodeType uid, textnode
|
||||
|
||||
types['XmlType'] = XmlType
|
||||
|
||||
json_types
|
||||
|
@ -1,42 +1,72 @@
|
||||
class YXml
|
||||
|
||||
constructor: (tagname, attributes = {})->
|
||||
@_xml = {}
|
||||
#TODO: How to force the user to specify parameters?
|
||||
#if not tagname?
|
||||
# throw new Error "You must specify a tagname"
|
||||
@_xml.tagname = tagname
|
||||
if attributes.constructor isnt Object
|
||||
throw new Error "The attributes must be specified as a Object"
|
||||
for a_name, a of attributes
|
||||
if a.constructor isnt String
|
||||
throw new Error "The attributes must be of type String!"
|
||||
@_xml.attributes = attributes
|
||||
@_xml.classes = {}
|
||||
_classes = @_xml.attributes.class
|
||||
delete @_xml.attributes.class
|
||||
if _classes?
|
||||
for c_name, c in _classes.split(" ")
|
||||
if c.length > 0
|
||||
@_xml.classes[c_name] = c
|
||||
undefined
|
||||
constructor: (tag_or_dom, attributes = {})->
|
||||
if not tag_or_dom?
|
||||
# nop
|
||||
else if tag_or_dom.constructor is String
|
||||
tagname = tag_or_dom
|
||||
@_xml = {}
|
||||
@_xml.children = []
|
||||
#TODO: How to force the user to specify parameters?
|
||||
#if not tagname?
|
||||
# throw new Error "You must specify a tagname"
|
||||
@_xml.tagname = tagname
|
||||
if attributes.constructor isnt Object
|
||||
throw new Error "The attributes must be specified as a Object"
|
||||
for a_name, a of attributes
|
||||
if a.constructor isnt String
|
||||
throw new Error "The attributes must be of type String!"
|
||||
@_xml.attributes = attributes
|
||||
@_xml.classes = {}
|
||||
_classes = @_xml.attributes.class
|
||||
delete @_xml.attributes.class
|
||||
if _classes?
|
||||
for c_name, c in _classes.split(" ")
|
||||
if c.length > 0
|
||||
@_xml.classes[c_name] = c
|
||||
undefined
|
||||
else if tag_or_dom instanceof Element
|
||||
@_dom = tag_or_dom
|
||||
@_xml = {}
|
||||
|
||||
|
||||
|
||||
_name: "Xml"
|
||||
|
||||
_getModel: (Y, ops)->
|
||||
if not @_model?
|
||||
if @_dom?
|
||||
@_xml.tagname = @_dom.tagName.toLowerCase()
|
||||
@_xml.attributes = {}
|
||||
@_xml.classes = {}
|
||||
for attribute in @_dom.attributes
|
||||
if attribute.name is "class"
|
||||
for c in attribute.value.split(" ")
|
||||
@_xml.classes[c] = true
|
||||
else
|
||||
@_xml.attributes[attribute.name] = attribute.value
|
||||
@_xml.children = []
|
||||
for child in @_dom.childNodes
|
||||
if child.nodeType is child.TEXT_NODE
|
||||
@_xml.children.push child.textContent
|
||||
else
|
||||
@_xml.children.push(new YXml(child))
|
||||
@_model = new ops.MapManager(@).execute()
|
||||
@_model.val("attributes", new Y.Object(@_xml.attributes))
|
||||
@_model.val("classes", new Y.Object(@_xml.classes))
|
||||
@_model.val("tagname", @_xml.tagname)
|
||||
@_model.val("children", new Y.List())
|
||||
@_model.val("children", new Y.List(@_xml.children))
|
||||
if @_xml.parent?
|
||||
@_model.val("parent", @_xml.parent)
|
||||
@_setModel @_model
|
||||
|
||||
if @_dom?
|
||||
@getDom() # two way bind dom to this xml type
|
||||
|
||||
@_setModel @_model
|
||||
|
||||
@_model
|
||||
|
||||
_setModel: (@_model)->
|
||||
delete @_xml
|
||||
@_model.observe (events)->
|
||||
for event in events
|
||||
if event.name is "parent" and event.type isnt "add"
|
||||
@ -47,8 +77,7 @@ class YXml
|
||||
if c is @
|
||||
parent._model.val("children").delete i
|
||||
break
|
||||
undefined
|
||||
|
||||
delete @_xml
|
||||
|
||||
_setParent: (parent)->
|
||||
if parent instanceof YXml
|
||||
@ -260,6 +289,158 @@ class YXml
|
||||
getChildren: ()->
|
||||
@_model.val("children").val()
|
||||
|
||||
getPosition: ()->
|
||||
parent = @_model.val("parent")
|
||||
if parent?
|
||||
for c,i in parent._model.val("children").val()
|
||||
if c is @
|
||||
return i
|
||||
throw new Error "This is not a child of its parent (should not happen in Y.Xml!)"
|
||||
else
|
||||
null
|
||||
|
||||
|
||||
getDom: ()->
|
||||
if not @_dom?
|
||||
@_dom = document.createElement(@_model.val("tagname"))
|
||||
|
||||
# set the attributes _and_ the classes (@see .attr())
|
||||
for attr_name, attr_value of @attr()
|
||||
@_dom.setAttribute attr_name, attr_value
|
||||
for child,i in @getChildren()
|
||||
if child.constructor is String
|
||||
dom = document.createTextNode child
|
||||
else
|
||||
dom = child.getDom()
|
||||
@_dom.insertBefore dom
|
||||
|
||||
that = @
|
||||
|
||||
if (not @_dom._y_xml?)
|
||||
@_dom._y_xml = @
|
||||
initialize_proxies.call @
|
||||
|
||||
@_model.val("children").observe (events)->
|
||||
for event in events
|
||||
if event.type is "insert"
|
||||
newNode = event.value.getDom()
|
||||
children = that._dom.childNodes
|
||||
if children.length > 0
|
||||
rightNode = children[0]
|
||||
else
|
||||
rightNode = null
|
||||
event.value._setParent that
|
||||
dont_proxy ()->
|
||||
that._dom.insertBefore newNode, rightNode
|
||||
else if event.type is "delete"
|
||||
deleted = event.oldValue.getDom()
|
||||
dont_proxy ()->
|
||||
that._dom.removeChild deleted
|
||||
@_model.val("attributes").observe (events)->
|
||||
for event in events
|
||||
if event.type is "add" or event.type is "update"
|
||||
newval = event.object.val(event.name)
|
||||
dont_proxy ()->
|
||||
that._dom.setAttribute event.name, newval
|
||||
else if event.type is "delete"
|
||||
dont_proxy ()->
|
||||
that._dom.removeAttribute event.name
|
||||
@_model.val("classes").observe (events)->
|
||||
for event in events
|
||||
if event.type is "add" or event.type is "update"
|
||||
dont_proxy ()->
|
||||
that._dom.classList.add event.name # classes are stored as the keys
|
||||
else if event.type is "delete"
|
||||
dont_proxy ()->
|
||||
that._dom.classList.remove event.name
|
||||
@_dom
|
||||
|
||||
proxies_are_initialized = false
|
||||
# some dom implementations may call another dom.method that simulates the behavior of another.
|
||||
# For example xml.insertChild(dom) , wich inserts an element at the end, and xml.insertAfter(dom,null) wich does the same
|
||||
# But Y's proxy may be called only once!
|
||||
proxy_token = false
|
||||
dont_proxy = (f)->
|
||||
proxy_token = true
|
||||
try
|
||||
f()
|
||||
catch e
|
||||
proxy_token = false
|
||||
throw new Error e
|
||||
proxy_token = false
|
||||
|
||||
initialize_proxies = ()->
|
||||
|
||||
_proxy = (f_name, f, source = Element.prototype)->
|
||||
old_f = source[f_name]
|
||||
source[f_name] = ()->
|
||||
if (not @_y_xml?) or proxy_token
|
||||
old_f.apply this, arguments
|
||||
else
|
||||
f.apply @_y_xml, arguments
|
||||
|
||||
that = this
|
||||
@_dom.classList.add = (c)->
|
||||
that.addClass c
|
||||
@_dom.classList.remove = (c)->
|
||||
that.removeClass c
|
||||
|
||||
@_dom.__defineSetter__ 'className', (val)->
|
||||
that.attr('class', val)
|
||||
@_dom.__defineGetter__ 'className', ()->
|
||||
that.attr('class')
|
||||
@_dom.__defineSetter__ 'textContent', (val)->
|
||||
# remove all nodes
|
||||
that.empty()
|
||||
|
||||
# insert word content
|
||||
if val isnt ""
|
||||
that.append val
|
||||
|
||||
|
||||
if proxies_are_initialized
|
||||
return
|
||||
proxies_are_initialized = true
|
||||
|
||||
# the following methods are initialized on prototypes and therefore they need to be written only once!
|
||||
|
||||
insertBefore = (insertedNode_s, adjacentNode)->
|
||||
if adjacentNode?
|
||||
pos = adjacentNode._y_xml.getPosition()
|
||||
else
|
||||
pos = @getChildren().length
|
||||
|
||||
new_childs = []
|
||||
if insertedNode_s.nodeType is insertedNode_s.DOCUMENT_FRAGMENT_NODE
|
||||
child = insertedNode_s.firstChild
|
||||
while child?
|
||||
new_childs.push child
|
||||
child = child.nextSibling
|
||||
else
|
||||
new_childs.push insertedNode_s
|
||||
new_childs = new_childs.map (child)->
|
||||
if child._y_xml?
|
||||
child._y_xml
|
||||
else if child.nodeType == child.TEXT_NODE
|
||||
child.textContent
|
||||
else
|
||||
new YXml(child)
|
||||
@_model.val("children").insertContents pos, new_childs
|
||||
|
||||
_proxy 'insertBefore', insertBefore
|
||||
_proxy 'appendChild', insertBefore
|
||||
_proxy 'removeAttribute', (name)->
|
||||
@removeAttr name
|
||||
_proxy 'setAttribute', (name, value)->
|
||||
@attr name, value
|
||||
|
||||
removeChild = (node)->
|
||||
node._y_xml.remove()
|
||||
_proxy 'removeChild', removeChild, @_dom
|
||||
replaceChild = (insertedNode, replacedNode)->
|
||||
insertBefore.call this, insertedNode, replacedNode
|
||||
removeChild.call this, replacedNode
|
||||
_proxy 'replaceChild', replaceChild, @_dom
|
||||
|
||||
if window?
|
||||
if window.Y?
|
||||
|
@ -4,6 +4,7 @@ should = chai.should()
|
||||
sinon = require('sinon')
|
||||
sinonChai = require('sinon-chai')
|
||||
_ = require("underscore")
|
||||
$ = require('jquery')
|
||||
|
||||
chai.use(sinonChai)
|
||||
|
||||
@ -27,7 +28,7 @@ class XmlTest extends Test
|
||||
super new Y conn
|
||||
|
||||
initUsers: (u)->
|
||||
u.val("xml",new Y.Xml("root"))
|
||||
u.val("xml",new Y.Xml("div"))
|
||||
|
||||
type: "XmlTest"
|
||||
|
||||
@ -175,7 +176,7 @@ describe "Y-Xml", ->
|
||||
child2 = new Y.Xml("child2")
|
||||
@u1.append child
|
||||
@u1.append child2
|
||||
expect(@u1.toString()).to.equal("<root><child></child><child2></child2></root>")
|
||||
expect(@u1.toString()).to.equal("<div><child></child><child2></child2></div>")
|
||||
@yTest.compareAll()
|
||||
|
||||
it "prepend", ->
|
||||
@ -183,31 +184,31 @@ describe "Y-Xml", ->
|
||||
child2 = new Y.Xml("child2")
|
||||
@u1.prepend child2
|
||||
@u1.prepend child
|
||||
expect(@u1.toString()).to.equal("<root><child></child><child2></child2></root>")
|
||||
expect(@u1.toString()).to.equal("<div><child></child><child2></child2></div>")
|
||||
@yTest.compareAll()
|
||||
|
||||
it "after", ->
|
||||
child = new Y.Xml("child")
|
||||
@u1.append child
|
||||
child.after new Y.Xml("right-child")
|
||||
expect(@u1.toString()).to.equal("<root><child></child><right-child></right-child></root>")
|
||||
expect(@u1.toString()).to.equal("<div><child></child><right-child></right-child></div>")
|
||||
@yTest.compareAll()
|
||||
|
||||
it "before", ->
|
||||
child = new Y.Xml("child")
|
||||
@u1.append child
|
||||
child.before new Y.Xml("left-child")
|
||||
expect(@u1.toString()).to.equal("<root><left-child></left-child><child></child></root>")
|
||||
expect(@u1.toString()).to.equal("<div><left-child></left-child><child></child></div>")
|
||||
@yTest.compareAll()
|
||||
|
||||
it "empty", ->
|
||||
child = new Y.Xml("child")
|
||||
@u1.append child
|
||||
child.before new Y.Xml("left-child")
|
||||
expect(@u1.toString()).to.equal("<root><left-child></left-child><child></child></root>")
|
||||
expect(@u1.toString()).to.equal("<div><left-child></left-child><child></child></div>")
|
||||
@yTest.compareAll()
|
||||
@u1.empty()
|
||||
expect(@u1.toString()).to.equal("<root></root>")
|
||||
expect(@u1.toString()).to.equal("<div></div>")
|
||||
@yTest.compareAll()
|
||||
|
||||
it "remove", ->
|
||||
@ -215,10 +216,10 @@ describe "Y-Xml", ->
|
||||
child2 = new Y.Xml("child2")
|
||||
@u1.prepend child2
|
||||
@u1.prepend child
|
||||
expect(@u1.toString()).to.equal("<root><child></child><child2></child2></root>")
|
||||
expect(@u1.toString()).to.equal("<div><child></child><child2></child2></div>")
|
||||
@yTest.compareAll()
|
||||
child2.remove()
|
||||
expect(@u1.toString()).to.equal("<root><child></child></root>")
|
||||
expect(@u1.toString()).to.equal("<div><child></child></div>")
|
||||
|
||||
it "removeAttr", ->
|
||||
@u1.attr("dtrn", "stuff")
|
||||
@ -258,4 +259,216 @@ describe "Y-Xml", ->
|
||||
@u1.prepend(child)
|
||||
expect(@u1.getChildren()[0]).to.equal(child)
|
||||
|
||||
if not window?
|
||||
describe "skip DOM tests (only in browser environment)", ->
|
||||
it "", ->
|
||||
else
|
||||
describe "DOM binding ", ->
|
||||
beforeEach (done)->
|
||||
@dom = @u1.getDom()
|
||||
@j = $(@dom)
|
||||
done()
|
||||
|
||||
it "can transform to a new real Dom element", ->
|
||||
expect(@dom).to.not.be.undefined
|
||||
|
||||
it "supports dom.insertBefore", ->
|
||||
newdom = $("<p>dtrn</p>")[0]
|
||||
newdom2 = $("<p>dtrn2</p>")[0]
|
||||
@dom.insertBefore(newdom2, null)
|
||||
@dom.insertBefore(newdom, newdom2)
|
||||
expect(@u1+"").to.equal("<div><p>dtrn</p><p>dtrn2</p></div>")
|
||||
expect(@dom.outerHTML).to.equal("<div><p>dtrn</p><p>dtrn2</p></div>")
|
||||
|
||||
it "supports dom.appendChild", ->
|
||||
newdom = $("<p>dtrn</p>")[0]
|
||||
@dom.appendChild(newdom)
|
||||
expect(@u1+"").to.equal("<div><p>dtrn</p></div>")
|
||||
|
||||
it "supports dom.setAttribute", ->
|
||||
@dom.setAttribute("test_attribute", "newVal")
|
||||
expect(@u1.attr("test_attribute")).to.equal("newVal")
|
||||
expect(@dom.getAttribute("test_attribute")).to.equal("newVal")
|
||||
|
||||
it "supports dom.removeAttribute", ->
|
||||
@dom.setAttribute("test_attribute", "newVal")
|
||||
expect(@u1.attr("test_attribute")).to.equal("newVal")
|
||||
expect(@dom.getAttribute("test_attribute")).to.equal("newVal")
|
||||
@dom.removeAttribute("test_attribute")
|
||||
expect(@u1.attr("test_attribute")).to.be.undefined
|
||||
expect(@dom.getAttribute("test_attribute")).to.be.undefined
|
||||
|
||||
it "supports dom.removeChild", ->
|
||||
newdom = $("<p>dtrn</p>")[0]
|
||||
@dom.appendChild(newdom)
|
||||
expect(@u1+"").to.equal("<div><p>dtrn</p></div>")
|
||||
expect(@dom.outerHTML).to.equal("<div><p>dtrn</p></div>")
|
||||
|
||||
@dom.removeChild(newdom)
|
||||
expect(@dom.childNodes.length).to.equal(0)
|
||||
expect(@u1.getChildren().length).to.equal(0)
|
||||
|
||||
it "supports dom.replaceChild", ->
|
||||
dom = $("<p>dtrn</p>")[0]
|
||||
@dom.appendChild(newdom)
|
||||
expect(@u1+"").to.equal("<div><p>dtrn</p></div>")
|
||||
expect(@dom.outerHTML).to.equal("<div><p>dtrn</p></div>")
|
||||
|
||||
newdom = $("<p>replaced</p>")[0]
|
||||
@dom.replaceChild(dom,newdom)
|
||||
expect(@dom.outerHTML).to.equal("<div><p>replaced</p></div>")
|
||||
expect(@u1+"").to.equal("<div><p>replaced</p></div>")
|
||||
|
||||
it "supports dom.classList.add", ->
|
||||
@dom.classList.add "classy"
|
||||
@dom.classList.add "moreclassy"
|
||||
expect(@u1.attr("class")).to.equal("classy moreclassy")
|
||||
expect(@dom.getAttribute("class")).to.equal("classy moreclassy")
|
||||
|
||||
|
||||
it "supports dom.textContent", ->
|
||||
dom = $("<p>dtrn</p>")[0]
|
||||
@dom.appendChild(newdom)
|
||||
expect(@u1+"").to.equal("<div><p>dtrn</p></div>")
|
||||
expect(@dom.outerHTML).to.equal("<div><p>dtrn</p></div>")
|
||||
|
||||
@dom.textContent = ""
|
||||
expect(@u1+"").to.equal("<div></div>")
|
||||
expect(@dom.outerHTML).to.equal("<div></div>")
|
||||
|
||||
it "suppports dom.textContent (non empty string)", ->
|
||||
dom = $("<p>dtrn</p>")[0]
|
||||
@dom.appendChild(newdom)
|
||||
expect(@u1+"").to.equal("<div><p>dtrn</p></div>")
|
||||
expect(@dom.outerHTML).to.equal("<div><p>dtrn</p></div>")
|
||||
|
||||
@dom.textContent = "stuff"
|
||||
expect(@u1+"").to.equal("<div>stuff</div>")
|
||||
expect(@dom.outerHTML).to.equal("<div>stuff</div>")
|
||||
|
||||
it "supports jquery.addClass", ->
|
||||
@j.addClass("testy")
|
||||
@j.addClass("testedy tested")
|
||||
expect(@dom.getAttribute("class")).to.equal("testy testedy tested")
|
||||
|
||||
it "supports jquery.after", ->
|
||||
d = $("<span></span>")
|
||||
@dom.appendChild(d[0], null)
|
||||
d.after("<div>after</div>")
|
||||
expect(@dom.outerHTML).to.equal("<div><span></span><div>after</div></div>")
|
||||
expect(@u1+"").to.equal("<div><span></span><div>after</div></div>")
|
||||
|
||||
it "supports jquery.append", ->
|
||||
d = $("<span></span>")[0]
|
||||
@j.append(d)
|
||||
d = $("<div></div>")[0]
|
||||
@dom.append(d)
|
||||
expect(@dom.outerHTML).to.equal("<div><span></span><div></div></div>")
|
||||
expect(@u1+"").to.equal("<div><span></span><div></div></div>")
|
||||
|
||||
it "supports jquery.appendTo", ->
|
||||
$("<b>appendedTo</b>").appendTo(@dom)
|
||||
$("p").appendTo(@dom)
|
||||
expect(@dom.outerHTML).to.equal("<div><b>appendedTo</b><p></p></div>")
|
||||
expect(@u1+"").to.equal("<div><b>appendedTo</b><p></p></div>")
|
||||
|
||||
it "supports jquery.before", ->
|
||||
newdom = $("p")
|
||||
$(@dom).append(newdom)
|
||||
newdom.before("<div>before</div>")
|
||||
expect(@dom.outerHTML).to.equal("<div><div>before</div><p></p></div>")
|
||||
expect(@u1+"").to.equal("<div><div>before</div><p></p></div>")
|
||||
|
||||
it "supports jquery.detach", ->
|
||||
d = $("p")
|
||||
$j.append(d)
|
||||
$j.detach("p")
|
||||
expect(@dom.outerHTML).to.equal("<div></div>")
|
||||
expect(@u1+"").to.equal("<div></div>")
|
||||
|
||||
it "supports jquery.empty", ->
|
||||
d = $("<p />")
|
||||
d.appendTo(@dom)
|
||||
d = $("<div />")
|
||||
d.appendTo(@dom)
|
||||
@j.empty()
|
||||
expect(@dom.outerHTML).to.equal("<div></div>")
|
||||
expect(@u1+"").to.equal("<div></div>")
|
||||
|
||||
it "supports jquery.insertAfter", ->
|
||||
d = $("span")
|
||||
d.appendTo(@dom)
|
||||
$("<p>after</p>").insertAfter(d)
|
||||
expect(@dom.outerHTML).to.equal("<div><span></span><p>after</p></div>")
|
||||
expect(@u1+"").to.equal("<div><span></span><p>after</p></div>")
|
||||
|
||||
it "supports jquery.insertBefore", ->
|
||||
d = $("span")
|
||||
d.appendTo(@j)
|
||||
$("<p>after</p>").insertAfter(d)
|
||||
expect(@dom.outerHTML).to.equal("<div><p>before</p><span></span></div>")
|
||||
expect(@u1+"").to.equal("<div><p>before</p><span></span></div>")
|
||||
|
||||
it "supports jquery.prepend", ->
|
||||
@j.prepend("<p>prepended2</p>")
|
||||
@j.prepend("<p>prepended1</p>")
|
||||
expect(@dom.outerHTML).to.equal("<div><p>prepended1</p><p>prepended2</p></div>")
|
||||
expect(@u1+"").to.equal("<div><p>prepended1</p><p>prepended2</p></div>")
|
||||
|
||||
it "supports jquery.prependTo", ->
|
||||
$("<p>prepended2</p>").prependTo(@j)
|
||||
$("<p>prepended1</p>").prependTo(@j)
|
||||
expect(@dom.outerHTML).to.equal("<div><p>prepended1</p><p>prepended2</p></div>")
|
||||
expect(@u1+"").to.equal("<div><p>prepended1</p><p>prepended2</p></div>")
|
||||
|
||||
it "supports jquery.remove", ->
|
||||
d = $("<div />")
|
||||
d.prependTo(@j)
|
||||
d.remove()
|
||||
expect(@dom.outerHTML).to.equal("<div></div>")
|
||||
expect(@u1+"").to.equal("<div></div>")
|
||||
|
||||
it "supports jquery.removeAttr", ->
|
||||
@dom.setAttribute("test_attribute", "newVal")
|
||||
expect(@u1.attr("test_attribute")).to.equal("newVal")
|
||||
expect(@dom.getAttribute("test_attribute")).to.equal("newVal")
|
||||
|
||||
@j.removeAttr("test_attribute")
|
||||
expect(@u1.attr("test_attribute")).to.be.undefined
|
||||
expect(@j.attr("test_attribute")).to.be.undefined
|
||||
|
||||
it "supports jquery.removeClass", ->
|
||||
@j.addClass("testy")
|
||||
@j.addClass("testedy tested")
|
||||
expect(@dom.getAttribute("class")).to.equal("testy testedy tested")
|
||||
|
||||
@j.removeClass("testedy")
|
||||
expect(@dom.getAttribute("class")).to.equal("testy tested")
|
||||
expect(@u1.hasClass("testedy")).to.be.false
|
||||
|
||||
it "supports jquery.attr", ->
|
||||
@j.attr("atone", "yeah")
|
||||
expect(@u1.attr("atone")).to.equal("yeah")
|
||||
|
||||
it "supports jquery.replaceAll", ->
|
||||
$("<span>New span content </span>").replaceAll("#test_dom div")
|
||||
@check()
|
||||
|
||||
it "supports jquery.replaceWith", ->
|
||||
d = $("span")
|
||||
@j.prepend(d)
|
||||
d = $("span")
|
||||
@j.prepend(d)
|
||||
d = $("span")
|
||||
@j.prepend(d)
|
||||
d = @j.getElementsByTagName("span")
|
||||
d.replaceWith("<div></div>")
|
||||
|
||||
expect(@dom.outerHTML).to.equal("<div><div></div><div></div><div></div></div>")
|
||||
expect(@u1+"").to.equal("<div><div></div><div></div><div></div></div>")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user