-
Notifications
You must be signed in to change notification settings - Fork 2
/
bootloader.js
182 lines (169 loc) · 4.91 KB
/
bootloader.js
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
var SecureUrl = require('./fetch')
var u = require('./util')
module.exports = function (prefix, store, log) {
var appname = prefix
var wb, running = false
//destroy everything
function scorchedEarth () {
for(var k in localStorage) {
delete localStorage[k]
}
}
function onProgress (ev) {
wb.onprogress && wb.onprogress(ev)
}
var init = '#'+appname+'_INIT'
return wb = {
scorchedEarth: scorchedEarth,
isInit: function () {
return location.hash.substring(0, init.length) === init
},
setup: function () {
if(!wb.isInit())
location.hash = init + location.hash
location.reload()
},
reinitialize: function (cb) {
log.destroy(function () {
store.destroy(cb)
})
},
install: function (url, cb) {
onProgress('installing from:'+url)
var id = SecureUrl.isSecureUrl(url)
if(!id) return cb(new Error('not a secure url:'+url))
//check whether we already have this
//before downloading anything
store.get(id, function (err, data) {
if(!err) return cb(null, data, id)
SecureUrl(url, function (err, data, id) {
if(err) cb(err)
else store.add(data, id, function (err) {
cb(err, data, id)
})
})
})
},
installAndRun: function (url, cb) {
wb.install(url, function (err, _, id) {
if(err) cb(err)
else wb.run(id, cb)
})
},
add: store.add,
run: function (id, cb) {
if(SecureUrl.isSecureUrl(id))
return cb(new Error('use WebBoot.installAndRun, to load a secure url'))
if(!id) return cb(new Error('WebBoot.run: id must be provided'))
var _id
//if we are already running, restart
//clear out init code, if we are in setup mode
if(wb.isInit())
location.hash = location.hash.substring(init.length)
log.head(function (err, data) {
if(err) return cb(err)
if(data) _id = data.value
if(_id === id)
run(id)
else
log.append(id, function (err) {
if(err) return cb(err)
run(id)
})
})
function run (id) {
if(running) {
//reload, and then the current version will trigger.
cb()
location.reload()
}
else
store.get(id, function (err, data) {
if(err) return cb(err)
var script = document.createElement('script')
running = true
document.body.innerHTML = ''
script.textContent = u.toUtf8(data)
document.head.appendChild(script) //run javascript.
cb()
})
}
},
size: function (cb) {
store.ls(function (err, ls) {
if(err) cb(err)
else cb(null, ls.reduce(function (total, item) {
return total + item.size
}, 0))
})
},
//clear target amount of space.
prune: function (target, cb) {
if(!target) return cb(new Error('WebBoot.prune: size to clear must be provided'))
var cleared = 0, remove = []
function clear () {
var n = remove.length
while(remove.length) store.rm(remove.shift(), function () {
if(--n) return
if(cleared < target)
cb(new Error('could not clear requested space'), cleared)
else
cb(null, cleared)
})
}
store.ls(function (err, ls) {
if(err) return cb(err)
log.unfiltered(function (err, unfiltered) {
if(err) return cb(err)
var stored = unfiltered.reverse()
ls.forEach(function (a) {
if(!unfiltered.find(function (b) {
return a.id == b.id
})) {
cleared += a.size
remove.push(a.id)
}
})
for(var i = 0; i < stored.length; i++) {
var id = stored[i].value
var item = ls.find(function (e) {
return e.id === id
})
if(item) {
cleared += item.size
remove.push(id)
if(cleared >= target) return clear()
}
}
clear()
})
})
},
version: require('./package.json').version,
remove: store.rm,
has: store.has,
versions: function (cb) {
log.filtered(function (err, ls) {
if(err) return cb(err)
else if(ls.length) cb(null, ls)
else {
var versions = u.parse(localStorage[appname+'_versions'])
if(!versions) return cb(null, [])
var n = Object.keys(versions).length
for(var ts in versions) {
log.append(versions[ts], function () {
if(--n) return
//try again
log.filtered(cb)
})
}
}
})
},
history: log.unfiltered,
current: log.head,
append: log.append,
revert: log.revert,
onprogress: null
}
}