156 lines
4.1 KiB
JavaScript
156 lines
4.1 KiB
JavaScript
|
var reloadCSS = require('reload-css')
|
|||
|
var errorPopup = require('./error-popup')
|
|||
|
|
|||
|
module.exports = connect()
|
|||
|
|
|||
|
// EXPERIMENTAL: This feature may not last, use carefully.
|
|||
|
// Attaches the API to 'budo-livereload' so you don't need to
|
|||
|
// expose and require() it.
|
|||
|
window['budo-livereload'] = module.exports
|
|||
|
|
|||
|
function connect () {
|
|||
|
var reconnectPoll = 1000
|
|||
|
var maxRetries = 50
|
|||
|
var retries = 0
|
|||
|
var reconnectInterval
|
|||
|
var isReconnecting = false
|
|||
|
var protocol = document.location.protocol
|
|||
|
var hostname = document.location.hostname
|
|||
|
var port = document.location.port
|
|||
|
var host = hostname + ':' + port
|
|||
|
|
|||
|
var isIOS = /(iOS|iPhone|iPad|iPod)/i.test(navigator.userAgent)
|
|||
|
var isSSL = /^https:/i.test(protocol)
|
|||
|
var queued = []
|
|||
|
var socket = createWebSocket()
|
|||
|
var listeners = []
|
|||
|
|
|||
|
var api = {
|
|||
|
send: function (message) {
|
|||
|
message = JSON.stringify(message)
|
|||
|
if (socket && socket.readyState === 1) {
|
|||
|
socket.send(message)
|
|||
|
} else {
|
|||
|
queued.push(message)
|
|||
|
}
|
|||
|
},
|
|||
|
listen: function (cb) {
|
|||
|
if (typeof cb !== 'function') {
|
|||
|
throw new TypeError('cb must be a function!')
|
|||
|
}
|
|||
|
listeners.push(cb)
|
|||
|
},
|
|||
|
removeListener: function (cb) {
|
|||
|
var idx = listeners.indexOf(cb)
|
|||
|
if (idx !== -1) {
|
|||
|
listeners.splice(idx, 1)
|
|||
|
}
|
|||
|
},
|
|||
|
showError: function (message) {
|
|||
|
errorPopup.show(message)
|
|||
|
},
|
|||
|
clearError: function () {
|
|||
|
errorPopup.hide()
|
|||
|
},
|
|||
|
reloadPage: reloadPage,
|
|||
|
reloadCSS: reloadCSS
|
|||
|
}
|
|||
|
|
|||
|
return api
|
|||
|
|
|||
|
function scheduleReconnect () {
|
|||
|
if (isIOS && isSSL) {
|
|||
|
// Special case for iOS with a self-signed certificate.
|
|||
|
console.warn('[budo] LiveReload disconnected. You may need to generate and ' +
|
|||
|
'trust a self-signed certificate, see here:\n' +
|
|||
|
'https://github.com/mattdesl/budo/blob/master/docs/' +
|
|||
|
'command-line-usage.md#ssl-on-ios')
|
|||
|
return
|
|||
|
}
|
|||
|
if (isSSL) {
|
|||
|
// Don't attempt to re-connect in SSL since it will likely be insecure
|
|||
|
console.warn('[budo] LiveReload disconnected. Please reload the page to retry.')
|
|||
|
return
|
|||
|
}
|
|||
|
if (retries >= maxRetries) {
|
|||
|
console.warn('[budo] LiveReload disconnected, exceeded retry count. Please reload the page to retry.')
|
|||
|
return
|
|||
|
}
|
|||
|
if (!isReconnecting) {
|
|||
|
isReconnecting = true
|
|||
|
console.warn('[budo] LiveReload disconnected, retrying...')
|
|||
|
}
|
|||
|
retries++
|
|||
|
clearTimeout(reconnectInterval)
|
|||
|
reconnectInterval = setTimeout(reconnect, reconnectPoll)
|
|||
|
}
|
|||
|
|
|||
|
function reconnect () {
|
|||
|
if (socket) {
|
|||
|
// force close the existing socket
|
|||
|
socket.onclose = function () {}
|
|||
|
socket.close()
|
|||
|
}
|
|||
|
socket = createWebSocket()
|
|||
|
}
|
|||
|
|
|||
|
function createWebSocket () {
|
|||
|
var wsProtocol = isSSL ? 'wss://' : 'ws://'
|
|||
|
var wsUrl = wsProtocol + host + '/livereload'
|
|||
|
var ws = new window.WebSocket(wsUrl)
|
|||
|
ws.onmessage = function (event) {
|
|||
|
var data
|
|||
|
try {
|
|||
|
data = JSON.parse(event.data)
|
|||
|
} catch (err) {
|
|||
|
console.warn('Error parsing LiveReload server data: ' + event.data)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if (data.event === 'reload') {
|
|||
|
if (/^\.?css$/i.test(data.ext)) {
|
|||
|
reloadCSS(data.url)
|
|||
|
} else {
|
|||
|
reloadPage()
|
|||
|
}
|
|||
|
} else if (data.event === 'error-popup') {
|
|||
|
if (data.message) errorPopup.show(data.message)
|
|||
|
else errorPopup.hide()
|
|||
|
}
|
|||
|
|
|||
|
// let listeners receive data
|
|||
|
listeners.forEach(function (listener) {
|
|||
|
listener(data)
|
|||
|
})
|
|||
|
}
|
|||
|
ws.onclose = function (ev) {
|
|||
|
if (ev.code === 1000 || ev.code === 1001) {
|
|||
|
// Browser is navigating away.
|
|||
|
return
|
|||
|
}
|
|||
|
scheduleReconnect()
|
|||
|
}
|
|||
|
ws.onopen = function () {
|
|||
|
if (isReconnecting) {
|
|||
|
isReconnecting = false
|
|||
|
retries = 0
|
|||
|
console.warn('[budo] LiveReload reconnected.')
|
|||
|
}
|
|||
|
if (queued.length && ws.readyState === 1) {
|
|||
|
queued.forEach(function (message) {
|
|||
|
ws.send(message)
|
|||
|
})
|
|||
|
queued.length = 0
|
|||
|
}
|
|||
|
}
|
|||
|
ws.onerror = function () {
|
|||
|
return false
|
|||
|
}
|
|||
|
return ws
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function reloadPage () {
|
|||
|
window.location.reload(true)
|
|||
|
}
|