ShareJsDoc.coffee 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. define [
  2. "libs/sharejs"
  3. "libs/backbone"
  4. ], (ShareJs) ->
  5. class ShareJsDoc
  6. constructor: (@doc_id, docLines, version, @socket) ->
  7. # Dencode any binary bits of data
  8. # See http://ecmanaut.blogspot.co.uk/2006/07/encoding-decoding-utf8-in-javascript.html
  9. @type = "text"
  10. docLines = for line in docLines
  11. if line.text?
  12. @type = "json"
  13. line.text = decodeURIComponent(escape(line.text))
  14. else
  15. @type = "text"
  16. line = decodeURIComponent(escape(line))
  17. line
  18. if @type == "text"
  19. snapshot = docLines.join("\n")
  20. else if @type == "json"
  21. snapshot = { lines: docLines }
  22. else
  23. throw new Error("Unknown type: #{@type}")
  24. @connection = {
  25. send: (update) =>
  26. @_startInflightOpTimeout(update)
  27. @socket.emit "applyOtUpdate", @doc_id, update
  28. state: "ok"
  29. id: @socket.socket.sessionid
  30. }
  31. @_doc = new ShareJs.Doc @connection, @doc_id,
  32. type: @type
  33. @_doc.on "change", () =>
  34. @trigger "change"
  35. @_doc.on "acknowledge", () =>
  36. @trigger "acknowledge"
  37. @_doc.on "remoteop", () =>
  38. @trigger "remoteop"
  39. @_bindToDocChanges(@_doc)
  40. @processUpdateFromServer
  41. open: true
  42. v: version
  43. snapshot: snapshot
  44. submitOp: (args...) -> @_doc.submitOp(args...)
  45. processUpdateFromServer: (message) ->
  46. try
  47. @_doc._onMessage message
  48. catch error
  49. # Version mismatches are thrown as errors
  50. @_handleError(error)
  51. if message?.meta?.type == "external"
  52. @trigger "externalUpdate"
  53. catchUp: (updates) ->
  54. for update, i in updates
  55. update.v = @_doc.version
  56. update.doc = @doc_id
  57. @processUpdateFromServer(update)
  58. getSnapshot: () -> @_doc.snapshot
  59. getVersion: () -> @_doc.version
  60. getType: () -> @type
  61. clearInflightAndPendingOps: () ->
  62. @_doc.inflightOp = null
  63. @_doc.inflightCallbacks = []
  64. @_doc.pendingOp = null
  65. @_doc.pendingCallbacks = []
  66. flushPendingOps: () ->
  67. # This will flush any ops that are pending.
  68. # If there is an inflight op it will do nothing.
  69. @_doc.flush()
  70. updateConnectionState: (state) ->
  71. @connection.state = state
  72. @connection.id = @socket.socket.sessionid
  73. @_doc.autoOpen = false
  74. @_doc._connectionStateChanged(state)
  75. hasBufferedOps: () ->
  76. @_doc.inflightOp? or @_doc.pendingOp?
  77. getInflightOp: () -> @_doc.inflightOp
  78. getPendingOp: () -> @_doc.pendingOp
  79. attachToAce: (ace) -> @_doc.attach_ace(ace)
  80. detachFromAce: () -> @_doc.detach_ace?()
  81. INFLIGHT_OP_TIMEOUT: 10000
  82. _startInflightOpTimeout: (update) ->
  83. meta =
  84. v: update.v
  85. op_sent_at: new Date()
  86. timer = setTimeout () =>
  87. @trigger "op:timeout", update
  88. , @INFLIGHT_OP_TIMEOUT
  89. @_doc.inflightCallbacks.push () =>
  90. clearTimeout timer
  91. _handleError: (error, meta = {}) ->
  92. @trigger "error", error, meta
  93. _bindToDocChanges: (doc) ->
  94. submitOp = doc.submitOp
  95. doc.submitOp = (args...) =>
  96. @trigger "op:sent", args...
  97. doc.pendingCallbacks.push () =>
  98. @trigger "op:acknowledged", args...
  99. submitOp.apply(doc, args)
  100. flush = doc.flush
  101. doc.flush = (args...) =>
  102. @trigger "flush", doc.inflightOp, doc.pendingOp, doc.version
  103. flush.apply(doc, args)
  104. _.extend(ShareJsDoc::, Backbone.Events)
  105. return ShareJsDoc