basic_types_uninitialized = require "./BasicTypes" module.exports = (HB)-> basic_types = basic_types_uninitialized HB types = basic_types.types # # @nodoc # Manages map like objects. E.g. Json-Type and XML attributes. # class types.MapManager extends types.Operation # # @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created. # constructor: (uid)-> @map = {} super uid type: "MapManager" applyDelete: ()-> for name,p of @map p.applyDelete() super() cleanup: ()-> super() # # @see JsonTypes.val # val: (name, content)-> if arguments.length > 1 @retrieveSub(name).replace content @ else if name? prop = @map[name] if prop? and not prop.isContentDeleted() prop.val() else undefined else result = {} for name,o of @map if not o.isContentDeleted() result[name] = o.val() result delete: (name)-> @map[name]?.deleteContent() @ retrieveSub: (property_name)-> if not @map[property_name]? event_properties = name: property_name event_this = @ rm_uid = noOperation: true sub: property_name alt: @ rm = new types.ReplaceManager event_properties, event_this, rm_uid # this operation shall not be saved in the HB @map[property_name] = rm rm.setParent @, property_name rm.execute() @map[property_name] # # @nodoc # Manages a list of Insert-type operations. # class types.ListManager extends types.Operation # # A ListManager maintains a non-empty list that has a beginning and an end (both Delimiters!) # @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: (uid)-> @beginning = new types.Delimiter undefined, undefined @end = new types.Delimiter @beginning, undefined @beginning.next_cl = @end @beginning.execute() @end.execute() super uid type: "ListManager" # # @private # @see Operation.execute # execute: ()-> if @validateSavedOperations() @beginning.setParent @ @end.setParent @ super else false # Get the element previous to the delemiter at the end getLastOperation: ()-> @end.prev_cl # similar to the above getFirstOperation: ()-> @beginning.next_cl # Transforms the the list to an array # Doesn't return left-right delimiter. toArray: ()-> o = @beginning.next_cl result = [] while o isnt @end result.push o o = o.next_cl result # # Retrieves the x-th not deleted element. # e.g. "abc" : the 1th character is "a" # the 0th character is the left Delimiter # getOperationByPosition: (position)-> o = @beginning while true # find the i-th op if o instanceof types.Delimiter and o.prev_cl? # the user or you gave a position parameter that is to big # for the current array. Therefore we reach a Delimiter. # Then, we'll just return the last character. o = o.prev_cl while o.isDeleted() or not (o instanceof types.Delimiter) o = o.prev_cl break if position <= 0 and not o.isDeleted() break o = o.next_cl if not o.isDeleted() position -= 1 o # # @nodoc # Adds support for replace. The ReplaceManager manages Replaceable operations. # Each Replaceable holds a value that is now replaceable. # # The TextType-type has implemented support for replace # @see TextType # class types.ReplaceManager extends types.ListManager # # @param {Object} event_properties Decorates the event that is thrown by the RM # @param {Object} event_this The object on which the event shall be executed # @param {Operation} initial_content Initialize this with a Replaceable that holds the initial_content. # @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: (@event_properties, @event_this, uid, beginning, end)-> if not @event_properties['object']? @event_properties['object'] = @event_this super uid, beginning, end type: "ReplaceManager" applyDelete: ()-> o = @beginning while o? o.applyDelete() o = o.next_cl super() cleanup: ()-> super() # # This doesn't throw the same events as the ListManager. Therefore, the # Replaceables also not throw the same events. # So, ReplaceManager and ListManager both implement # these functions that are called when an Insertion is executed (at the end). # # callEventDecorator: (events)-> if not (@isDeleted() or @getLastOperation().isDeleted()) for event in events for name,prop of @event_properties event[name] = prop @event_this.callEvent events undefined # # Replace the existing word with a new word. # # @param content {Operation} The new value of this ReplaceManager. # @param replaceable_uid {UID} Optional: Unique id of the Replaceable that is created # replace: (content, replaceable_uid)-> o = @getLastOperation() relp = (new types.Replaceable content, @, replaceable_uid, o, o.next_cl).execute() # TODO: delete repl (for debugging) undefined isContentDeleted: ()-> @getLastOperation().isDeleted() deleteContent: ()-> (new types.Delete undefined, @getLastOperation().uid).execute() undefined # # Get the value of this # @return {String} # val: ()-> o = @getLastOperation() #if o instanceof types.Delimiter # throw new Error "Replace Manager doesn't contain anything." o.val?() # ? - for the case that (currently) the RM does not contain anything (then o is a Delimiter) # # Encode this operation in such a way that it can be parsed by remote peers. # _encode: ()-> json = { 'type': @type 'uid' : @getUid() 'beginning' : @beginning.getUid() 'end' : @end.getUid() } json # # @nodoc # The ReplaceManager manages Replaceables. # @see ReplaceManager # class types.Replaceable extends types.Insert # # @param {Operation} content The value that this Replaceable holds. # @param {ReplaceManager} parent Used to replace this Replaceable with another one. # @param {Object} uid A unique identifier. If uid is undefined, a new uid will be created. # constructor: (content, parent, uid, prev, next, origin, is_deleted)-> # see encode to see, why we are doing it this way if content? and content.creator? @saveOperation 'content', content else @content = content @saveOperation 'parent', parent super uid, prev, next, origin # Parent is already saved by Replaceable @is_deleted = is_deleted type: "Replaceable" # # Return the content that this operation holds. # val: ()-> @content applyDelete: ()-> res = super if @content? if @next_cl.type isnt "Delimiter" @content.deleteAllObservers?() @content.applyDelete?() @content.dontSync?() @content = null res cleanup: ()-> super # # This is called, when the Insert-type was successfully executed. # TODO: consider doing this in a more consistent manner. This could also be # done with execute. But currently, there are no specital Insert-types for ListManager. # callOperationSpecificInsertEvents: ()-> if @next_cl.type is "Delimiter" and @prev_cl.type isnt "Delimiter" # this replaces another Replaceable if not @is_deleted # When this is received from the HB, this could already be deleted! old_value = @prev_cl.content @parent.callEventDecorator [ type: "update" changedBy: @uid.creator oldValue: old_value ] @prev_cl.applyDelete() else if @next_cl.type isnt "Delimiter" # This won't be recognized by the user, because another # concurrent operation is set as the current value of the RM @applyDelete() else # prev _and_ next are Delimiters. This is the first created Replaceable in the RM @parent.callEventDecorator [ type: "add" changedBy: @uid.creator ] undefined callOperationSpecificDeleteEvents: (o)-> if @next_cl.type is "Delimiter" @parent.callEventDecorator [ type: "delete" changedBy: o.uid.creator oldValue: @content ] # # Encode this operation in such a way that it can be parsed by remote peers. # _encode: ()-> json = { 'type': @type 'parent' : @parent.getUid() 'prev': @prev_cl.getUid() 'next': @next_cl.getUid() 'origin' : @origin.getUid() 'uid' : @getUid() 'is_deleted': @is_deleted } if @content instanceof types.Operation json['content'] = @content.getUid() else # This could be a security concern. # Throw error if the users wants to trick us if @content? and @content.creator? throw new Error "You must not set creator here!" json['content'] = @content json types.Replaceable.parse = (json)-> { 'content' : content 'parent' : parent 'uid' : uid 'prev': prev 'next': next 'origin' : origin 'is_deleted': is_deleted } = json new this(content, parent, uid, prev, next, origin, is_deleted) basic_types