redditor 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,724 +0,0 @@
1
- # FileAPI — a set of tools for working with files.
2
-
3
-
4
- <p align="center">
5
- ~~~ <a href="http://mailru.github.com/FileAPI/">DEMO</a>
6
- ~~~ <a href="http://mailru.github.com/FileAPI/example.userpic.html">user pic</a>
7
- ~~~
8
- </p>
9
-
10
-
11
- ## Support
12
- * Multiupload: all browsers that support HTML5 or [Flash](#flash-settings)
13
- * Drag'n'Drop upload: files (HTML5) & directories (Chrome 21+)
14
- * [Chunked](#chunked) file upload (HTML5)
15
- * Upload one file: all browsers
16
- * Working with [Images](#images): IE6+, FF 3.6+, Chrome 10+, Opera 11.1+, Safari 5.4+
17
- + crop, resize, preview & rotate (HTML5 or Flash)
18
- + auto orientation by exif (HTML5, if include FileAPI.exif.js or Flash)
19
-
20
-
21
-
22
-
23
- ## Example
24
- ```html
25
- <span class="js-fileapi-wrapper" style="position: relative;">
26
- <input id="user-files" type="file" multiple />
27
- </span>
28
-
29
- <div id="preview-list">
30
- </div>
31
- ```
32
- ```js
33
- var input = document.getElementById('user-files');
34
- var previewNode = document.getElementById('preview-list');
35
-
36
- // Drag'n'Drop
37
- FileAPI.event.dnd(previewNode, function (over){
38
- $(this).css('background', over ? 'red' : '');
39
- }, function (files){
40
- // ..
41
- });
42
-
43
-
44
-
45
- FileAPI.event.on(input, 'change', function (evt){
46
- var files = FileAPI.getFiles(evt.target); // or FileAPI.getFiles(evt)
47
-
48
- // filtering
49
- FileAPI.filterFiles(files, function (file, info){
50
- if( /image/.test(file.type) && info ){
51
- return info.width >= 320 && info.height >= 240;
52
- }
53
- else {
54
- return file.size > 128;
55
- }
56
- }, function (fileList, ignor){
57
- if( ignor.length ){
58
- // ...
59
- }
60
-
61
- if( !fileList.length ){
62
- // empty file list
63
- return;
64
- }
65
-
66
-
67
- // do preview
68
- var imageList = FileAPI.filter(fileList, function (file){ return /image/.test(file.type); });
69
- FileAPI.each(imageList, function (imageFile){
70
- FileAPI.Image(imageFile)
71
- .preview(100, 120)
72
- .get(function (err, image){
73
- if( err ){
74
- // ...
75
- }
76
- else {
77
- previewNode.appendChild(image);
78
- }
79
- })
80
- ;
81
- });
82
-
83
-
84
- // upload on server
85
- var xhr = FileAPI.upload({
86
- url: '...',
87
- data: { foo: 'bar' }, // POST-data (iframe, flash, html5)
88
- headers: { 'x-header': '...' }, // request headers (html5)
89
- files: {
90
- files: FileAPI.filter(fileList, function (file){ return !/image/.test(file.type); }),
91
- pictures: imageList
92
- },
93
- imageTransform: {
94
- maxWidth: 1024,
95
- maxHeight: 768
96
- },
97
- imageAutoOrientation: true,
98
- fileprogress: function (evt){ // (flash, html5)
99
- var percent = evt.loaded/evt.total*100;
100
- // ...
101
- },
102
- progress: function (evt){ // (flash, html5)
103
- var percent = evt.loaded/evt.total*100;
104
- // ...
105
- },
106
- complete: function (err, xhr){
107
- // ...
108
- }
109
- });
110
- });
111
- });
112
- ```
113
-
114
- ### HTML structure (templates)
115
- * [Default](#html-default)
116
- * [Button](#html-button)
117
- * [Link](#html-link)
118
-
119
-
120
- ### API
121
- * FileAPI.[getFiles](#getFiles)(`source:HTMLInput|Event`)`:Array`
122
- * FileAPI.[getDropFiles](#getDropFiles)(`files:Array`, `callback:Function`)
123
- * FileAPI.[filterFiles](#filterFiles)(`files:Array`, `iterator:Function`, `complete:Function`)
124
- * FileAPI.[upload](#upload)(`options:Object`)`:XMLHttpRequest`
125
- * FileAPI.[getInfo](#getInfo)(`file:File`, `callback:Function`)
126
- * FileAPI.[readAsImage](#readAs)(`file:File`, `callback:function`)
127
- * FileAPI.[readAsDataURL](#readAs)(`file:File`, `callback:function`)
128
- * FileAPI.[readAsBinaryString](#readAs)(`file:File`, `callback:function`)
129
- * FileAPI.[readAsArrayBuffer](#readAs)(`file:File`, `callback:function`)
130
- * FileAPI.[readAsText](#readAs)(`file:File`, `callback:function`)
131
- * FileAPI.[readAsText](#readAs)(`file:File`, `encoding:String`, `callback:function`)
132
-
133
-
134
- ### Events
135
- * FileAPI.event.on(`el:HTMLElement`, `eventType:String`, `fn:Function`)
136
- * FileAPI.event.off(`el:HTMLElement`, `eventType:String`, `fn:Function`)
137
- * FileAPI.event.one(`el:HTMLElement`, `eventType:String`, `fn:Function`)
138
- * FileAPI.event.dnd(`el:HTMLElement`, `onHover:Function`, `onDrop:Function`)
139
- * jQuery('#el').dnd(onHover, onDrop)
140
-
141
-
142
- <a name="images"></a>
143
- ### FileAPI.Image
144
- * .crop(width[, height])
145
- * .crop(x, y, width[, height])
146
- * .resize(width[, height])
147
- * .resize(width, height, `type:Enum(min,max,preview)`)
148
- * .preview(width[, height])
149
- * .rotate(deg)
150
- * .get(`fn:Function`)
151
-
152
-
153
-
154
- ### Utils
155
- * FileAPI.KB
156
- * FileAPI.MB
157
- * FileAPI.GB
158
- * FileAPI.TB
159
- * FileAPI.support.`html5:Boolean`
160
- * FileAPI.support.`cors:Boolean`
161
- * FileAPI.support.`dnd:Boolean`
162
- * FileAPI.support.`flash:Boolean`
163
- * FileAPI.support.`canvas:Boolean`
164
- * FileAPI.support.`dataURI:Boolean`
165
- * FileAPI.support.`chunked:Boolean`
166
- * FileAPI.each(`obj:Object|Array`, `fn:function`, `context:Mixed`)
167
- * FileAPI.extend(`dst:Object`, `src:Object`)`:Object`
168
- * FileAPI.filter(`list:Array`, `iterator:Function`)`:Array`
169
- * FileAPI.isFile(`file:Mixed`)`:Boolean`
170
- * FileAPI.toBinaryString(`str:Base64`)`:String`
171
-
172
-
173
-
174
- ---------------------------------------
175
-
176
-
177
- <a name="getFiles"></a>
178
- ### FileAPI.getFiles
179
- ```js
180
- FileAPI.event.on('#my-file-1', 'change', onSelect);
181
-
182
- // or jQuery
183
- $('#my-file-2').on('change', onSelect);
184
-
185
- function onSelect(evt/**Event*/){
186
- // (1) extract fileList from event
187
- var files = FileAPI.getFiles(evt);
188
-
189
- // (2) or so
190
- var files = FileAPI.getFiles(evt.target);
191
- }
192
- ```
193
-
194
-
195
- <a name="getDropFiles"></a>
196
- ### FileAPI.getDropFiles
197
- ```js
198
- function onDrop(evt){
199
- FileAPI.getDropFiles(evt, function (files){
200
- if( files.length ){
201
- // ...
202
- }
203
- });
204
- }
205
-
206
- // OR
207
-
208
- var el = document.getElementById('el');
209
- FileAPI.event.dnd(el, function (over/**Boolean*/, evt/**Event*/){
210
- el.style.background = over ? 'red' : '';
211
- }, function (files/**Array*/, evt/**Event*/){
212
- // ...
213
- });
214
- ```
215
-
216
-
217
- <a name="getInfo"></a>
218
- ### FileAPI.getInfo
219
- ```js
220
- FileAPI.getInfo(imageFile/**File*/, function (err/**Boolean*/, info/**Object*/){
221
- if( !err ){
222
- switch( info.exif.Orientation ){
223
- // ...
224
- }
225
- }
226
- });
227
-
228
- // ...
229
-
230
- FileAPI.addInfoReader(/^image/, function (file/**File*/, callback/**Function*/){
231
- // http://www.nihilogic.dk/labs/exif/exif.js
232
- // http://www.nihilogic.dk/labs/binaryajax/binaryajax.js
233
- FileAPI.readAsBinaryString(file, function (evt){
234
- if( evt.type == 'load' ){
235
- var binaryString = evt.result;
236
- var oFile = new BinaryFile(binaryString, 0, file.size);
237
- var exif = EXIF.readFromBinaryFile(oFile);
238
- callback(false, { 'exif': exif || {} });
239
- }
240
- else if( evt.type == 'error' ){
241
- callback('read_as_binary_string');
242
- }
243
- else if( evt.type == 'progress' ){
244
- // ...
245
- }
246
- });
247
- });
248
- ```
249
-
250
-
251
- <a name="filterFiles"></a>
252
- ### FileAPI.filterFiles
253
- ```js
254
- FileAPI.filterFiles(files, function (file, info){
255
- if( /image/.test(file.type) && info ){
256
- return info.width > 320 && info.height > 240;
257
- }
258
- return file.size < 10 * FileAPI.MB;
259
- }, function (result, ignor){
260
- // ...
261
- });
262
- ```
263
-
264
-
265
- <a name="readAs"></a>
266
- ### FileAPI.readAsImage, FileAPI.readAsDataURL (FileAPI.readAsBinaryString)
267
- ```js
268
- FileAPI.readAsImage(file, function (evt){
269
- if( evt.type == 'load' ){
270
- var images = document.getElementById('images');
271
- images.appendChild(evt.result);
272
- }
273
- else {
274
- // ...
275
- }
276
- });
277
-
278
-
279
- FileAPI.readAsDataURL(file, function (evt){
280
- if( evt.type == 'load' ){
281
- // success
282
- var result = evt.result;
283
- }
284
- else if( evt.type == 'progress' ){
285
- var pr = evt.loaded/evt.total * 100;
286
- }
287
- else {
288
- // error
289
- }
290
- });
291
- ```
292
-
293
-
294
- <a name="upload"></a>
295
- ### FileAPI.upload
296
- ```js
297
- var xhr = FileAPI.upload({
298
- url: '...',
299
- data: { foo: 'bar' },
300
- headers: { 'x-header': '...' },
301
- files: {
302
- images: FileAPI.filter(files, function (file){ return /image/.test(file.type); }),
303
- customFile: { file: 'generate.txt', blob: customFileBlob }
304
- },
305
-
306
- chunkSize: 0, // or chunk size in bytes, eg: FileAPI.MB*.5 (html5)
307
- chunkUploadRetry: 0, // number of retries during upload chunks (html5)
308
-
309
- imageTransform: {
310
- maxWidth: 1024,
311
- maxHeight: 768
312
- },
313
- imageAutoOrientation: true,
314
- prepare: function (file, options){
315
- // prepare options for current file
316
- options.data.filename = file.name;
317
- },
318
- upload: function (xhr, options){
319
- // start uploading
320
- },
321
- fileupload: function (xhr, options){
322
- // start file uploading
323
- },
324
- fileprogress: function (evt){
325
- // progress file uploading
326
- var filePercent = evt.loaded/evt.total*100;
327
- },
328
- filecomplete: function (err, xhr){
329
- if( !err ){
330
- var response = xhr.responseText;
331
- }
332
- },
333
- progress: function (evt){
334
- // total progress uploading
335
- var totalPercent = evt.loaded/evt.total*100;
336
- },
337
- complete: function (err, xhr){
338
- if( !err ){
339
- // Congratulations, the uploading was successful!
340
- }
341
- }
342
- });
343
- ```
344
-
345
-
346
- <a href="imageTransform"></a>
347
- ### imageTransform
348
- * width`:Number`
349
- * height`:Number`
350
- * preview`:Boolean`
351
- * maxWidth`:Number`
352
- * maxHeight`:Number`
353
- * rotate`:Number`
354
- ```js
355
- FileAPI.upload({
356
- // ..
357
- imageOriginal: false, // don't send original on server
358
-
359
- imageTransform: {
360
- // (1) Resize to 120x200
361
- resize: { width: 120, height: 200 }
362
-
363
- // (2) create preview 320x240
364
- thumb: { width: 320, height: 240, preview: true }
365
-
366
- // (3) Resize by max side
367
- max: { maxWidth: 800, maxHeight: 600 }
368
-
369
- // (4) Custom resize
370
- custom: function (info, transform){
371
- return transform
372
- .crop(100, 100, 300, 200)
373
- .resize(100, 50)
374
- ;
375
- }
376
- }
377
- });
378
- ```
379
-
380
-
381
- <a name="imageAutoOrientation"></a>
382
- ### imageAutoOrientation
383
- ```js
384
- // (1) all images
385
- FileAPI.upload({
386
- // ..
387
- imageAutoOrientation: true
388
- });
389
-
390
- // (2) or so
391
- FileAPI.upload({
392
- // ..
393
- imageAutoOrientation: true,
394
- imageTransform: { width: .., height: .. }
395
- });
396
-
397
- // (3) or so
398
- FileAPI.upload({
399
- // ..
400
- imageTransform: { rotate: 'auto' }
401
- });
402
-
403
- // (4) only "800x600", original not modified
404
- FileAPI.upload({
405
- // ..
406
- imageTransform: {
407
- "800x600": { width: 800, height: 600, rotate: 'auto' }
408
- }
409
- });
410
- ```
411
-
412
-
413
- -----
414
-
415
-
416
- <a name="flash-settings"></a>
417
- ### Flash settings
418
- ```html
419
- <script>
420
- var FileAPI = {
421
- // @required
422
- staticPath: '/js/' // @default: "./"
423
-
424
- // @optional
425
- , flashUrl: '/js/FileAPI.flash.swf' // @default: FileAPI.staticPath + "FileAPI.flash.swf"
426
- , flashImageUrl: '/js/FileAPI.flash.image.swf' // @default: FileAPI.staticPath + "FileAPI.flash.image.swf"
427
- };
428
- </script>
429
- <script src="/js/FileAPI.min.js"></script>
430
- ```
431
-
432
- #### Flash-request ([FileReference](http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/FileReference.html))
433
- The following sample HTTP POST request is sent from Flash Player to a server-side script if no parameters are specified:
434
- ```
435
- POST /handler.cfm HTTP/1.1
436
- Accept: text/*
437
- Content-Type: multipart/form-data;
438
- boundary=----------Ij5ae0ae0KM7GI3KM7
439
- User-Agent: Shockwave Flash
440
- Host: www.example.com
441
- Content-Length: 421
442
- Connection: Keep-Alive
443
- Cache-Control: no-cache
444
-
445
- ------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7
446
- Content-Disposition: form-data; name="Filename"
447
-
448
- MyFile.jpg
449
- ------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7
450
- Content-Disposition: form-data; name="Filedata"; filename="MyFile.jpg"
451
- Content-Type: application/octet-stream
452
-
453
- FileDataHere
454
- ------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7
455
- Content-Disposition: form-data; name="Upload"
456
-
457
- Submit Query
458
- ------------Ij5GI3GI3ei4GI3ei4KM7GI3KM7KM7--
459
- ```
460
-
461
-
462
-
463
- -----
464
-
465
-
466
-
467
- <a name="chunked"></a>
468
- ### Chunked file upload (html5)
469
- ```js
470
- FileAPI.upload({
471
- url: '...'
472
- , files: fileList
473
- , chunkSize: .5 * FileAPI.MB // 512KB
474
- , chunkUploadRetry: 1
475
- , complete: function (err, xhr){}
476
- });
477
- ```
478
-
479
- Client and server communicate to each other using the following HTTP headers and status codes.
480
-
481
- Client explicitly sets the following headers:
482
- * [Content-Range](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16): bytes \<start-offset\>-\<end-offset\>/\<total\>
483
- * [Content-Disposition](http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1): attachment; filename=\<file-name\>
484
-
485
- Any other headers are set by a target browser and are not used by client.
486
- Library does not provide any facilities to track a file uniqueness across requests, it's left on developer's consideration.
487
-
488
- Client recognizes the following response codes:
489
- * 200, 201 - chunk is successfully saved
490
- * 416, 500 - recoverable error, library tries to resend chunk 'chunkUploadRetry' times then fails
491
-
492
- All the other codes - fatal error, user's involvement is recommend.
493
-
494
-
495
- -----
496
-
497
-
498
-
499
- ### File object (https://developer.mozilla.org/en/DOM/File)
500
- ```js
501
- {
502
- name: 'fileName',
503
- type: 'mime-type',
504
- size: 'fileSize'
505
- }
506
- ```
507
-
508
-
509
- ### XMLHttpRequest
510
- ```js
511
- {
512
- status: Number,
513
- statusText: Number,
514
- readyState: Number,
515
- response: Blob,
516
- responseXML: XML,
517
- responseText: String,
518
- responseBody: String,
519
- getResponseHeader: function (name/**String*/)/**String*/{},
520
- getAllResponseHeaders: function ()/**Object*/{},
521
- abort: function (){}
522
- }
523
- ```
524
-
525
-
526
- ### Cross-Domain upload-controller headers
527
- ```php
528
- <?
529
- header('Access-Control-Allow-Methods: POST, OPTIONS');
530
- header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Range, Content-Disposition, Content-Type'); // and other custom headers
531
- header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']); // a comma-separated list of domains
532
-
533
- if( $_SERVER['REQUEST_METHOD'] == 'OPTIONS' ){
534
- exit;
535
- }
536
-
537
- if( $_SERVER['REQUEST_METHOD'] == 'POST' ){
538
- // ...
539
- }
540
- ?>
541
- ```
542
-
543
-
544
- ### iframe
545
- #### POST-query
546
- ```
547
- /controller.php
548
- ?foo=bar
549
- &images=...
550
- &callback=...
551
- ```
552
-
553
-
554
- #### POST-response
555
- ```php
556
- <script type="text/javascript">
557
- (function (ctx, jsonp){
558
- if( ctx && ctx[jsonp] ){
559
- ctx[jsonp](<?=$statusCode/*200 — OK*/?>, "<?=addslashes($statusText)?>", "<?=addslashes($response)?>");
560
- }
561
- })(this.parent, "<?=htmlspecialchars($_POST['callback'])?>");
562
- </script>
563
- ```
564
-
565
-
566
-
567
- ---
568
-
569
-
570
-
571
- ### HTML structure (templates)
572
-
573
- <a name="html-default"></a>
574
- ### Default
575
- ```html
576
- <span class="js-fileapi-wrapper" style="position: relative; display: inline-block;">
577
- <input name="files" type="file" multiple />
578
- </span>
579
- ```
580
-
581
-
582
- <a name="html-button"></a>
583
- ### Button
584
- ```html
585
- <style>
586
- .upload-btn {
587
- width: 130px;
588
- height: 25px;
589
- overflow: hidden;
590
- position: relative;
591
- border: 3px solid #06c;
592
- border-radius: 5px;
593
- background: #0cf;
594
-
595
- }
596
- .upload-btn:hover {
597
- background: #09f;
598
- }
599
- .upload-btn__txt {
600
- z-index: 1;
601
- position: relative;
602
- color: #fff;
603
- font-size: 18px;
604
- font-family: "Helvetica Neue";
605
- line-height: 24px;
606
- text-align: center;
607
- text-shadow: 0 1px 1px #000;
608
- }
609
- .upload-btn__inp {
610
- top: -10px;
611
- right: -40px;
612
- z-index: 2;
613
- position: absolute;
614
- cursor: pointer;
615
- opacity: 0;
616
- filter: alpha(opacity=0);
617
- font-size: 50px;
618
- }
619
- </style>
620
- <div class="upload-btn js-fileapi-wrapper">
621
- <div class="upload-btn__txt">Upload files</div>
622
- <input class="upload-btn__inp" name="files" type="file" multiple />
623
- </div>
624
- ```
625
-
626
-
627
- <a name="html-link"></a>
628
- ### Link
629
- ```html
630
- <style>
631
- .upload-link {
632
- color: #36c;
633
- display: inline-block;
634
- *zoom: 1;
635
- *display: inline;
636
- overflow: hidden;
637
- position: relative;
638
- padding-bottom: 2px;
639
- text-decoration: none;
640
- }
641
- .upload-link__txt {
642
- z-index: 1;
643
- position: relative;
644
- border-bottom: 1px dotted #36c;
645
- }
646
- .upload-link:hover .upload-link__txt {
647
- color: #f00;
648
- border-bottom-color: #f00;
649
- }
650
-
651
- .upload-link__inp {
652
- top: -10px;
653
- right: -40px;
654
- z-index: 2;
655
- position: absolute;
656
- cursor: pointer;
657
- opacity: 0;
658
- filter: alpha(opacity=0);
659
- font-size: 50px;
660
- }
661
- </style>
662
- <a class="upload-link js-fileapi-wrapper">
663
- <span class="upload-link__txt">Upload photo</span>
664
- <input class="upload-link__inp" name="photo" type="file" accept=".jpg,.jpeg,.gif" />
665
- </a>
666
- ```
667
-
668
-
669
- ---
670
-
671
-
672
- ## Changelog
673
- ### 1.2.5
674
- * [#86](https://github.com/mailru/FileAPI/issues/86): Smarter upload recovery
675
- * [#87](https://github.com/mailru/FileAPI/issues/87): Fixed upload files into browsers that do not support FormData
676
- * Fixed support "accept" attribute for Flash.
677
- * Fixed detection of HTML5 support for FireFox 3.6
678
- * + FileAPI.html5 option, default "true"
679
-
680
-
681
- ### 1.2.4
682
- * Fixed auto orientation image by EXIF (Flash)
683
- * Fixed image dimensions after rotate (Flash)
684
- * [#82](https://github.com/mailru/FileAPI/issues/82): "undefined" data-fields cause exceptions
685
- * [#83](https://github.com/mailru/FileAPI/issues/83): Allow requests without files
686
- * [#84](https://github.com/mailru/FileAPI/pull/84): Fixed connection abort when waiting for connection recovery
687
-
688
-
689
- ### 1.2.3
690
- * [#77](https://github.com/mailru/FileAPI/pull/77): Fixed flash.abort(), [#75](https://github.com/mailru/FileAPI/issues/75)
691
- * - `FileAPI.addMime`
692
- * + `FileAPI.accept` — fallback for flash.
693
-
694
-
695
- ### 1.2.2
696
- * [#67](https://github.com/mailru/FileAPI/pull/67): Added correct httpStatus for upload fail, [#62](https://github.com/mailru/FileAPI/pull/68)
697
- * [#68](https://github.com/mailru/FileAPI/pull/68) Added "Content-Type" for chunked upload, [#65](https://github.com/mailru/FileAPI/pull/65)
698
- * [#69](https://github.com/mailru/FileAPI/issues/69): Fixed network down recovery
699
- * Fixed progress event, [#66](https://github.com/mailru/FileAPI/issues/66)
700
- * Increase flash stage size, [#73](https://github.com/mailru/FileAPI/pull/73)
701
- * - array index from POST-param "name", [#72](https://github.com/mailru/FileAPI/issues/72)
702
- * - dependency on FileAPI.Image for FileAPI.Flash
703
-
704
-
705
- ### 1.2.1
706
- * [#64](https://github.com/mailru/FileAPI/issues/64): Bufixed for [#63](https://github.com/mailru/FileAPI/issues/63)
707
-
708
-
709
-
710
- ### 1.2.0
711
- * [#57](https://github.com/mailru/FileAPI/issues/57): Chunked file upload
712
-
713
-
714
- ### 1.1.0
715
- * [#54](https://github.com/mailru/FileAPI/issues/54): added `FileAPI.flashUrl` and `FileAPI.flashImageUrl`
716
-
717
-
718
- ### 1.0.1
719
- * [#51](https://github.com/mailru/FileAPI/issues/51): remove circular references from `file-objects` (Flash transport)
720
- * added `changelog`
721
-
722
-
723
- ### 1.0.0
724
- * first release