1'use strict' 2 3const { 4 staticPropertyDescriptors, 5 readOperation, 6 fireAProgressEvent 7} = require('./util') 8const { 9 kState, 10 kError, 11 kResult, 12 kEvents, 13 kAborted 14} = require('./symbols') 15const { webidl } = require('../fetch/webidl') 16const { kEnumerableProperty } = require('../core/util') 17 18class FileReader extends EventTarget { 19 constructor () { 20 super() 21 22 this[kState] = 'empty' 23 this[kResult] = null 24 this[kError] = null 25 this[kEvents] = { 26 loadend: null, 27 error: null, 28 abort: null, 29 load: null, 30 progress: null, 31 loadstart: null 32 } 33 } 34 35 /** 36 * @see https://w3c.github.io/FileAPI/#dfn-readAsArrayBuffer 37 * @param {import('buffer').Blob} blob 38 */ 39 readAsArrayBuffer (blob) { 40 webidl.brandCheck(this, FileReader) 41 42 webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsArrayBuffer' }) 43 44 blob = webidl.converters.Blob(blob, { strict: false }) 45 46 // The readAsArrayBuffer(blob) method, when invoked, 47 // must initiate a read operation for blob with ArrayBuffer. 48 readOperation(this, blob, 'ArrayBuffer') 49 } 50 51 /** 52 * @see https://w3c.github.io/FileAPI/#readAsBinaryString 53 * @param {import('buffer').Blob} blob 54 */ 55 readAsBinaryString (blob) { 56 webidl.brandCheck(this, FileReader) 57 58 webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsBinaryString' }) 59 60 blob = webidl.converters.Blob(blob, { strict: false }) 61 62 // The readAsBinaryString(blob) method, when invoked, 63 // must initiate a read operation for blob with BinaryString. 64 readOperation(this, blob, 'BinaryString') 65 } 66 67 /** 68 * @see https://w3c.github.io/FileAPI/#readAsDataText 69 * @param {import('buffer').Blob} blob 70 * @param {string?} encoding 71 */ 72 readAsText (blob, encoding = undefined) { 73 webidl.brandCheck(this, FileReader) 74 75 webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsText' }) 76 77 blob = webidl.converters.Blob(blob, { strict: false }) 78 79 if (encoding !== undefined) { 80 encoding = webidl.converters.DOMString(encoding) 81 } 82 83 // The readAsText(blob, encoding) method, when invoked, 84 // must initiate a read operation for blob with Text and encoding. 85 readOperation(this, blob, 'Text', encoding) 86 } 87 88 /** 89 * @see https://w3c.github.io/FileAPI/#dfn-readAsDataURL 90 * @param {import('buffer').Blob} blob 91 */ 92 readAsDataURL (blob) { 93 webidl.brandCheck(this, FileReader) 94 95 webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsDataURL' }) 96 97 blob = webidl.converters.Blob(blob, { strict: false }) 98 99 // The readAsDataURL(blob) method, when invoked, must 100 // initiate a read operation for blob with DataURL. 101 readOperation(this, blob, 'DataURL') 102 } 103 104 /** 105 * @see https://w3c.github.io/FileAPI/#dfn-abort 106 */ 107 abort () { 108 // 1. If this's state is "empty" or if this's state is 109 // "done" set this's result to null and terminate 110 // this algorithm. 111 if (this[kState] === 'empty' || this[kState] === 'done') { 112 this[kResult] = null 113 return 114 } 115 116 // 2. If this's state is "loading" set this's state to 117 // "done" and set this's result to null. 118 if (this[kState] === 'loading') { 119 this[kState] = 'done' 120 this[kResult] = null 121 } 122 123 // 3. If there are any tasks from this on the file reading 124 // task source in an affiliated task queue, then remove 125 // those tasks from that task queue. 126 this[kAborted] = true 127 128 // 4. Terminate the algorithm for the read method being processed. 129 // TODO 130 131 // 5. Fire a progress event called abort at this. 132 fireAProgressEvent('abort', this) 133 134 // 6. If this's state is not "loading", fire a progress 135 // event called loadend at this. 136 if (this[kState] !== 'loading') { 137 fireAProgressEvent('loadend', this) 138 } 139 } 140 141 /** 142 * @see https://w3c.github.io/FileAPI/#dom-filereader-readystate 143 */ 144 get readyState () { 145 webidl.brandCheck(this, FileReader) 146 147 switch (this[kState]) { 148 case 'empty': return this.EMPTY 149 case 'loading': return this.LOADING 150 case 'done': return this.DONE 151 } 152 } 153 154 /** 155 * @see https://w3c.github.io/FileAPI/#dom-filereader-result 156 */ 157 get result () { 158 webidl.brandCheck(this, FileReader) 159 160 // The result attribute’s getter, when invoked, must return 161 // this's result. 162 return this[kResult] 163 } 164 165 /** 166 * @see https://w3c.github.io/FileAPI/#dom-filereader-error 167 */ 168 get error () { 169 webidl.brandCheck(this, FileReader) 170 171 // The error attribute’s getter, when invoked, must return 172 // this's error. 173 return this[kError] 174 } 175 176 get onloadend () { 177 webidl.brandCheck(this, FileReader) 178 179 return this[kEvents].loadend 180 } 181 182 set onloadend (fn) { 183 webidl.brandCheck(this, FileReader) 184 185 if (this[kEvents].loadend) { 186 this.removeEventListener('loadend', this[kEvents].loadend) 187 } 188 189 if (typeof fn === 'function') { 190 this[kEvents].loadend = fn 191 this.addEventListener('loadend', fn) 192 } else { 193 this[kEvents].loadend = null 194 } 195 } 196 197 get onerror () { 198 webidl.brandCheck(this, FileReader) 199 200 return this[kEvents].error 201 } 202 203 set onerror (fn) { 204 webidl.brandCheck(this, FileReader) 205 206 if (this[kEvents].error) { 207 this.removeEventListener('error', this[kEvents].error) 208 } 209 210 if (typeof fn === 'function') { 211 this[kEvents].error = fn 212 this.addEventListener('error', fn) 213 } else { 214 this[kEvents].error = null 215 } 216 } 217 218 get onloadstart () { 219 webidl.brandCheck(this, FileReader) 220 221 return this[kEvents].loadstart 222 } 223 224 set onloadstart (fn) { 225 webidl.brandCheck(this, FileReader) 226 227 if (this[kEvents].loadstart) { 228 this.removeEventListener('loadstart', this[kEvents].loadstart) 229 } 230 231 if (typeof fn === 'function') { 232 this[kEvents].loadstart = fn 233 this.addEventListener('loadstart', fn) 234 } else { 235 this[kEvents].loadstart = null 236 } 237 } 238 239 get onprogress () { 240 webidl.brandCheck(this, FileReader) 241 242 return this[kEvents].progress 243 } 244 245 set onprogress (fn) { 246 webidl.brandCheck(this, FileReader) 247 248 if (this[kEvents].progress) { 249 this.removeEventListener('progress', this[kEvents].progress) 250 } 251 252 if (typeof fn === 'function') { 253 this[kEvents].progress = fn 254 this.addEventListener('progress', fn) 255 } else { 256 this[kEvents].progress = null 257 } 258 } 259 260 get onload () { 261 webidl.brandCheck(this, FileReader) 262 263 return this[kEvents].load 264 } 265 266 set onload (fn) { 267 webidl.brandCheck(this, FileReader) 268 269 if (this[kEvents].load) { 270 this.removeEventListener('load', this[kEvents].load) 271 } 272 273 if (typeof fn === 'function') { 274 this[kEvents].load = fn 275 this.addEventListener('load', fn) 276 } else { 277 this[kEvents].load = null 278 } 279 } 280 281 get onabort () { 282 webidl.brandCheck(this, FileReader) 283 284 return this[kEvents].abort 285 } 286 287 set onabort (fn) { 288 webidl.brandCheck(this, FileReader) 289 290 if (this[kEvents].abort) { 291 this.removeEventListener('abort', this[kEvents].abort) 292 } 293 294 if (typeof fn === 'function') { 295 this[kEvents].abort = fn 296 this.addEventListener('abort', fn) 297 } else { 298 this[kEvents].abort = null 299 } 300 } 301} 302 303// https://w3c.github.io/FileAPI/#dom-filereader-empty 304FileReader.EMPTY = FileReader.prototype.EMPTY = 0 305// https://w3c.github.io/FileAPI/#dom-filereader-loading 306FileReader.LOADING = FileReader.prototype.LOADING = 1 307// https://w3c.github.io/FileAPI/#dom-filereader-done 308FileReader.DONE = FileReader.prototype.DONE = 2 309 310Object.defineProperties(FileReader.prototype, { 311 EMPTY: staticPropertyDescriptors, 312 LOADING: staticPropertyDescriptors, 313 DONE: staticPropertyDescriptors, 314 readAsArrayBuffer: kEnumerableProperty, 315 readAsBinaryString: kEnumerableProperty, 316 readAsText: kEnumerableProperty, 317 readAsDataURL: kEnumerableProperty, 318 abort: kEnumerableProperty, 319 readyState: kEnumerableProperty, 320 result: kEnumerableProperty, 321 error: kEnumerableProperty, 322 onloadstart: kEnumerableProperty, 323 onprogress: kEnumerableProperty, 324 onload: kEnumerableProperty, 325 onabort: kEnumerableProperty, 326 onerror: kEnumerableProperty, 327 onloadend: kEnumerableProperty, 328 [Symbol.toStringTag]: { 329 value: 'FileReader', 330 writable: false, 331 enumerable: false, 332 configurable: true 333 } 334}) 335 336Object.defineProperties(FileReader, { 337 EMPTY: staticPropertyDescriptors, 338 LOADING: staticPropertyDescriptors, 339 DONE: staticPropertyDescriptors 340}) 341 342module.exports = { 343 FileReader 344} 345