forked from headzoo/surf
/
assets.go
233 lines (190 loc) · 5.29 KB
/
assets.go
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package browser
import (
"io"
"net/http"
"net/url"
)
// AssetType describes a type of page asset, such as an image or stylesheet.
type AssetType uint16
const (
// LinkAsset describes a *Link asset.
LinkAsset AssetType = iota
// ImageAsset describes an *Image asset.
ImageAsset
// StylesheetAsset describes a *Stylesheet asset.
StylesheetAsset
// ScriptAsset describes a *Script asset.
ScriptAsset
)
// AsyncDownloadResult has the results of an asynchronous download.
type AsyncDownloadResult struct {
// Asset is a pointer to the Downloadable asset that was downloaded.
Asset Downloadable
// Writer where the asset data was written.
Writer io.Writer
// Size is the number of bytes written to the io.Writer.
Size int64
// Error contains any error that occurred during the download or nil.
Error error
}
// AsyncDownloadChannel is a channel upon which the results of an async download
// are passed.
type AsyncDownloadChannel chan *AsyncDownloadResult
// Assetable represents a page asset, such as an image or stylesheet.
type Assetable interface {
// Url returns the asset URL.
Url() *url.URL
// Id returns the asset ID or an empty string when not available.
Id() string
// Type describes the type of asset.
AssetType() AssetType
}
// Asset implements Assetable.
type Asset struct {
// ID is the value of the id attribute if available.
ID string
// URL is the asset URL.
URL *url.URL
// Type describes the type of asset.
Type AssetType
}
// Url returns the asset URL.
func (at *Asset) Url() *url.URL {
return at.URL
}
// Id returns the asset ID or an empty string when not available.
func (at *Asset) Id() string {
return at.ID
}
// Type returns the asset type.
func (at *Asset) AssetType() AssetType {
return at.Type
}
// Downloadable represents an asset that may be downloaded.
type Downloadable interface {
Assetable
// Download writes the contents of the element to the given writer.
//
// Returns the number of bytes written.
Download(out io.Writer) (int64, error)
// DownloadAsync downloads the contents of the element asynchronously.
//
// An instance of AsyncDownloadResult will be sent down the given channel
// when the download is complete.
DownloadAsync(out io.Writer, ch AsyncDownloadChannel)
}
// DownloadableAsset is an asset that may be downloaded.
type DownloadableAsset struct {
Asset
}
// Download writes the asset to the given io.Writer type.
func (at *DownloadableAsset) Download(out io.Writer) (int64, error) {
return DownloadAsset(at, out)
}
// DownloadAsync downloads the asset asynchronously.
func (at *DownloadableAsset) DownloadAsync(out io.Writer, ch AsyncDownloadChannel) {
DownloadAssetAsync(at, out, ch)
}
// Link stores the properties of a page link.
type Link struct {
Asset
// Text is the text appearing between the opening and closing anchor tag.
Text string
}
// NewLinkAsset creates and returns a new *Link type.
func NewLinkAsset(u *url.URL, id, text string) *Link {
return &Link{
Asset: Asset{
URL: u,
ID: id,
Type: LinkAsset,
},
Text: text,
}
}
// Image stores the properties of an image.
type Image struct {
DownloadableAsset
// Alt is the value of the image alt attribute if available.
Alt string
// Title is the value of the image title attribute if available.
Title string
}
// NewImageAsset creates and returns a new *Image type.
func NewImageAsset(url *url.URL, id, alt, title string) *Image {
return &Image{
DownloadableAsset: DownloadableAsset{
Asset: Asset{
URL: url,
ID: id,
Type: ImageAsset,
},
},
Alt: alt,
Title: title,
}
}
// Stylesheet stores the properties of a linked stylesheet.
type Stylesheet struct {
DownloadableAsset
// Media is the value of the media attribute. Defaults to "all" when not specified.
Media string
// Type is the value of the type attribute. Defaults to "text/css" when not specified.
Type string
}
// NewStylesheetAsset creates and returns a new *Stylesheet type.
func NewStylesheetAsset(url *url.URL, id, media, typ string) *Stylesheet {
return &Stylesheet{
DownloadableAsset: DownloadableAsset{
Asset: Asset{
URL: url,
Type: StylesheetAsset,
ID: id,
},
},
Media: media,
Type: typ,
}
}
// Script stores the properties of a linked script.
type Script struct {
DownloadableAsset
// Type is the value of the type attribute. Defaults to "text/javascript" when not specified.
Type string
}
// NewScriptAsset creates and returns a new *Script type.
func NewScriptAsset(url *url.URL, id, typ string) *Script {
return &Script{
DownloadableAsset: DownloadableAsset{
Asset: Asset{
URL: url,
Type: ScriptAsset,
ID: id,
},
},
Type: typ,
}
}
// DownloadAsset copies a remote file to the given writer.
func DownloadAsset(asset Downloadable, out io.Writer) (int64, error) {
resp, err := http.Get(asset.Url().String())
if err != nil {
return 0, err
}
defer resp.Body.Close()
return io.Copy(out, resp.Body)
}
// DownloadAssetAsync downloads an asset asynchronously and notifies the given channel
// when the download is complete.
func DownloadAssetAsync(asset Downloadable, out io.Writer, c AsyncDownloadChannel) {
go func() {
results := &AsyncDownloadResult{Asset: asset, Writer: out}
size, err := DownloadAsset(asset, out)
if err != nil {
results.Error = err
} else {
results.Size = size
}
c <- results
}()
}