-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathiframe.coffee
More file actions
145 lines (115 loc) · 3.87 KB
/
iframe.coffee
File metadata and controls
145 lines (115 loc) · 3.87 KB
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
Base = require './base'
class IframeRuntime extends Base
constructor: (definition) ->
super definition
@origin = window.location.origin
@connecting = false
@connected = false
@buffer = []
@iframe = null
getElement: -> @iframe
isConnected: -> @connected
setMain: (graph) ->
if @graph
# Unsubscribe from previous main graph
@graph.removeListener 'changeProperties', @updateIframe
return super graph unless graph
# Update contents on property changes
graph.on 'changeProperties', @updateIframe
super graph
# Ensure iframe gets updated
do @updateIframe
return
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: ->
if @iframe
@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
msg = @_prepareMessage protocol, command, payload
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(msg), '*'
return
w.postMessage JSON.stringify(msg), w.location.href
onMessage: (message) =>
if message.source and message.source isnt @iframe.contentWindow
# Message from unrelated source
return
if typeof message.data is 'string'
data = JSON.parse message.data
else
data = message.data
@recvMessage data
flush: ->
for item in @buffer
@postMessage item.protocol, item.command, item.payload
@buffer = []
module.exports = IframeRuntime