README.md 13.4 KB
Newer Older
Sebastian Pekarek's avatar
Sebastian Pekarek committed
1
2
# ical-generator

Sebastian Pekarek's avatar
Sebastian Pekarek committed
3
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE)
Sebastian Pekarek's avatar
Sebastian Pekarek committed
4
5
[![CI Status](https://img.shields.io/travis/sebbo2002/ical-generator.svg?style=flat-square)](https://travis-ci.org/sebbo2002/ical-generator)
[![Test Coverage](https://sebbo.helium.uberspace.de/teamcity-badges/ICalGenerator_UnitTests/coverage-istanbul)](https://ci.sebbo.net/project.html?projectId=ICalGenerator&tab=preport_project1_Test_Coverage&guest=1)
Sebastian Pekarek's avatar
Sebastian Pekarek committed
6

Sebastian Pekarek's avatar
Sebastian Pekarek committed
7
8
ical-generator is a small piece of code which generates ical calendar files. I use this to generate subscriptionable
calendar feeds.
Sebastian Pekarek's avatar
Sebastian Pekarek committed
9

10

Sebastian Pekarek's avatar
Sebastian Pekarek committed
11
12
13
14
## Installation

	npm install ical-generator

15

Sebastian Pekarek's avatar
Sebastian Pekarek committed
16
17
18
19
20
21
22
23
24
25
## Upgrade from 0.1.x

ical-generator 0.2.0 introduces a completely new API, but because you guys used 0.1.x a lot, the old API still works. So
you should be able to upgrade from ical-generator 0.1.x to 0.2.0 without any code changes. In case you need the old API
docs, you can find the deprecated documentation [here](https://github.com/sebbo2002/ical-generator/blob/0.1.10/README.md).

In case you have any issues with the new API, feel free to [create an issue](https://github.com/sebbo2002/ical-generator/issues/new).


## Quick Start
Sebastian Pekarek's avatar
Sebastian Pekarek committed
26
27
28
29

```javascript
var ical = require('ical-generator'),
	http = require('http'),
Sebastian Pekarek's avatar
Sebastian Pekarek committed
30
	cal = ical({domain: 'github.com', name: 'my first iCal'});
Sebastian Pekarek's avatar
Sebastian Pekarek committed
31

Sebastian Pekarek's avatar
Sebastian Pekarek committed
32
33
// overwrite domain
cal.domain('sebbo.net');
34

Sebastian Pekarek's avatar
Sebastian Pekarek committed
35
cal.createEvent({
Sebastian Pekarek's avatar
Sebastian Pekarek committed
36
37
38
39
	start: new Date(),
	end: new Date(new Date().getTime() + 3600000),
	summary: 'Example Event',
	description: 'It works ;)',
Sebastian Pekarek's avatar
Sebastian Pekarek committed
40
	location: 'my room',
Sebastian Pekarek's avatar
Sebastian Pekarek committed
41
42
43
44
45
	url: 'http://sebbo.net/'
});

http.createServer(function(req, res) {
	cal.serve(res);
Sebastian Pekarek's avatar
Sebastian Pekarek committed
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
}).listen(3000, '127.0.0.1', function() {
    console.log('Server running at http://127.0.0.1:3000/');
});
```


## Just another example

```javascript
var ical = require('ical-generator'),

    // Create new Calendar and set optional fields
    cal = ical({
        domain: 'sebbo.net',
        prodId: {company: 'superman-industries.com', product: 'ical-generator'},
        name: 'My Testfeed',
        timezone: 'Europe/Berlin'
    });

// You can also set values like this…
cal.domain('sebbo.net');

// … or get values
cal.domain(); // --> "sebbo.net"

// create a new event
var event = cal.createEvent({
    start: new Date(),
    end: new Date(new Date().getTime() + 3600000),
    timestamp: new Date(),
    summary: 'My Event',
    organizer: 'Sebastian Pekarek <mail@example.com>'
});

// like above, you can also set/change values like this…
event.summary('My Super Mega Awesome Event');

// get the iCal string
cal.toString(); // --> "BEGIN:VCALENDAR…"


// You can also create events directly with ical()
cal = ical({
    domain: 'sebbo.net',
    prodId: '//superman-industries.com//ical-generator//EN',
    events: [
        {
            start: new Date(),
            end: new Date(new Date().getTime() + 3600000),
            timestamp: new Date(),
            summary: 'My Event',
            organizer: 'Sebastian Pekarek <mail@example.com>'
        }
    ]
}).toString();
101
102
103
104
105
106
```



## API

Sebastian Pekarek's avatar
Sebastian Pekarek committed
107
### ical-generator
108

Sebastian Pekarek's avatar
Sebastian Pekarek committed
109
#### ical([_Object_ options])
110

Sebastian Pekarek's avatar
Sebastian Pekarek committed
111
Creates a new [Calendar](#calendar) ([`ICalCalendar`](#calendar)).
112

Sebastian Pekarek's avatar
Sebastian Pekarek committed
113
114
115
116
117
118
119
120
121
122
```javascript
var ical = require('ical-generator'),
    cal = ical();
```

You can pass options to setup your calendar or use setters to do this.

```javascript
var ical = require('ical-generator'),
    cal = ical({domain: 'sebbo.net'});
Sebastian Pekarek's avatar
Sebastian Pekarek committed
123

Sebastian Pekarek's avatar
Sebastian Pekarek committed
124
// is the same as
Sebastian Pekarek's avatar
Sebastian Pekarek committed
125

Sebastian Pekarek's avatar
Sebastian Pekarek committed
126
cal = ical().domain('sebbo.net');
Sebastian Pekarek's avatar
Sebastian Pekarek committed
127

Sebastian Pekarek's avatar
Sebastian Pekarek committed
128
// is the same as
lee's avatar
lee committed
129

Sebastian Pekarek's avatar
Sebastian Pekarek committed
130
131
132
133
134
135
136
cal = ical();
cal.domain('sebbo.net');
```


### Calendar

Sebastian Pekarek's avatar
Sebastian Pekarek committed
137
#### domain([_String_ domain])
lee's avatar
lee committed
138

Sebastian Pekarek's avatar
Sebastian Pekarek committed
139
140
Use this method to set your server's hostname. It will be used to generate the feed's UID. Default hostname is your
server's one (`require('os').hostname()`).
lee's avatar
lee committed
141

142

Sebastian Pekarek's avatar
Sebastian Pekarek committed
143
#### prodId([_String_|_Object_ prodId])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
144
145
146

Use this method to overwrite the default Product Identifier (`//sebbo.net//ical-generator//EN`). `prodId` can be ether
a valid Product Identifier or an object:
147
148

```javascript
Sebastian Pekarek's avatar
Sebastian Pekarek committed
149
cal.prodId({
150
151
	company: 'My Company',
	product: 'My Product',
Sebastian Pekarek's avatar
Sebastian Pekarek committed
152
	language: 'EN' // optional, defaults to EN
153
});
Sebastian Pekarek's avatar
Sebastian Pekarek committed
154
155
156
157

// OR

cal.prodId('//My Company//My Product//EN');
158
159
160
```


Sebastian Pekarek's avatar
Sebastian Pekarek committed
161
#### name([_String_ name])
162

Sebastian's avatar
Sebastian committed
163
Use this method to set your feed's name. Is used to fill `NAME` and `X-WR-CALNAME` in your iCal file.
164
165


Sebastian's avatar
Sebastian committed
166
167
168
169
170
171
172
173
174
#### url([_String_ url])

Use this method to set your feed's URL.

```javascript
var cal = ical().url('https://example.com/calendar.ical');
```


Sebastian Pekarek's avatar
Sebastian Pekarek committed
175
#### timezone([_String_ timezone])
176

Sebastian's avatar
Sebastian committed
177
Use this method to set your feed's timezone. Is used to fill `TIMEZONE-ID` and `X-WR-TIMEZONE` in your iCal.
178
179

```javascript
Sebastian Pekarek's avatar
Sebastian Pekarek committed
180
181
182
183
var cal = ical().timezone('Europe/Berlin');
```


Sebastian's avatar
Sebastian committed
184
185
186
187
188
189
190
191
192
#### ttl([_Number_ ttl])

Use this method to set your feed's time to live. Is used to fill `REFRESH-INTERVAL` and `X-PUBLISHED-TTL` in your iCal.

```javascript
var cal = ical().ttl(60 * 60 * 24);
```


Sebastian Pekarek's avatar
Sebastian Pekarek committed
193
#### createEvent([_Object_ options])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
194
195
196
197
198
199
200
201
202
203
204

Creates a new [Event](#event) ([`ICalEvent`](#event)) and returns it. Use options to prefill the event's attributes.
Calling this method without options will create an empty event.

```javascript
var ical = require('ical-generator'),
    cal = ical(),
    event = cal.createEvent({summary: 'My Event'});

// overwrite event summary
event.summary('Your Event');
205
206
```

Sebastian Pekarek's avatar
Sebastian Pekarek committed
207

Sebastian Pekarek's avatar
Sebastian Pekarek committed
208
#### events([_Object_ events])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227

Add Events to calendar or return all attached events.

```javascript
var cal = ical();
cal.events([
    {
        start: new Date(),
        end: new Date(new Date().getTime() + 3600000),
        summary: 'Example Event',
        description: 'It works ;)',
        url: 'http://sebbo.net/'
    }
]);

cal.events(); // --> [ICalEvent]
```


Sebastian Pekarek's avatar
Sebastian Pekarek committed
228
#### save(**_String_ file**[, _Function_ cb])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
229
230
231
232

Save Calendar to disk asynchronously using [fs.writeFile](http://nodejs.org/api/fs.html#fs_fs_writefile_filename_data_options_callback).


Sebastian Pekarek's avatar
Sebastian Pekarek committed
233
#### saveSync(**_String_ file**)
Sebastian Pekarek's avatar
Sebastian Pekarek committed
234
235
236
237

Save Calendar to disk synchronously using [fs.writeFileSync](http://nodejs.org/api/fs.html#fs_fs_writefilesync_filename_data_options).


Sebastian Pekarek's avatar
Sebastian Pekarek committed
238
#### serve(**_http.ServerResponse_ response**)
Sebastian Pekarek's avatar
Sebastian Pekarek committed
239
240
241
242
243
244
245
246
247

Send Calendar to the User when using HTTP. See Quick Start above.


#### toString()

Return Calendar as a String.


Sebastian Pekarek's avatar
Sebastian Pekarek committed
248
249
250
251
252
253
254
255
#### toJSON()

Return a shallow copy of the calendar's options for JSON stringification. Can be used for persistance.
```javascript
var cal = ical(),
    json = JSON.stringify(cal);
```

Sebastian Pekarek's avatar
Sebastian Pekarek committed
256
257
258
259
260
261
262
263
264
265
266
267
268
#### length()

Returns the ammount of events in the calendar.


#### clear()

Empty the Calender.



### Event

Sebastian Pekarek's avatar
Sebastian Pekarek committed
269
#### uid([_String_|_Number_ uid]) or id([_String_|_Number_ id])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
270
271
272
273

Use this method to set the event's ID. If not set, an UID will be generated randomly.


Sebastian Pekarek's avatar
Sebastian Pekarek committed
274
#### start([_Date_ start])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
275
276
277
278

Appointment date of beginning as Date object. This is required for all events!


Sebastian Pekarek's avatar
Sebastian Pekarek committed
279
#### end([_Date_ end])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
280
281
282
283

Appointment date of end as Date object. This is also required for all events!


Sebastian Pekarek's avatar
Sebastian Pekarek committed
284
#### timestamp([_Date_ stamp]) or stamp([_Date_ stamp])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
285
286
287
288

Appointment date of creation as Date object. Default to `new Date()`.


Sebastian Pekarek's avatar
Sebastian Pekarek committed
289
#### allDay([_Boolean_ allDay])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
290
291
292
293

When allDay == true -> appointment is for the whole day


Sebastian Pekarek's avatar
Sebastian Pekarek committed
294
#### floating([_Boolean_ floating])
295
296
297
298
299
300
301
302
303
304
Appointment is a "floating" time. From [section 3.3.12 of RFC 554](https://tools.ietf.org/html/rfc5545#section-3.3.12):

> Time values of this type are said to be "floating" and are not
> bound to any time zone in particular.  They are used to represent
> the same hour, minute, and second value regardless of which time
> zone is currently being observed.  For example, an event can be
> defined that indicates that an individual will be busy from 11:00
> AM to 1:00 PM every day, no matter which time zone the person is
> in.  In these cases, a local time can be specified.

305

Sebastian Pekarek's avatar
Sebastian Pekarek committed
306
#### repeating([_Object_ repeating])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
307
308
309
Appointment is a repeating event

```javascript
Sebastian Pekarek's avatar
Sebastian Pekarek committed
310
event.repeating({
Sebastian Pekarek's avatar
Sebastian Pekarek committed
311
312
313
    freq: 'MONTHLY', // required
    count: 5,
    interval: 2,
314
315
316
317
    until: new Date('Jan 01 2014 00:00:00 UTC'),
    byDay: ['su', 'mo'], // repeat only sunday and monday
    byMonth: [1, 2], // repeat only in january und february,
    byMonthDay: [1, 15] // repeat only on the 1st and 15th
Sebastian Pekarek's avatar
Sebastian Pekarek committed
318
319
320
321
});
```


Sebastian Pekarek's avatar
Sebastian Pekarek committed
322
#### summary([_String_ summary])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
323

Sebastian Pekarek's avatar
Sebastian Pekarek committed
324
Appointment summary, defaults to empty string.
Sebastian Pekarek's avatar
Sebastian Pekarek committed
325
326


Sebastian Pekarek's avatar
Sebastian Pekarek committed
327
#### description([_String_ description])
328
329
330

Appointment description

Sebastian Pekarek's avatar
Sebastian Pekarek committed
331

Sebastian Pekarek's avatar
Sebastian Pekarek committed
332
#### location([_String_ location])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
333

Sebastian Pekarek's avatar
Sebastian Pekarek committed
334
335
Appointment location

Sebastian Pekarek's avatar
Sebastian Pekarek committed
336

Sebastian Pekarek's avatar
Sebastian Pekarek committed
337
#### organizer([_String_|Object organizer])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
338

339
340
341
Appointment organizer

```javascript
Sebastian Pekarek's avatar
Sebastian Pekarek committed
342
343
344
cal.organizer({
    name: 'Organizer\'s Name',
    email: 'organizer@example.com'
345
346
});

Sebastian Pekarek's avatar
Sebastian Pekarek committed
347
// OR
Lisa Övermyr's avatar
Lisa Övermyr committed
348

Sebastian Pekarek's avatar
Sebastian Pekarek committed
349
350
cal.organizer('Organizer\'s Name <organizer@example.com>');
```
Lisa Övermyr's avatar
Lisa Övermyr committed
351

352

Sebastian Pekarek's avatar
Sebastian Pekarek committed
353
#### createAttendee([_Object_ options])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371

Creates a new [Attendee](#attendee) ([`ICalAttendee`](#attendee)) and returns it. Use options to prefill the attendee's attributes.
Calling this method without options will create an empty attendee.

```javascript
var ical = require('ical-generator'),
    cal = ical(),
    event = cal.createEvent(),
    attendee = event.createAttendee({email: 'hui@example.com', 'name': 'Hui'});

// overwrite attendee's email address
attendee.email('hui@example.net');

// add another attendee
event.createAttendee('Buh <buh@example.net>');
```


Sebastian Pekarek's avatar
Sebastian Pekarek committed
372
#### attendees([_Object_ attendees])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
373
374
375
376
377
378
379
380
381
382
383
384
385
386

Add Attendees to the event or return all attached attendees.

```javascript
var event = ical().createEvent();
cal.attendees([
    {email: 'a@example.com', name: 'Person A'},
    {email: 'b@example.com', name: 'Person B'}
]);

cal.attendees(); // --> [ICalAttendee, ICalAttendee]
```


Sebastian Pekarek's avatar
Sebastian Pekarek committed
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
#### createAlarm([_Object_ options])

Creates a new [Alarm](#alarm) ([`ICalAlarm`](#alarm)) and returns it. Use options to prefill the alarm's attributes.
Calling this method without options will create an empty alarm.

```javascript
var ical = require('ical-generator'),
    cal = ical(),
    event = cal.createEvent(),
    alarm = event.createAlarm({type: 'display', trigger: 300});

// add another alarm
event.createAlarm({
    type: 'audio',
    trigger: 300, // 5min before event
});
```


#### alarms([_Object_ alarms])

Add alarms to the event or return all attached alarms.

```javascript
var event = ical().createEvent();
cal.alarms([
    {type: 'display', trigger: 600},
    {type: 'audio', trigger: 300}
]);

cal.attendees(); // --> [ICalAlarm, ICalAlarm]
```


Sebastian Pekarek's avatar
Sebastian Pekarek committed
421
#### url([_String_ url])
422

Sebastian Pekarek's avatar
Sebastian Pekarek committed
423
Appointment URL
424
425


Sebastian Pekarek's avatar
Sebastian Pekarek committed
426
#### method([_String_ method])
427

Sebastian Pekarek's avatar
Sebastian Pekarek committed
428
Appointment method. May be any of the following: `publish`, `request`, `reply`, `add`, `cancel`, `refresh`, `counter`, `declinecounter`.
429
430


Sebastian Pekarek's avatar
Sebastian Pekarek committed
431
#### status([_String_ status])
432

Sebastian Pekarek's avatar
Sebastian Pekarek committed
433
Appointment status. May be any of the following: `confirmed`, `tenative`, `cancelled`.
Sebastian Pekarek's avatar
Sebastian Pekarek committed
434

Sebastian Pekarek's avatar
Sebastian Pekarek committed
435
436


Sebastian Pekarek's avatar
Sebastian Pekarek committed
437
438
### Attendee

Sebastian Pekarek's avatar
Sebastian Pekarek committed
439
#### name([_String_ name])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
440
441
442
443

Use this method to set the attendee's name.


Sebastian Pekarek's avatar
Sebastian Pekarek committed
444
#### email([_String_ email])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
445
446
447
448

The attendee's email address. An email address is required for every attendee!


Sebastian Pekarek's avatar
Sebastian Pekarek committed
449
#### role([_String_ role])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
450

Sebastian Pekarek's avatar
Sebastian Pekarek committed
451
Set the attendee's role, defaults to `REQ-PARTICIPANT`. May be one of the following: `req-participant`, `non-participant`
Sebastian Pekarek's avatar
Sebastian Pekarek committed
452
453


Sebastian Pekarek's avatar
Sebastian Pekarek committed
454
#### status([_String_ status])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
455

Sebastian Pekarek's avatar
Sebastian Pekarek committed
456
Set the attendee's status. May be one of the following: `accepted`, `tentative`, `declined`
Sebastian Pekarek's avatar
Sebastian Pekarek committed
457
458


459
460
#### type([_String_ type])

Sebastian Pekarek's avatar
Sebastian Pekarek committed
461
Set the attendee's type. May be one of the following: `individual`, `group`, `resource`, `room`, `unknown` (See [Section 4.2.3](https://tools.ietf.org/html/rfc2445#section-4.2.3))
462
463
464



Sebastian Pekarek's avatar
Sebastian Pekarek committed
465
#### delegatesTo(**_ICalAttendee_|_Object_ attendee**)
Sebastian Pekarek's avatar
Sebastian Pekarek committed
466
467
468
469
470
471
472
473
474
475
476
477

Creates a new Attendee if passed object is not already an attendee. Will set the delegatedTo and delegatedFrom attributes.

```javascript
var cal = ical(),
    event = cal.createEvent(),
    attendee = cal.createAttendee();

attendee.delegatesTo({email: 'foo@bar.com', name: 'Foo'});
```


Sebastian Pekarek's avatar
Sebastian Pekarek committed
478
#### delegatesFrom(**_ICalAttendee_|_Object_ attendee**)
Sebastian Pekarek's avatar
Sebastian Pekarek committed
479
480
481
482
483
484
485
486
487
488
489
490
491

Creates a new Attendee if passed object is not already an attendee. Will set the delegatedTo and delegatedFrom attributes.

```javascript
var cal = ical(),
    event = cal.createEvent(),
    attendee = cal.createAttendee();

attendee.delegatesFrom({email: 'foo@bar.com', name: 'Foo'});
```



Sebastian Pekarek's avatar
Sebastian Pekarek committed
492
493
494
495
496
497
498
### Alarm

#### type([_String_ type])

Use this method to set the alarm type. Right now, `audio` and `display` is supported.


Sebastian Pekarek's avatar
Sebastian Pekarek committed
499
#### trigger([_Number_|_Date_ trigger]) / triggerBefore([_Number_|_Date_ trigger])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
500
501
502
503
504
505
506
507
508
509
510
511
512

Use this method to set the alarm time.

```javascript
var cal = ical(),
    event = cal.createEvent(),
    alarm = cal.createAlarm();

alarm.trigger(600); // -> 10 minutes before event starts
alarm.trigger(new Date()); // -> now
```


Sebastian Pekarek's avatar
Sebastian Pekarek committed
513
#### triggerAfter([_Number_|_Date_ trigger])
Sebastian Pekarek's avatar
Sebastian Pekarek committed
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588

Use this method to set the alarm time.

```javascript
var cal = ical(),
    event = cal.createEvent(),
    alarm = cal.createAlarm();

alarm.trigger(600); // -> 10 minutes after the event finishes
alarm.trigger(new Date()); // -> now
```


#### repeat([_Number_ repeat])

Use this method to repeat the alarm.

```javascript
var cal = ical(),
    event = cal.createEvent(),

// repeat the alarm 4 times every 5 minutes…
cal.createAlarm({
    repeat: 4,
    interval: 300
});
```


#### interval([_Number_ interval])

Use this method to set the alarm's interval.

```javascript
var cal = ical(),
    event = cal.createEvent(),

// repeat the alarm 4 times every 5 minutes…
cal.createAlarm({
    repeat: 4,
    interval: 300
});
```


#### attach([_String_|_Object_ attach])

Alarm attachment; used to set the alarm sound if type = audio. Defaults to "Basso".

```javascript
var cal = ical(),
    event = cal.createEvent(),

event.createAlarm({
    attach: 'https://example.com/notification.aud'
});

// OR

event.createAlarm({
    attach: {
        uri: 'https://example.com/notification.aud',
        mime: 'audio/basic'
    }
});
```


#### description([_String_| description])

Alarm description; used to set the alarm message if type = display. Defaults to the event's summary.




Sebastian Pekarek's avatar
Sebastian Pekarek committed
589
590
## Tests

Sebastian Pekarek's avatar
Sebastian Pekarek committed
591
```
Sebastian Pekarek's avatar
Sebastian Pekarek committed
592
npm test
593
594
595
596
597
```


## Copyright and license

Sebastian Pekarek's avatar
Sebastian Pekarek committed
598
Copyright (c) Sebastian Pekarek under the [MIT license](LICENSE).