Changeset - c20dc52e5593
[Not reviewed]
default
0 2 1
Drew Perttula - 9 years ago 2016-06-05 09:53:24
drewp@bigasterisk.com
move to rdfdbclient.coffee
Ignore-this: 16a68c0fd3efffb27e6037570c1a48b1
3 files changed with 143 insertions and 139 deletions:
0 comments (0 inline, 0 general)
light9/web/graph.coffee
Show inline comments
 
@@ -3,7 +3,8 @@ log = console.log
 
# Patch is {addQuads: <quads>, delQuads: <quads>}
 
# <quads> is [{subject: s, ...}, ...]
 

	
 
patchSizeSummary = (patch) ->
 
# (sloppily shared to rdfdbclient.coffee too)
 
window.patchSizeSummary = (patch) ->
 
  '-' + patch.delQuads.length + ' +' + patch.addQuads.length
 

	
 
# partial port of autodepgraphapi.py
 
@@ -45,144 +46,6 @@ class GraphWatchers
 
        cb({delQuads: [], addQuads: [quad]})
 

	
 

	
 
toJsonPatch = (jsPatch, cb) ->
 
  out = {patch: {adds: '', deletes: ''}}
 

	
 
  writeDels = (cb) ->
 
    writer = N3.Writer({ format: 'N-Quads' })
 
    writer.addTriples(jsPatch.delQuads)
 
    writer.end((err, result) ->
 
      out.patch.deletes = result
 
      cb())
 

	
 
  writeAdds = (cb) ->
 
    writer = N3.Writer({ format: 'N-Quads' })
 
    writer.addTriples(jsPatch.addQuads)
 
    writer.end((err, result) ->
 
      out.patch.adds = result
 
      cb())
 
    
 
  async.parallel([writeDels, writeAdds], (err) ->
 
      cb(JSON.stringify(out))
 
    )
 

	
 
parseJsonPatch = (jsonPatch, cb) ->
 
  # note response cb doesn't have an error arg.
 
  input = JSON.parse(jsonPatch)
 
  patch = {delQuads: [], addQuads: []}
 

	
 
  parseAdds = (cb) =>
 
    parser = N3.Parser()
 
    parser.parse input.patch.adds, (error, quad, prefixes) =>
 
                  if (quad)
 
                    patch.addQuads.push(quad)
 
                  else
 
                    cb()
 
  parseDels = (cb) =>
 
    parser = N3.Parser()
 
    parser.parse input.patch.deletes, (error, quad, prefixes) =>
 
                  if (quad)
 
                    patch.delQuads.push(quad)
 
                  else
 
                    cb()
 

	
 
  async.parallel([parseAdds, parseDels], ((err) => cb(patch)))
 

	
 
class RdfDbClient
 
  # Send and receive patches from rdfdb
 
  constructor: (@patchSenderUrl, @clearGraph, @applyPatch, @setStatus) ->
 
    @_patchesToSend = []
 
    @_lastPingMs = -1
 
    @_patchesReceived = 0
 
    @_patchesSent = 0
 

	
 
    @_reconnectionTimeout = null
 
    @_newConnection()
 

	
 
  _updateStatus: ->
 
    ws = (if not @ws? then 'no' else switch @ws.readyState
 
      when @ws.CONNECTING then 'connecting'
 
      when @ws.OPEN then 'open'
 
      when @ws.CLOSING then 'closing'
 
      when @ws.CLOSED then 'close'
 
      )
 

	
 
    ping = if @_lastPingMs > 0 then @_lastPingMs else '...'
 
    @setStatus("#{ws};
 
      #{@_patchesReceived} recv
 
      #{@_patchesSent} sent
 
      #{@_patchesToSend.length} pending;
 
      #{ping}ms")
 
 
 
  sendPatch: (patch) ->
 
    console.log('queue patch to server ', patchSizeSummary(patch))
 
    @_patchesToSend.push(patch)
 
    @_updateStatus()
 
    @_continueSending()           
 

	
 
  _newConnection: ->
 
    fullUrl = 'ws://' + window.location.host + @patchSenderUrl
 
    @ws.close() if @ws?
 
    @ws = new WebSocket(fullUrl)
 

	
 
    @ws.onopen = =>
 
      log('connected to', fullUrl)
 
      @_updateStatus()
 
      @clearGraph()
 
      @_pingLoop()
 

	
 
    @ws.onerror = (e) =>
 
      log('ws error ' + e)
 
      @ws.onclose()
 

	
 
    @ws.onclose = =>
 
      log('ws close')
 
      @_updateStatus()
 
      clearTimeout(@_reconnectionTimeout) if @_reconnectionTimeout?
 
      @_reconnectionTimeout = setTimeout(@_newConnection.bind(@), 1000)
 

	
 
    @ws.onmessage = @_onMessage.bind(@)
 

	
 
  _pingLoop: () ->
 
    if @ws.readyState == @ws.OPEN
 
      @ws.send('PING')
 
      @_lastPingMs = -Date.now()
 
      
 
      clearTimeout(@_pingLoopTimeout) if @_pingLoopTimeout?
 
      @_pingLoopTimeout = setTimeout(@_pingLoop.bind(@), 10000)
 

	
 
  _onMessage: (evt) ->
 
    msg = evt.data
 
    if msg == 'PONG'
 
      @_lastPingMs = Date.now() + @_lastPingMs
 
      @_updateStatus()
 
      return
 
    parseJsonPatch(msg, @applyPatch.bind(@))
 
    @_patchesReceived++
 
    @_updateStatus()
 

	
 
  _continueSending: ->
 
    if @ws.readyState != @ws.OPEN
 
      setTimeout(@_continueSending.bind(@), 500)
 
      return
 

	
 
    # we could call this less often and coalesce patches together to optimize
 
    # the dragging cases.
 

	
 
    sendOne = (patch, cb) =>
 
        toJsonPatch(patch, (json) =>
 
          log('send patch to server, ' + json.length + ' bytes')
 
          @ws.send(json)
 
          @_patchesSent++
 
          @_updateStatus()
 
          cb(null)
 
      )
 

	
 
    async.eachSeries(@_patchesToSend, sendOne, () =>
 
        @_patchesToSend = []
 
        @_updateStatus()
 
      )
 

	
 
class window.SyncedGraph
 
  # Main graph object for a browser to use. Syncs both ways with
 
  # rdfdb. Meant to hide the choice of RDF lib, so we can change it
light9/web/rdfdb-synced-graph.html
Show inline comments
 
@@ -13,6 +13,7 @@
 
    </style>
 
    <span>[[status]]</span>
 
  </template>
 
  <script src="rdfdbclient.js"></script>
 
  <script src="graph.js"></script>
 
  <script>
 
   Polymer({
light9/web/rdfdbclient.coffee
Show inline comments
 
new file 100644
 
log = console.log
 

	
 

	
 
toJsonPatch = (jsPatch, cb) ->
 
  out = {patch: {adds: '', deletes: ''}}
 

	
 
  writeDels = (cb) ->
 
    writer = N3.Writer({ format: 'N-Quads' })
 
    writer.addTriples(jsPatch.delQuads)
 
    writer.end((err, result) ->
 
      out.patch.deletes = result
 
      cb())
 

	
 
  writeAdds = (cb) ->
 
    writer = N3.Writer({ format: 'N-Quads' })
 
    writer.addTriples(jsPatch.addQuads)
 
    writer.end((err, result) ->
 
      out.patch.adds = result
 
      cb())
 
    
 
  async.parallel([writeDels, writeAdds], (err) ->
 
      cb(JSON.stringify(out))
 
    )
 

	
 
parseJsonPatch = (jsonPatch, cb) ->
 
  # note response cb doesn't have an error arg.
 
  input = JSON.parse(jsonPatch)
 
  patch = {delQuads: [], addQuads: []}
 

	
 
  parseAdds = (cb) =>
 
    parser = N3.Parser()
 
    parser.parse input.patch.adds, (error, quad, prefixes) =>
 
                  if (quad)
 
                    patch.addQuads.push(quad)
 
                  else
 
                    cb()
 
  parseDels = (cb) =>
 
    parser = N3.Parser()
 
    parser.parse input.patch.deletes, (error, quad, prefixes) =>
 
                  if (quad)
 
                    patch.delQuads.push(quad)
 
                  else
 
                    cb()
 

	
 
  async.parallel([parseAdds, parseDels], ((err) => cb(patch)))
 

	
 
class window.RdfDbClient
 
  # Send and receive patches from rdfdb
 
  constructor: (@patchSenderUrl, @clearGraph, @applyPatch, @setStatus) ->
 
    @_patchesToSend = []
 
    @_lastPingMs = -1
 
    @_patchesReceived = 0
 
    @_patchesSent = 0
 

	
 
    @_reconnectionTimeout = null
 
    @_newConnection()
 

	
 
  _updateStatus: ->
 
    ws = (if not @ws? then 'no' else switch @ws.readyState
 
      when @ws.CONNECTING then 'connecting'
 
      when @ws.OPEN then 'open'
 
      when @ws.CLOSING then 'closing'
 
      when @ws.CLOSED then 'close'
 
      )
 

	
 
    ping = if @_lastPingMs > 0 then @_lastPingMs else '...'
 
    @setStatus("#{ws};
 
      #{@_patchesReceived} recv
 
      #{@_patchesSent} sent
 
      #{@_patchesToSend.length} pending;
 
      #{ping}ms")
 
 
 
  sendPatch: (patch) ->
 
    console.log('queue patch to server ', patchSizeSummary(patch))
 
    @_patchesToSend.push(patch)
 
    @_updateStatus()
 
    @_continueSending()           
 

	
 
  _newConnection: ->
 
    fullUrl = 'ws://' + window.location.host + @patchSenderUrl
 
    @ws.close() if @ws?
 
    @ws = new WebSocket(fullUrl)
 

	
 
    @ws.onopen = =>
 
      log('connected to', fullUrl)
 
      @_updateStatus()
 
      @clearGraph()
 
      @_pingLoop()
 

	
 
    @ws.onerror = (e) =>
 
      log('ws error ' + e)
 
      @ws.onclose()
 

	
 
    @ws.onclose = =>
 
      log('ws close')
 
      @_updateStatus()
 
      clearTimeout(@_reconnectionTimeout) if @_reconnectionTimeout?
 
      @_reconnectionTimeout = setTimeout(@_newConnection.bind(@), 1000)
 

	
 
    @ws.onmessage = @_onMessage.bind(@)
 

	
 
  _pingLoop: () ->
 
    if @ws.readyState == @ws.OPEN
 
      @ws.send('PING')
 
      @_lastPingMs = -Date.now()
 
      
 
      clearTimeout(@_pingLoopTimeout) if @_pingLoopTimeout?
 
      @_pingLoopTimeout = setTimeout(@_pingLoop.bind(@), 10000)
 

	
 
  _onMessage: (evt) ->
 
    msg = evt.data
 
    if msg == 'PONG'
 
      @_lastPingMs = Date.now() + @_lastPingMs
 
      @_updateStatus()
 
      return
 
    parseJsonPatch(msg, @applyPatch.bind(@))
 
    @_patchesReceived++
 
    @_updateStatus()
 

	
 
  _continueSending: ->
 
    if @ws.readyState != @ws.OPEN
 
      setTimeout(@_continueSending.bind(@), 500)
 
      return
 

	
 
    # we could call this less often and coalesce patches together to optimize
 
    # the dragging cases.
 

	
 
    sendOne = (patch, cb) =>
 
        toJsonPatch(patch, (json) =>
 
          log('send patch to server, ' + json.length + ' bytes')
 
          @ws.send(json)
 
          @_patchesSent++
 
          @_updateStatus()
 
          cb(null)
 
      )
 

	
 
    async.eachSeries(@_patchesToSend, sendOne, () =>
 
        @_patchesToSend = []
 
        @_updateStatus()
 
      )
0 comments (0 inline, 0 general)