Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
282 lines (223 sloc) 6.65 KB
module Sails
# Represents a certain version of a Blip at a certain point in time. Playback
# creates instances of this class for you. Mainly is a list of contents.
class Blip
attr_accessor :name, :authors, :last_changed, :contents, :special, :annotations, :parent, :threads
def initialize name
@name = name
@authors = []
@contents = ''
@special = []
@annotations = []
@threads = []
end
def parent_blip
return nil unless @parent
return nil unless @parent.parent.is_a? Blip
@parent.parent
end
def digest
@contents.gsub("\001", '')
end
def << thread
@threads << thread
end
def children
kids = []
threads.each do |thread|
kids += thread.to_arr
end
kids
end
def flatten
kids = []
threads.each do |thread|
kids += thread.flatten
end
kids
end
# Dumps the current version of this Blip instance to XML. Note that said
# XML probably won't be value XML in practice.
def to_xml
#puts "---------"
element_stack = []
special_index = 0
annotations = {}
users = []
@annotations.each do |annotation|
annotations[annotation.start] ||= []
annotations[annotation.start] << annotation
if annotation =~ /^user\/d\/(.+)$/
users << $1
end
end
#puts "indexes: #{annotations.keys.join ', '}"
string = @contents.clone
@contents.size.times do |index|
index = @contents.size - 1 - index
#puts "at index #{index}"
(annotations[index] || []).each do |annotation|
puts "adding annotation #{annotation.key} = #{annotation.value}"
if annotation.key =~ /^user\/e\/(.+)$/
annotation.value =~ /^(.+)@(.+)$/
text = "<span class=\"blinkybit\">#{$1 || annotation.value}</span>"
users.delete $1
else
text = ''#"&lt;annotation: #{annotation.key} = #{annotation.value} /&gt;"
end
string.insert index, text
end
end
#p users
string += users.map do |user|
"<span class=\"blinkybit\">#{user}</span>"
end.join ''
#puts "---------"
open = false
string.gsub! "\001" do
item = @special[special_index]
special_index += 1
if item == :end
next '' if (element_stack.last.nil? || element_stack.last == 'body') && element_stack.pop.nil?
"</#{element_stack.pop}>"
elsif item.is_a? Element
if item.type == 'line'
tag = 'p'
tag = item['t'] if item['t']
element_stack << nil
opening = "<#{tag}>"
if open
opening = "</#{open}>#{opening}"
end
open = tag
opening
else
element_stack << item.type
next '' if item.type == 'body'
attribs = ''
item.each_pair do |key, value|
attribs << " #{key}=\"#{value}\""
end
"<#{item.type}#{attribs}>"
end
else
raise Sails::Error, "unknown document content type: #{item.class}"
end
end
string += "</#{open}>" if open
string
end
# Apply a mutation. Does NO version checking!
#
# TODO: Handle mid-string stuff. Might add a whole class for mutations.
def apply_mutate(author, operations)
pp operations
p @contents
@authors << author unless @authors.include? author
current_annotations = {}
index = 0 # in the 'contents' array
special_index = 0 # in the 'special' array
operations.compact.each do |component|
value = component.values.first
if component[:retain_item_count]
string = @contents[index, value]
special_index += string.count("\001")
index += value
elsif component[:element_start]
element = Element.new value[:type]
(value[:attributes] || []).each do |attribute|
element[attribute[:key]] = attribute[:value]
end
@contents.insert index, "\001"
@special.insert special_index, element
move_annotations index
index += 1
special_index += 1
elsif component[:update_attributes]
element = @special[special_index]
(value[:updates] || []).each do |attribute|
element[attribute[:key]] = attribute[:new_value]
end
index += 1
special_index += 1
elsif component[:element_end]
@contents.insert index, "\001"
@special.insert special_index, :end
move_annotations index
index += 1
special_index += 1
elsif component[:characters]
@contents.insert index, value
move_annotations index, value.size
index += value.size
elsif component[:delete_chars]
if @contents[index, value.size] != value
raise Sails::Error, "chars to delete didn't match existing chars"
end
move_annotations index, -value.size
@contents.slice! index, value.size
elsif component[:annotation_boundary]
(value[:key_value_update] || []).each do |update|
if update[:old_value]
if current_annotations[update[:key]]
#if !update[:new_value] || update[:new_value].empty?
# @annotations.delete current_annotations[update[:key]]
# current_annotations.delete update[:key]
#end
else
current_annotations[update[:key]] = @annotations.select do |annotation|
annotation.start <= index && annotation.end >= index && annotation.key == update[:key]
end.first
if current_annotations[update[:key]]
current_annotations[update[:key]].end = index
else
current_annotations.delete update[:key]
end
end
end
if update[:key] =~ /^user\//
@annotations.delete_if do |annotation|
annotation.key == update[:key]
end
end
update[:new_value] ||= update[:old_value]
#if update[:new_value] && update[:new_value].any?
annotation = Annotation.new update[:key], update[:new_value], index
current_annotations[update[:key]] = annotation
@annotations << annotation
#end
end
(value[:end] || []).each do |key|
if current_annotations[key]
current_annotations[key].end = index
current_annotations.delete key
end
end
end
end
p @contents
if index != @contents.size
raise Sails::Error, "didn't end up at the end of the contents array. #{@contents.size - index} bytes too short."
end
if current_annotations.any?
pp current_annotations
raise Sails::Error, "annotation update hash was left with #{current_annotations.size} unclosed annotations. supposed to be 0."
end
end
def move_annotations starting, distance=1
@annotations.each do |annotation|
annotation.start += distance if annotation.start >= starting
annotation.end += distance if annotation.end >= starting
annotation.end = annotation.start + 1 if annotation.end <= annotation.start
end
end
end # class
# Represents an element starting tag.
class Element < Hash
attr_accessor :type
def initialize(type=nil)
@type = type
super()
end
end
end # module
Something went wrong with that request. Please try again.