-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathiframe.coffee
147 lines (120 loc) · 4.06 KB
/
iframe.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
Base = require './base'
class IframeRuntime extends Base
constructor: (definition) ->
@origin = window.location.origin
@connecting = false
@connected = false
@buffer = []
@iframe = null
super definition
getElement: -> @iframe
isConnected: -> @connected
setMain: (graph) ->
if @graph
# Unsubscribe from previous main graph
@graph.removeListener 'changeProperties', @updateIframe
# Update contents on property changes
graph.on 'changeProperties', @updateIframe if graph
# Ensure iframe gets updated
do @updateIframe
super graph
setParentElement: (parent) ->
@iframe = document.createElement 'iframe'
@iframe.setAttribute 'sandbox', 'allow-scripts allow-same-origin allow-forms'
parent.appendChild @iframe
connect: ->
unless @iframe
throw new Error 'Unable to connect without a parent element'
@iframe.addEventListener 'load', @onLoaded, false
# Let the UI know we're connecting
@connecting = true
@emit 'status',
online: false
label: 'connecting'
# Set the source to the iframe so that it can load
@iframe.setAttribute 'src', @getAddress()
# Set an ID for targeting purposes
@iframe.id = 'preview-iframe'
# Start listening for messages from the iframe
window.addEventListener 'message', @onMessage, false
updateIframe: =>
return if !@iframe or !@graph
env = @graph.properties.environment
return if !env or !env.content
@send 'iframe', 'setcontent', env.content
disconnect: ->
@iframe.removeEventListener 'load', @onLoaded, false
@connected = false
# Stop listening to messages
window.removeEventListener 'message', @onMessage, false
@emit 'status',
online: false
label: 'disconnected'
@emit 'disconnected'
# Called every time the iframe has loaded successfully
onLoaded: =>
# Since the iframe runtime runs in user's browser, being loaded doesn't
# necessarily mean that the runtime has started. Especially on slower
# mobile devices the runtime initialization can still take a while.
# Because of this we loop requesting runtime info until runtime
# responds and only then consider ourselves connected.
@once 'capabilities', =>
# Runtime responded with a capabilities message. We're live!
clearTimeout timeout if timeout
@connecting = false
@connected = true
@emit 'status',
online: true
label: 'connected'
@emit 'connected'
do @updateIframe
@flush()
# Start requesting capabilities
@postMessage 'runtime', 'getruntime', {}
timeout = setTimeout =>
# Keep trying until runtime responds
@postMessage 'runtime', 'getruntime', {}
, 500
send: (protocol, command, payload) ->
if @connecting
@buffer.push
protocol: protocol
command: command
payload: payload
return
@postMessage protocol, command, payload
postMessage: (protocol, command, payload) ->
w = @iframe.contentWindow
return unless w
try
return if w.location.href is 'about:blank'
if w.location.href.indexOf('chrome-extension://') isnt -1
throw new Error 'Use * for IFRAME communications in a Chrome app'
catch e
# Chrome Apps
w.postMessage JSON.stringify(
protocol: protocol
command: command
payload: payload
), '*'
return
w.postMessage JSON.stringify(
protocol: protocol
command: command
payload: payload
), w.location.href
onMessage: (message) =>
if typeof message.data is 'string'
data = JSON.parse message.data
else
data = message.data
switch data.protocol
when 'runtime' then @recvRuntime data.command, data.payload
when 'graph' then @recvGraph data.command, data.payload
when 'network' then @recvNetwork data.command, data.payload
when 'component' then @recvComponent data.command, data.payload
flush: ->
for item in @buffer
@postMessage item.protocol, item.command, item.payload
@buffer = []
module.exports = IframeRuntime