insertBefore (xml), and prevent bug for addProperty-listener
This commit is contained in:
100
lib/Frameworks/XmlFramework.coffee
Normal file
100
lib/Frameworks/XmlFramework.coffee
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
json_types_uninitialized = require "../Types/XmlTypes"
|
||||
HistoryBuffer = require "../HistoryBuffer"
|
||||
Engine = require "../Engine"
|
||||
|
||||
#
|
||||
# Framework for Xml-like data-structures.
|
||||
# Known values that are supported:
|
||||
#
|
||||
class XmlFramework
|
||||
|
||||
#
|
||||
# @param {String} user_id Unique id of the peer.
|
||||
# @param {Connector} Connector the connector class.
|
||||
#
|
||||
constructor: (user_id, Connector)->
|
||||
@HB = new HistoryBuffer user_id
|
||||
type_manager = json_types_uninitialized @HB
|
||||
@types = type_manager.types
|
||||
@engine = new Engine @HB, type_manager.parser
|
||||
@HB.engine = @engine # TODO: !! only for debugging
|
||||
@connector = new Connector @engine, @HB, type_manager.execution_listener, @
|
||||
#first_word = new @types.XmlType(undefined, undefined, undefined, undefined, document.createElement("shared"))
|
||||
#@HB.addOperation(first_word).execute()
|
||||
|
||||
uid_beg = @HB.getReservedUniqueIdentifier()
|
||||
uid_end = @HB.getReservedUniqueIdentifier()
|
||||
beg = @HB.addOperation(new @types.Delimiter uid_beg, undefined, uid_end).execute()
|
||||
end = @HB.addOperation(new @types.Delimiter uid_end, beg, undefined).execute()
|
||||
|
||||
@root_element = new @types.ReplaceManager undefined, @HB.getReservedUniqueIdentifier(), beg, end
|
||||
@HB.addOperation(@root_element).execute()
|
||||
#@root_element.replace first_word
|
||||
|
||||
#
|
||||
# @return JsonType
|
||||
#
|
||||
getSharedObject: ()->
|
||||
@root_element.val()
|
||||
|
||||
#
|
||||
# Get the initialized connector.
|
||||
#
|
||||
getConnector: ()->
|
||||
@connector
|
||||
|
||||
#
|
||||
# @see HistoryBuffer
|
||||
#
|
||||
getHistoryBuffer: ()->
|
||||
@HB
|
||||
|
||||
#
|
||||
# @see JsonType.setMutableDefault
|
||||
#
|
||||
setMutableDefault: (mutable)->
|
||||
@getSharedObject().setMutableDefault(mutable)
|
||||
|
||||
#
|
||||
# Get the UserId from the HistoryBuffer object.
|
||||
# In most cases this will be the same as the user_id value with which
|
||||
# JsonFramework was initialized (Depending on the HistoryBuffer implementation).
|
||||
#
|
||||
getUserId: ()->
|
||||
@HB.getUserId()
|
||||
|
||||
#
|
||||
# @see JsonType.toJson
|
||||
#
|
||||
toJson : ()->
|
||||
@getSharedObject().toJson()
|
||||
|
||||
#
|
||||
# @see JsonType.val
|
||||
#
|
||||
val : ()->
|
||||
if (arguments.length is 0) or (typeof arguments[0] is "boolean")
|
||||
@getSharedObject().val(arguments[0])
|
||||
else if arguments.length is 1
|
||||
newXml = new @types.XmlType(undefined, undefined, undefined, undefined, arguments[0])
|
||||
@HB.addOperation(newXml).execute()
|
||||
@root_element.replace newXml
|
||||
newXml
|
||||
else
|
||||
throw new Error "can only parse 0, or 1 parameter!"
|
||||
|
||||
|
||||
#
|
||||
# @see Operation.on
|
||||
#
|
||||
on: ()->
|
||||
@getSharedObject().on arguments...
|
||||
|
||||
|
||||
|
||||
module.exports = XmlFramework
|
||||
if window?
|
||||
if not window.Y?
|
||||
window.Y = {}
|
||||
window.Y.XmlFramework = XmlFramework
|
||||
@@ -277,16 +277,16 @@ module.exports = (HB)->
|
||||
if o?
|
||||
@deleted_by.push o
|
||||
garbagecollect = false
|
||||
if @prev_cl.isDeleted()
|
||||
if not (@prev_cl? and @next_cl?) or @prev_cl.isDeleted()
|
||||
garbagecollect = true
|
||||
super garbagecollect
|
||||
if @next_cl.isDeleted()
|
||||
if @next_cl?.isDeleted()
|
||||
# garbage collect next_cl
|
||||
@next_cl.applyDelete()
|
||||
|
||||
cleanup: ()->
|
||||
# TODO: Debugging
|
||||
if @prev_cl.isDeleted()
|
||||
if @prev_cl?.isDeleted()
|
||||
# delete all ops that delete this insertion
|
||||
for d in @deleted_by
|
||||
d.cleanup()
|
||||
|
||||
@@ -257,8 +257,8 @@ module.exports = (HB)->
|
||||
repl_manager.parent.callEvent 'change', property_name, op
|
||||
# Call this, when the first element is inserted. Then delete the listener.
|
||||
addPropertyListener = (event, op)->
|
||||
repl_manager.parent.callEvent 'addProperty', property_name, op
|
||||
repl_manager.deleteListener 'addProperty', addPropertyListener
|
||||
repl_manager.parent.callEvent 'addProperty', property_name, op
|
||||
@on 'insert', addPropertyListener
|
||||
super parent
|
||||
|
||||
|
||||
@@ -115,6 +115,22 @@ module.exports = (HB)->
|
||||
cleanup: ()->
|
||||
super()
|
||||
|
||||
push: (content)->
|
||||
@insertAfter @end.prev_cl, content
|
||||
|
||||
insertAfter: (left, content)->
|
||||
while left.isDeleted()
|
||||
left = left.prev_cl # find the first character to the left, that is not deleted. Case position is 0, its the Delimiter.
|
||||
right = left.next_cl
|
||||
if content.type?
|
||||
op = new TextInsert content, undefined, left, right
|
||||
HB.addOperation(op).execute()
|
||||
else
|
||||
for c in content
|
||||
op = new TextInsert c, undefined, left, right
|
||||
HB.addOperation(op).execute()
|
||||
left = op
|
||||
@
|
||||
#
|
||||
# Inserts a string into the word.
|
||||
#
|
||||
@@ -124,14 +140,7 @@ module.exports = (HB)->
|
||||
# TODO: getOperationByPosition should return "(i-2)th" character
|
||||
ith = @getOperationByPosition position # the (i-1)th character. e.g. "abc" a is the 0th character
|
||||
left = ith.prev_cl # left is the non-deleted charather to the left of ith
|
||||
while left.isDeleted()
|
||||
left = left.prev_cl # find the first character to the left, that is not deleted. Case position is 0, its the Delimiter.
|
||||
right = left.next_cl
|
||||
for c in content
|
||||
op = new TextInsert c, undefined, left, right
|
||||
HB.addOperation(op).execute()
|
||||
left = op
|
||||
@
|
||||
@insertAfter left, content
|
||||
|
||||
#
|
||||
# Deletes a part of the word.
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
|
||||
json_types_uninitialized = require "./JsonTypes"
|
||||
|
||||
Element?.prototype._proxy = (f_name, f)->
|
||||
old_f = @[f_name]
|
||||
if old_f?
|
||||
@[f_name] = ()->
|
||||
f.apply this, arguments
|
||||
old_f.apply this, arguments
|
||||
else
|
||||
@[f_name] = f
|
||||
|
||||
|
||||
module.exports = (HB)->
|
||||
json_types = json_types_uninitialized HB
|
||||
types = json_types.types
|
||||
@@ -11,8 +21,16 @@ module.exports = (HB)->
|
||||
#
|
||||
class XmlType extends types.Insert
|
||||
|
||||
constructor: (uid, @tagname, attributes, elements, prev_cl, next_cl, origin)->
|
||||
super uid, prev_cl, next_cl, origin
|
||||
constructor: (uid, @tagname, attributes, elements, @xml, prev, next, origin)->
|
||||
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, prev, next, origin
|
||||
|
||||
if attributes? and elements?
|
||||
@saveOperation 'attributes', attributes
|
||||
@@ -21,10 +39,31 @@ module.exports = (HB)->
|
||||
@attributes = new types.JsonType()
|
||||
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 types.WordType()
|
||||
HB.addOperation(word).execute()
|
||||
word.push n.textContent
|
||||
@elements.push word
|
||||
else if n.nodeType is n.ELEMENT_NODE
|
||||
last = @elements.end
|
||||
element = new XmlType undefined, undefined, undefined, undefined, n, last.prev_cl, last
|
||||
HB.addOperation(element).execute()
|
||||
@elements.push element
|
||||
else
|
||||
throw new Error "I don't know Node-type #{n.nodeType}!!"
|
||||
@setXmlProxy()
|
||||
undefined
|
||||
|
||||
#
|
||||
# Identifies this class.
|
||||
@@ -40,28 +79,43 @@ module.exports = (HB)->
|
||||
cleanup: ()->
|
||||
super()
|
||||
|
||||
val: ()->
|
||||
if document?
|
||||
if arguments.length is 0
|
||||
if not @xml?
|
||||
@xml = document.createElement @tagname
|
||||
|
||||
attr = @attributes.val()
|
||||
for attr_name, value of attr
|
||||
a = document.createAttribute attr_name
|
||||
a.value = value
|
||||
@xml.setAttributeNode a
|
||||
|
||||
e = @elements.beginning.next_cl
|
||||
while e.type isnt "Delimiter"
|
||||
if not e.isDeleted()
|
||||
@xml.appendChild e.val()
|
||||
e.next_cl
|
||||
@xml
|
||||
else if arguments.length is 1
|
||||
|
||||
setXmlProxy: ()->
|
||||
@xml._yatta = @
|
||||
@xml._proxy 'insertBefore', (insertedNode, adjacentNode)->
|
||||
next = adjacentNode?._yatta
|
||||
prev = null
|
||||
if next?
|
||||
prev = next.prev_cl
|
||||
else
|
||||
throw new Error "Can only parse one parameter"
|
||||
prev = @_yatta.elements.end.prev_cl
|
||||
element = new XmlType undefined, undefined, undefined, undefined, insertedNode, prev
|
||||
HB.addOperation(element).execute()
|
||||
|
||||
val: (enforce = false)->
|
||||
if document?
|
||||
if (not @xml?) or enforce
|
||||
@xml = document.createElement @tagname
|
||||
|
||||
attr = @attributes.val()
|
||||
for attr_name, value of attr
|
||||
a = document.createAttribute attr_name
|
||||
a.value = value
|
||||
@xml.setAttributeNode a
|
||||
|
||||
e = @elements.beginning.next_cl
|
||||
while e.type isnt "Delimiter"
|
||||
if not e.isDeleted()
|
||||
if e.type is "XmlType"
|
||||
@xml.appendChild e.val(enforce)
|
||||
else if e.type is "WordType"
|
||||
text_node = document.createTextNode e.val()
|
||||
@xml.appendChild text_node
|
||||
else
|
||||
throw new Error "Internal structure cannot be transformed to dom"
|
||||
e = e.next_cl
|
||||
@setXmlProxy()
|
||||
@xml
|
||||
|
||||
|
||||
|
||||
#
|
||||
@@ -99,12 +153,11 @@ module.exports = (HB)->
|
||||
'elements' : @elements.getUid()
|
||||
'tagname' : @tagname
|
||||
'uid' : @getUid()
|
||||
'uid' : @getUid()
|
||||
'prev': @prev_cl.getUid()
|
||||
'next': @next_cl.getUid()
|
||||
'prev': @prev_cl?.getUid()
|
||||
'next': @next_cl?.getUid()
|
||||
}
|
||||
if @origin isnt @prev_cl
|
||||
json["origin"] = @origin.getUid()
|
||||
json["origin"] = @origin?.getUid()
|
||||
json
|
||||
|
||||
parser['XmlType'] = (json)->
|
||||
@@ -117,7 +170,8 @@ module.exports = (HB)->
|
||||
'next': next
|
||||
'origin' : origin
|
||||
} = json
|
||||
new XmlType uid, tagname, attributes, elements, prev, next, origin
|
||||
|
||||
new XmlType uid, tagname, attributes, elements, undefined, prev, next, origin
|
||||
|
||||
|
||||
types['XmlType'] = XmlType
|
||||
|
||||
@@ -8,4 +8,6 @@ exports['JsonFramework'] =
|
||||
require './Frameworks/JsonFramework'
|
||||
exports['TextFramework'] =
|
||||
require './Frameworks/TextFramework'
|
||||
exports['XmlFramework'] =
|
||||
require './Frameworks/XmlFramework'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user