Cleaning up. No more bubbling events. All tests run fine. Following the Object.observe pattern (untested).
This commit is contained in:
@@ -19,12 +19,13 @@ module.exports = (HB)->
|
||||
class Operation
|
||||
|
||||
#
|
||||
# @param {Object} uid A unique identifier.
|
||||
# @param {Object} uid A unique identifier.
|
||||
# If uid is undefined, a new uid will be created before at the end of the execution sequence
|
||||
#
|
||||
constructor: (uid)->
|
||||
@is_deleted = false
|
||||
@garbage_collected = false
|
||||
@event_listeners = [] # TODO: rename to observers or sth like that
|
||||
if uid?
|
||||
@uid = uid
|
||||
|
||||
@@ -32,41 +33,27 @@ module.exports = (HB)->
|
||||
|
||||
#
|
||||
# Add an event listener. It depends on the operation which events are supported.
|
||||
# @param {String} event Name of the event.
|
||||
# @param {Function} f f is executed in case the event fires.
|
||||
#
|
||||
on: (events, f)->
|
||||
@event_listeners ?= {}
|
||||
if events.constructor isnt [].constructor
|
||||
events = [events]
|
||||
for e in events
|
||||
@event_listeners[e] ?= []
|
||||
@event_listeners[e].push f
|
||||
observe: (f)->
|
||||
@event_listeners.push f
|
||||
|
||||
#
|
||||
# Deletes a function from an event / list of events.
|
||||
# @see Operation.on
|
||||
# Deletes function from the observer list
|
||||
# @see Operation.observe
|
||||
#
|
||||
# @overload deleteListener(event, f)
|
||||
# @param event {String} An event name
|
||||
# @param f {Function} The function that you want to delete from these events
|
||||
# @overload deleteListener(events, f)
|
||||
# @param events {Array<String>} A list of event names
|
||||
# @param f {Function} The function that you want to delete from these events.
|
||||
deleteListener: (events, f)->
|
||||
if events.constructor isnt [].constructor
|
||||
events = [events]
|
||||
for e in events
|
||||
if @event_listeners?[e]?
|
||||
@event_listeners[e] = @event_listeners[e].filter (g)->
|
||||
f isnt g
|
||||
|
||||
#
|
||||
# Deletes all subscribed event listeners.
|
||||
# This should be called, e.g. after this has been replaced.
|
||||
# @overload unobserve(event, f)
|
||||
# @param f {Function} The function that you want to delete
|
||||
unobserve: (f)->
|
||||
@event_listeners.filter (g)->
|
||||
f isnt g
|
||||
|
||||
#
|
||||
# Deletes all subscribed event listeners.
|
||||
# This should be called, e.g. after this has been replaced.
|
||||
# (Then only one replace event should fire. )
|
||||
# This is also called in the cleanup method.
|
||||
deleteAllListeners: ()->
|
||||
# This is also called in the cleanup method.
|
||||
deleteAllObservers: ()->
|
||||
@event_listeners = []
|
||||
|
||||
#
|
||||
@@ -78,11 +65,10 @@ module.exports = (HB)->
|
||||
|
||||
#
|
||||
# Fire an event and specify in which context the listener is called (set 'this').
|
||||
#
|
||||
forwardEvent: (op, event, args...)->
|
||||
if @event_listeners?[event]?
|
||||
for f in @event_listeners[event]
|
||||
f.call op, event, args...
|
||||
# TODO: do you need this ?
|
||||
forwardEvent: (op, args...)->
|
||||
for f in @event_listeners
|
||||
f.call op, args...
|
||||
|
||||
isDeleted: ()->
|
||||
@is_deleted
|
||||
@@ -98,7 +84,7 @@ module.exports = (HB)->
|
||||
cleanup: ()->
|
||||
#console.log "cleanup: #{@type}"
|
||||
HB.removeOperation @
|
||||
@deleteAllListeners()
|
||||
@deleteAllObservers()
|
||||
|
||||
#
|
||||
# Set the parent of this operation.
|
||||
@@ -128,15 +114,15 @@ module.exports = (HB)->
|
||||
#
|
||||
execute: ()->
|
||||
@is_executed = true
|
||||
if not @uid?
|
||||
# When this operation was created without a uid, then set it here.
|
||||
# There is only one other place, where this can be done - before an Insertion
|
||||
if not @uid?
|
||||
# When this operation was created without a uid, then set it here.
|
||||
# There is only one other place, where this can be done - before an Insertion
|
||||
# is executed (because we need the creator_id)
|
||||
@uid = HB.getNextOperationIdentifier()
|
||||
@uid = HB.getNextOperationIdentifier()
|
||||
HB.addOperation @
|
||||
for l in execution_listener
|
||||
l @_encode()
|
||||
@
|
||||
@
|
||||
|
||||
#
|
||||
# @private
|
||||
@@ -388,7 +374,7 @@ module.exports = (HB)->
|
||||
if parent? and fire_event
|
||||
@setParent parent
|
||||
@parent.callEvent "insert", @
|
||||
@
|
||||
@
|
||||
|
||||
#
|
||||
# Compute the position of this operation.
|
||||
@@ -501,7 +487,7 @@ module.exports = (HB)->
|
||||
delete @prev_cl.unchecked.next_cl
|
||||
@prev_cl.next_cl = @
|
||||
super
|
||||
else if @prev_cl? or @next_cl? or true # TODO: are you sure? This can happen right?
|
||||
else if @prev_cl? or @next_cl? or true # TODO: are you sure? This can happen right?
|
||||
super
|
||||
#else
|
||||
# throw new Error "Delimiter is unsufficient defined!"
|
||||
|
||||
@@ -115,12 +115,16 @@ module.exports = (HB)->
|
||||
|
||||
cleanup: ()->
|
||||
super()
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Transform this to a Json. If your browser supports Object.observe it will be transformed automatically when a change arrives.
|
||||
# Transform this to a Json. If your browser supports Object.observe it will be transformed automatically when a change arrives.
|
||||
# Otherwise you will loose all the sharing-abilities (the new object will be a deep clone)!
|
||||
# @return {Json}
|
||||
#
|
||||
# TODO: at the moment you don't consider changing of properties.
|
||||
# E.g.: let x = {a:[]}. Then x.a.push 1 wouldn't change anything
|
||||
#
|
||||
toJson: ()->
|
||||
if not @bound_json? or not Object.observe? or true # TODO: currently, you are not watching mutable strings for changes, and, therefore, the @bound_json is not updated. TODO TODO wuawuawua easy
|
||||
val = @val()
|
||||
@@ -137,14 +141,14 @@ module.exports = (HB)->
|
||||
else
|
||||
json[name] = o
|
||||
@bound_json = json
|
||||
if Object.observe?
|
||||
if Object.observe?
|
||||
that = @
|
||||
Object.observe @bound_json, (events)->
|
||||
for event in events
|
||||
if not event.changed_by? and (event.type is "add" or event.type = "update")
|
||||
# this event is not created by Yatta.
|
||||
that.val(event.name, event.object[event.name])
|
||||
that.on 'change', (event_name, property_name, op)->
|
||||
that.observe (event_name, property_name, op)->
|
||||
if this is that and op.uid.creator isnt HB.getUserId()
|
||||
notifier = Object.getNotifier(that.bound_json)
|
||||
oldVal = that.bound_json[property_name]
|
||||
@@ -152,17 +156,17 @@ module.exports = (HB)->
|
||||
notifier.performChange 'update', ()->
|
||||
that.bound_json[property_name] = that.val(property_name)
|
||||
, that.bound_json
|
||||
notifier.notify
|
||||
notifier.notify
|
||||
object: that.bound_json
|
||||
type: 'update'
|
||||
name: property_name
|
||||
oldValue: oldVal
|
||||
changed_by: op.uid.creator
|
||||
else
|
||||
else
|
||||
notifier.performChange 'add', ()->
|
||||
that.bound_json[property_name] = that.val(property_name)
|
||||
, that.bound_json
|
||||
notifier.notify
|
||||
notifier.notify
|
||||
object: that.bound_json
|
||||
type: 'add'
|
||||
name: property_name
|
||||
@@ -170,23 +174,6 @@ module.exports = (HB)->
|
||||
changed_by: op.uid.creator
|
||||
@bound_json
|
||||
|
||||
#
|
||||
# @see WordType.setReplaceManager
|
||||
# Sets the parent of this JsonType object.
|
||||
#
|
||||
setReplaceManager: (replace_manager)->
|
||||
@replace_manager = replace_manager
|
||||
@on ['change','addProperty'], ()->
|
||||
if replace_manager.parent?
|
||||
replace_manager.parent.forwardEvent this, arguments...
|
||||
|
||||
#
|
||||
# Get the parent of this JsonType.
|
||||
# @return {JsonType}
|
||||
#
|
||||
getParent: ()->
|
||||
@replace_manager.parent
|
||||
|
||||
#
|
||||
# Whether the default is 'mutable' (true) or 'immutable' (false)
|
||||
#
|
||||
@@ -222,16 +209,7 @@ module.exports = (HB)->
|
||||
# @return [JsonType] This object. (supports chaining)
|
||||
#
|
||||
val: (name, content, mutable)->
|
||||
if typeof name is 'object'
|
||||
# Special case. First argument is an object. Then the second arg is mutable.
|
||||
# (I refer to var name and content here)
|
||||
# Keep that in mind when reading the following..
|
||||
jt = new JsonType()
|
||||
@replace_manager.replace jt.execute()
|
||||
for n,o of name
|
||||
jt.val n, o, mutable
|
||||
@
|
||||
else if name? and arguments.length > 1
|
||||
if name? and arguments.length > 1
|
||||
if mutable?
|
||||
if mutable is true or mutable is 'mutable'
|
||||
mutable = true
|
||||
|
||||
@@ -91,10 +91,10 @@ module.exports = (HB)->
|
||||
# always have the same result (ReplaceManager, and its beginning and end are the same)
|
||||
#
|
||||
execute: ()->
|
||||
if not @validateSavedOperations()
|
||||
if not @validateSavedOperations()
|
||||
return false
|
||||
else
|
||||
# helper for cloning an object
|
||||
# helper for cloning an object
|
||||
clone = (o)->
|
||||
p = {}
|
||||
for name,value of o
|
||||
@@ -110,7 +110,7 @@ module.exports = (HB)->
|
||||
uid_end.op_number = "#{uid_r.op_number}_end"
|
||||
beg = (new types.Delimiter uid_beg, undefined, uid_end).execute()
|
||||
end = (new types.Delimiter uid_end, beg, undefined).execute()
|
||||
@map_manager.map[@name] = new ReplaceManager undefined, uid_r, beg, end
|
||||
@map_manager.map[@name] = new ReplaceManager uid_r, beg, end
|
||||
@map_manager.map[@name].setParent @map_manager, @name
|
||||
(@map_manager.map[@name].add_name_ops ?= []).push @
|
||||
@map_manager.map[@name].execute()
|
||||
@@ -224,10 +224,8 @@ module.exports = (HB)->
|
||||
# @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created.
|
||||
# @param {Delimiter} beginning Reference or Object.
|
||||
# @param {Delimiter} end Reference or Object.
|
||||
constructor: (initial_content, uid, beginning, end, prev, next, origin)->
|
||||
super uid, beginning, end, prev, next, origin
|
||||
if initial_content?
|
||||
@replace initial_content
|
||||
# constructor: (uid, beginning, end, prev, next, origin)->
|
||||
# super uid, beginning, end, prev, next, origin
|
||||
|
||||
type: "ReplaceManager"
|
||||
|
||||
@@ -256,24 +254,6 @@ module.exports = (HB)->
|
||||
(new Replaceable content, @, replaceable_uid, o, o.next_cl).execute()
|
||||
undefined
|
||||
|
||||
#
|
||||
# Add change listeners for parent.
|
||||
#
|
||||
setParent: (parent, property_name)->
|
||||
repl_manager = this
|
||||
@on 'insert', (event, op)->
|
||||
if op.next_cl instanceof types.Delimiter
|
||||
repl_manager.parent.callEvent 'change', property_name, op
|
||||
@on 'change', (event, op)->
|
||||
if repl_manager isnt this
|
||||
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.deleteListener 'insert', addPropertyListener
|
||||
repl_manager.parent.callEvent 'addProperty', property_name, op
|
||||
@on 'insert', addPropertyListener
|
||||
super parent
|
||||
|
||||
#
|
||||
# Get the value of this WordType
|
||||
# @return {String}
|
||||
@@ -304,7 +284,6 @@ module.exports = (HB)->
|
||||
|
||||
parser["ReplaceManager"] = (json)->
|
||||
{
|
||||
'content' : content
|
||||
'uid' : uid
|
||||
'prev': prev
|
||||
'next': next
|
||||
@@ -312,7 +291,7 @@ module.exports = (HB)->
|
||||
'beginning' : beginning
|
||||
'end' : end
|
||||
} = json
|
||||
new ReplaceManager content, uid, beginning, end, prev, next, origin
|
||||
new ReplaceManager uid, beginning, end, prev, next, origin
|
||||
|
||||
|
||||
#
|
||||
@@ -342,16 +321,10 @@ module.exports = (HB)->
|
||||
val: ()->
|
||||
@content
|
||||
|
||||
#
|
||||
# Replace the content of this replaceable with new content.
|
||||
#
|
||||
replace: (content)->
|
||||
@parent.replace content
|
||||
|
||||
applyDelete: ()->
|
||||
if @content?
|
||||
if @next_cl.type isnt "Delimiter"
|
||||
@content.deleteAllListeners()
|
||||
@content.deleteAllObservers()
|
||||
@content.applyDelete()
|
||||
@content.dontSync()
|
||||
@content = null
|
||||
@@ -361,16 +334,14 @@ module.exports = (HB)->
|
||||
super
|
||||
|
||||
#
|
||||
# If possible set the replace manager in the content.
|
||||
# @see WordType.setReplaceManager
|
||||
#
|
||||
execute: ()->
|
||||
if not @validateSavedOperations()
|
||||
return false
|
||||
else
|
||||
@content?.setReplaceManager?(@parent)
|
||||
# only fire 'insert-event' (which will result in addProperty and change events),
|
||||
# when content is added. In case of Json, empty content means that this is not the last update,
|
||||
# when content is added.
|
||||
# In case of Json, empty content means that this is not the last update,
|
||||
# since content is deleted when 'applyDelete' was exectuted.
|
||||
ins_result = super(@content?) # @content? whether to fire or not
|
||||
if ins_result
|
||||
|
||||
@@ -176,23 +176,6 @@ module.exports = (HB)->
|
||||
delete_ops.push d._encode()
|
||||
@
|
||||
|
||||
#
|
||||
# Replace the content of this word with another one. Concurrent replacements are not merged!
|
||||
# Only one of the replacements will be used.
|
||||
#
|
||||
# @return {WordType} Returns the new WordType object.
|
||||
#
|
||||
replaceText: (text)->
|
||||
# Can only be used if the ReplaceManager was set!
|
||||
# @see WordType.setReplaceManager
|
||||
if @replace_manager?
|
||||
word = (new WordType undefined).execute()
|
||||
word.insertText 0, text
|
||||
@replace_manager.replace(word)
|
||||
word
|
||||
else
|
||||
throw new Error "This type is currently not maintained by a ReplaceManager!"
|
||||
|
||||
#
|
||||
# Get the String-representation of this word.
|
||||
# @return {String} The String-representation of this object.
|
||||
@@ -213,18 +196,6 @@ module.exports = (HB)->
|
||||
@val()
|
||||
|
||||
#
|
||||
# @private
|
||||
# In most cases you would embed a WordType in a Replaceable, wich is handled by the ReplaceManager in order
|
||||
# to provide replace functionality.
|
||||
#
|
||||
setReplaceManager: (op)->
|
||||
@saveOperation 'replace_manager', op
|
||||
@validateSavedOperations()
|
||||
@on 'insert', (event, ins)=>
|
||||
@replace_manager?.forwardEvent @, 'change', ins
|
||||
@on 'delete', (event, ins, del)=>
|
||||
@replace_manager?.forwardEvent @, 'change', del
|
||||
#
|
||||
# Bind this WordType to a textfield or input field.
|
||||
#
|
||||
# @example
|
||||
|
||||
@@ -135,7 +135,7 @@ module.exports = (HB)->
|
||||
dont_proxy ()->
|
||||
that.xml.removeChild deleted
|
||||
|
||||
@attributes.on ['addProperty', 'change'], (event, property_name, op)->
|
||||
@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()
|
||||
|
||||
Reference in New Issue
Block a user