31 Commits

Author SHA1 Message Date
Travis Krause 8821207478 Create LICENSE 2017-09-14 12:26:28 -05:00
Travis Krause e07db639f1 fixed typo 2017-09-14 12:23:18 -05:00
Travis Krause 6677d0568b Fixed bad name 2017-09-14 11:48:28 -05:00
Travis Krause ce02e5d8a9 Adding additional stuff 2017-09-14 11:19:37 -05:00
Travis Krause 1425bdfad3 Merge branch 'master' of https://github.com/nwcell/ics.js 2017-09-14 11:05:22 -05:00
Travis Krause 1edd227f13 Merging bugfix 2017-09-14 11:05:04 -05:00
Travis Krause f8dbe2c5c5 Merge pull request #32 from GeoffreyPlitt/master
fixing "main" section of package.json
2017-09-14 11:04:16 -05:00
Travis Krause 82a31cb801 Merge pull request #29 from blaiseIT/patch-1
grammatical fix on JavaScript
2017-09-14 10:41:47 -05:00
Travis Krause 358d4f4f18 Merge pull request #12 from connorbode/master
create recurring events
2017-09-14 10:35:54 -05:00
Adam Brown fd2d0a81aa Output now passes ical validation
- A few required fields were missing for proper ical validation: PRODID,
UID, DTSTAMP
- Added optional params to configure prodid and uid
- Fixed bug: Start and End times were reporting DATE instead of
DATE-TIME, so some consumers would ignore event times
- Fixed bug: If start/end times only contained hours, no time would be
reported at all
- Added a new method: "build" which simply returns the ical contents
instead of trying to trigger a download
2016-07-31 02:00:23 -07:00
Geoffrey Plitt 40a2c1244c fixing "main" section of package.json 2016-06-06 13:01:35 -07:00
Connor Bode a12cab01e5 Merge pull request #5 from nhunt3/patch-1
Multi-line description bug
2016-05-09 09:29:34 -04:00
Nick Hunt 28cf7546ef Multi-line description bug
I have multi-line descriptions in the calendar I'm exporting from. When I try to import the file that your ics.js program generates into Outlook or Google Calendar, it only pulls in the first line in and it ignores the rest. My solution fixes this. I tested it in both Outlook and Google Calendar and it works and also follows generally accepted ics file formatting standards I believe. As far as I can tell, it doesn't seem to break anything.

Let me know what you think.
Thanks.
2016-05-05 18:05:38 -04:00
Blaise Petric ef20bfeea4 grammatical fix on JavaScript 2016-03-05 01:17:38 -05:00
Connor Bode 17a1b06fdd Merge pull request #2 from jmuia/master
BYDAY property for recurrence events
2015-10-05 20:33:57 -04:00
Connor Bode 387392d04b Merge pull request #1 from asr1/master
Fix minute/second discrepancy
2015-08-17 20:49:52 -04:00
Alex Rinehart e7c3489c2d Fix minute/second bug 2015-08-17 12:18:33 -05:00
Alex Rinehart b18b40fa03 Update README.md
Fixed the 30 minute event to be spelled correctly and actually be 30 minutes (and not 1470 minutes).
2015-06-02 23:56:43 -07:00
jmuia 592454900c Add DTSTAMP to events and remove VALUE=DATE from DTSTART and DTEND properties 2014-08-20 17:29:53 -04:00
jmuia 6502ca83df Add .DS_Store to .gitignore 2014-08-20 17:28:58 -04:00
jmuia 98b22dd60a add BYDAY property to recurrence events 2014-08-20 14:46:26 -04:00
Connor Bode 75369c0184 update readme for recurring events 2014-08-18 20:21:28 -04:00
Connor Bode 64eaacd0b9 add functionality for recurring events 2014-08-18 20:13:42 -04:00
Connor Bode c86d55ac39 failing spec for recurring events 2014-08-18 19:20:30 -04:00
Connor Bode 762d842513 fix tests 2014-08-18 18:53:34 -04:00
Connor Bode 4790ef4156 fix lint 2014-08-18 18:50:17 -04:00
Travis Krause d0359a5e06 Updated js link for mimetype workaround 2014-03-20 00:59:44 -07:00
Travis Krause 2a5ea5d165 Update readme
Documented the new time feature.
2014-03-11 23:05:54 -07:00
Travis Krause b479d03dff Updated versions... 2014-03-11 21:58:38 -08:00
Travis Krause 6369bb98f3 Added time 2014-03-11 21:56:36 -08:00
Travis efb5222403 Updated readme and demo 2014-03-11 14:23:28 -08:00
14 changed files with 504 additions and 204 deletions
+11
View File
@@ -0,0 +1,11 @@
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
# editorconfig-tools is unable to ignore longs strings or urls
max_line_length = null
+1
View File
@@ -1,2 +1,3 @@
node_modules/
bower_components/
.DS_Store
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Travis Krause
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+29 -4
View File
@@ -1,21 +1,39 @@
ics.js
============
A browser firendly .ics/.vcs file generator written entirely in javascript!!!!!!
A browser friendly .ics/.vcs file generator written entirely in JavaScript!
Now you can make calendar friendly files client-side. It outputs .ics files, so the files are compatible with all modern calendar software (Outlook, Apple Calendar, Google, etc.)
How To Use
----------
Simply use invoke the object and use the functions...
var cal = ics();
cal.addEvent(subject, description, location, begin, end);
cal.addEvent(subject, description, location, begin, end); // yes, you can have multiple events :-)
cal.download(filename);
`begin` and `end` need to be formatted in a way that is friendly to `Date()`
Recurring Events
----------------
Recurring events can be added with the `rrule` object.
`cal.addEvent(subject, description, location, begin, end, rrule)`
The `rrule` object has the following properties:
- `freq` : __Required.__ The frequency of event recurrence. Can be `DAILY`, `WEEKLY`, `MONTHLY`, or `YEARLY`.
- `until` : A date string representing the date on which to end repitition. Must be friendly to `Date()`
- `count` : Alternative to until. Repeat the event `count` times. Must be an integer
- `interval` : The interval of `freq` to recur at. For example, if `freq` is `WEEKLY` and `interval` is `2`, the event will repeat every 2 weeks. Must be an integer.
- `byday` : Which days of the week the event is to occur. An array containing any of `SU`, `MO`, `TU`, `WE`, `TH`, `FR`, `SA`.
The four properties described above are not exhaustive of recurrence rule capabilities. If extra functionality is required, you can set the `rrule.rule` property to a full recurrence rule string. In this case, none of the four properties described above are necessary. See [this page](http://www.kanzaki.com/docs/ical/rrule.html) for a description of recurrence rules.
Example
-------
* **[Demo](http://htmlpreview.github.io/?https://github.com/nwcell/ics.js/blob/master/demo/demo.html)**
@@ -23,7 +41,8 @@ Example
```
<script>
var cal = ics();
cal.addEvent('Demo Event', 'This is an awesome demo event', 'Nome, AK', '8/7/2013', '8/9/2013');
cal.addEvent('Demo Event', 'This is an all day event', 'Nome, AK', '8/7/2013', '8/7/2013');
cal.addEvent('Demo Event', 'This is thirty minute event', 'Nome, AK', '8/7/2013 5:30 pm', '8/7/2013 6:00 pm');
</script>
<a href="javascript:cal.download()">Demo</a>
```
@@ -37,7 +56,7 @@ The tool uses 2 libraries from the following projects:
I've compressed them and included them into the source for the normal file. Other variations are available in the repo.
If you want IE to allow for either opening documents as well as saving documents, you can use my fork of FileSaver.js (https://github.com/nwcell/FileSaver.js)
If you want IE to allow for either opening documents as well as saving documents, you can use my fork of FileSaver.js (https://github.com/nwcell/FileSaver.js)... Though you honestly are probebly best off using their main.
Supported Browsers
------------------
@@ -52,3 +71,9 @@ Supported Browsers
| Opera Next | [FileSaver.js](https://github.com/eligrey/FileSaver.js) |
| Opera < 15 | [FileSaver.js](https://github.com/eligrey/FileSaver.js), [Blob.js](https://github.com/eligrey/Blob.js) |
| Safari ≤ 6 | [FileSaver.js](https://github.com/eligrey/FileSaver.js), [Blob.js](https://github.com/eligrey/Blob.js) |
Credits
------------------
* [Travis Krause](https://github.com/nwcell): Me
* [Kyle Hornberg](https://github.com/khornberg): Added multi event functionality and made everything a package firendly
+2 -2
View File
@@ -1,8 +1,8 @@
{
"name": "ics.js",
"main": "ics.js",
"version": "0.1.1",
"homepage": "https://github.com/khornberg/ics.js",
"version": "0.1.3",
"homepage": "https://github.com/nwcell/ics.js",
"contributors": [
"Travis Krause <travis@codeforemen.com>",
"Kyle Hornberg <kyle.hornberg@gmail.com>"
+79 -46
View File
@@ -1,51 +1,84 @@
<!DOCTYPE html>
<head>
<script type="text/javascript" src="../ics.deps.min.js"></script>
<script type="text/javascript" src="demo.js"></script>
<style>
html, body {
height: 100%;
width: 100%;
}
.wrap {
margin: auto;
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
height: 50px;
width: 100px;
}
a {
margin-top: 5px;
margin-bottom: 5px;
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: normal;
line-height: 1.428571429;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid transparent;
border-radius: 4px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
cursor: pointer;
color: #ffffff;
text-decoration: none;
font-weight: bold;
background-color: #428bca;
border-color: #357ebd;
font-family: sans-serif;
}
</style>
<style>
html, body {
height: 100%;
width: 100%;
}
.wrap {
margin: auto;
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
height: 50px;
width: 100px;
}
a {
margin-top: 5px;
margin-bottom: 5px;
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: normal;
line-height: 1.428571429;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid transparent;
border-radius: 4px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
cursor: pointer;
color: #ffffff;
text-decoration: none;
font-weight: bold;
background-color: #428bca;
border-color: #357ebd;
font-family: sans-serif;
width: 120px;
}
</style>
</head>
<body>
<div class="wrap">
<a href="javascript:cal.download('christmas')">Download iCal</a>
Look at the console too!
</div>
<div class="wrap">
<a href="#" onclick="javascript:cal_single.download('Awesome Day')">Single Event</a>
<br>
<a href="#" onclick="javascript:cal.download('Holidays')">Multiple Events</a>
<br>
<a href="#" onclick="javascript:makelogs(cal)">Console Output</a>
</div>
<!-- File Saver Dependency -->
<script>/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs=saveAs||function(e){"use strict";if(typeof e==="undefined"||typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),o="download"in r,a=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},i=/constructor/i.test(e.HTMLElement)||e.safari,f=/CriOS\/[\d]+/.test(navigator.userAgent),u=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},s="application/octet-stream",d=1e3*40,c=function(e){var t=function(){if(typeof e==="string"){n().revokeObjectURL(e)}else{e.remove()}};setTimeout(t,d)},l=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var o=e["on"+t[r]];if(typeof o==="function"){try{o.call(e,n||e)}catch(a){u(a)}}}},p=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob([String.fromCharCode(65279),e],{type:e.type})}return e},v=function(t,u,d){if(!d){t=p(t)}var v=this,w=t.type,m=w===s,y,h=function(){l(v,"writestart progress write writeend".split(" "))},S=function(){if((f||m&&i)&&e.FileReader){var r=new FileReader;r.onloadend=function(){var t=f?r.result:r.result.replace(/^data:[^;]*;/,"data:attachment/file;");var n=e.open(t,"_blank");if(!n)e.location.href=t;t=undefined;v.readyState=v.DONE;h()};r.readAsDataURL(t);v.readyState=v.INIT;return}if(!y){y=n().createObjectURL(t)}if(m){e.location.href=y}else{var o=e.open(y,"_blank");if(!o){e.location.href=y}}v.readyState=v.DONE;h();c(y)};v.readyState=v.INIT;if(o){y=n().createObjectURL(t);setTimeout(function(){r.href=y;r.download=u;a(r);h();c(y);v.readyState=v.DONE});return}S()},w=v.prototype,m=function(e,t,n){return new v(e,t||e.name||"download",n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){t=t||e.name||"download";if(!n){e=p(e)}return navigator.msSaveOrOpenBlob(e,t)}}w.abort=function(){};w.readyState=w.INIT=0;w.WRITING=1;w.DONE=2;w.error=w.onwritestart=w.onprogress=w.onwrite=w.onabort=w.onerror=w.onwriteend=null;return m}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!==null){define("FileSaver.js",function(){return saveAs})}
</script>
<script src="../ics.js"></script>
<script type="text/javascript">
// Demo
var cal = ics();
cal.addEvent('Christmas', 'Christian holiday celebrating the birth of Jesus Christ', 'Bethlehem', '12/25/2013', '12/25/2013');
cal.addEvent('Christmas', 'Christian holiday celebrating the birth of Jesus Christ', 'Bethlehem', '12/25/2014', '12/25/2014');
cal.addEvent('New Years', 'Watch the ball drop!', 'New York', '01/01/2015', '01/01/2015');
cal.addEvent('New Years', 'Watch the ball drop!', 'New York', '01/01/2016', '01/01/2016');
var cal_single = ics();
cal_single.addEvent('Best Day', 'This is the best day to demonstrate a single event.', 'New York', '11/12/1987', '11/12/1987');
// You can use this for easy debugging
var makelogs = function(obj) {
console.log('Events Array');
console.log('=================');
console.log(obj.events());
console.log('Calendar With Header');
console.log('=================');
console.log(obj.calendar());
}
</script>
</html>
-6
View File
@@ -1,6 +0,0 @@
// Demo
var cal = ics();
cal.addEvent('Christmas', 'Christian holiday celebrating the birth of Jesus Christ', 'Bethlehem', '12/25/2013', '12/25/2013');
cal.addEvent('Christmas', 'Christian holiday celebrating the birth of Jesus Christ', 'Bethlehem', '12/25/2014', '12/25/2014');
console.log('Events\n' + cal.events());
console.log('Calendar\n' + cal.calendar());
+45
View File
@@ -0,0 +1,45 @@
html,
body {
height: 100%;
width: 100%;
}
.wrap {
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
height: 50px;
width: 100px;
}
a {
margin-top: 5px;
margin-bottom: 5px;
display: inline-block;
padding: 6px 12px;
margin-bottom: 0;
font-size: 14px;
font-weight: normal;
line-height: 1.428571429;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid transparent;
border-radius: 4px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
cursor: pointer;
color: #ffffff;
text-decoration: none;
font-weight: bold;
background-color: #428bca;
border-color: #357ebd;
font-family: sans-serif;
width: 120px;
}
+2 -2
View File
File diff suppressed because one or more lines are too long
+225 -82
View File
@@ -1,97 +1,240 @@
/* global saveAs, Blob, BlobBuilder, console */
/* exported ics */
var ics = function() {
'use strict';
var ics = function(uidDomain, prodId) {
'use strict';
if (navigator.userAgent.indexOf('MSIE') > -1 && navigator.userAgent.indexOf('MSIE 10') == -1) {
console.log('Unsupported Browser');
return;
}
if (navigator.userAgent.indexOf('MSIE') > -1 && navigator.userAgent.indexOf('MSIE 10') == -1) {
console.log('Unsupported Browser');
return;
}
var SEPARATOR = (navigator.appVersion.indexOf('Win') !== -1) ? '\r\n' : '\n';
var calendarEvents = [];
var calendarStart = [
'BEGIN:VCALENDAR',
'VERSION:2.0'
].join(SEPARATOR);
var calendarEnd = SEPARATOR + 'END:VCALENDAR';
if (typeof uidDomain === 'undefined') { uidDomain = 'default'; }
if (typeof prodId === 'undefined') { prodId = 'Calendar'; }
return {
/**
* Returns events array
* @return {array} Events
*/
'events' : function () {
return calendarEvents;
},
var SEPARATOR = (navigator.appVersion.indexOf('Win') !== -1) ? '\r\n' : '\n';
var calendarEvents = [];
var calendarStart = [
'BEGIN:VCALENDAR',
'PRODID:' + prodId,
'VERSION:2.0'
].join(SEPARATOR);
var calendarEnd = SEPARATOR + 'END:VCALENDAR';
var BYDAY_VALUES = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];
/**
* Returns calendar
* @return {string} Calendar in iCalendar format
*/
'calendar' : function () {
return calendarStart + SEPARATOR + calendarEvents.join(SEPARATOR) + calendarEnd;
},
return {
/**
* Returns events array
* @return {array} Events
*/
'events': function() {
return calendarEvents;
},
/**
* Add event to the calendar
* @param {string} subject Subject/Title of event
* @param {string} description Description of event
* @param {string} location Location of event
* @param {string} begin Beginning date of event
* @param {string} stop Ending date of event
*/
'addEvent' : function (subject, description, location, begin, stop) {
//TODO add time and time zone? use moment to format?
var start_date = new Date(begin);
var end_date = new Date(stop);
/**
* Returns calendar
* @return {string} Calendar in iCalendar format
*/
'calendar': function() {
return calendarStart + SEPARATOR + calendarEvents.join(SEPARATOR) + calendarEnd;
},
var start_year = ('0000' + (start_date.getFullYear().toString())).slice(-4);
var start_month = ('00' + ((start_date.getMonth() + 1).toString())).slice(-2);
var start_day = ('00' + (start_date.getDate().toString())).slice(-2);
var start = start_year + start_month + start_day;
/**
* Add event to the calendar
* @param {string} subject Subject/Title of event
* @param {string} description Description of event
* @param {string} location Location of event
* @param {string} begin Beginning date of event
* @param {string} stop Ending date of event
*/
'addEvent': function(subject, description, location, begin, stop, rrule) {
// I'm not in the mood to make these optional... So they are all required
if (typeof subject === 'undefined' ||
typeof description === 'undefined' ||
typeof location === 'undefined' ||
typeof begin === 'undefined' ||
typeof stop === 'undefined'
) {
return false;
}
var end_year = ('0000' + (end_date.getFullYear().toString())).slice(-4);
var end_month = ('00' + ((end_date.getMonth() + 1).toString())).slice(-2);
var end_day = ('00' + (end_date.getDate().toString())).slice(-2);
var end = end_year + end_month + end_day;
// validate rrule
if (rrule) {
if (!rrule.rrule) {
if (rrule.freq !== 'YEARLY' && rrule.freq !== 'MONTHLY' && rrule.freq !== 'WEEKLY' && rrule.freq !== 'DAILY') {
throw "Recurrence rrule frequency must be provided and be one of the following: 'YEARLY', 'MONTHLY', 'WEEKLY', or 'DAILY'";
}
var calendarEvent = [
'BEGIN:VEVENT',
'CLASS:PUBLIC',
'DESCRIPTION:'+description,
'DTSTART;VALUE=DATE:'+start,
'DTEND;VALUE=DATE:'+end,
'LOCATION:'+location,
'SUMMARY;LANGUAGE=en-us:'+subject,
'TRANSP:TRANSPARENT',
'END:VEVENT'
].join(SEPARATOR);
if (rrule.until) {
if (isNaN(Date.parse(rrule.until))) {
throw "Recurrence rrule 'until' must be a valid date string";
}
}
calendarEvents.push(calendarEvent);
},
if (rrule.interval) {
if (isNaN(parseInt(rrule.interval))) {
throw "Recurrence rrule 'interval' must be an integer";
}
}
/**
* Download calendar using the saveAs function from filesave.js
* @param {string} filename Filename
* @param {string} ext Extention
*/
'download' : function (filename, ext) {
ext = (typeof ext !== 'undefined') ? ext : '.ics';
filename = (typeof filename !== 'undefined') ? filename : 'calendar';
var calendar = calendarStart + SEPARATOR + calendarEvents.join(SEPARATOR) + calendarEnd;
if (rrule.count) {
if (isNaN(parseInt(rrule.count))) {
throw "Recurrence rrule 'count' must be an integer";
}
}
var blob;
if (navigator.userAgent.indexOf('MSIE 10') === -1) { // chrome or firefox
blob = new Blob([calendar]);
}
else { // ie
var bb = new BlobBuilder();
bb.append(calendar);
blob = bb.getBlob('text/x-vCalendar;charset=' + document.characterSet);
}
saveAs(blob, filename+ext);
}
};
if (typeof rrule.byday !== 'undefined') {
if ((Object.prototype.toString.call(rrule.byday) !== '[object Array]')) {
throw "Recurrence rrule 'byday' must be an array";
}
if (rrule.byday.length > 7) {
throw "Recurrence rrule 'byday' array must not be longer than the 7 days in a week";
}
// Filter any possible repeats
rrule.byday = rrule.byday.filter(function(elem, pos) {
return rrule.byday.indexOf(elem) == pos;
});
for (var d in rrule.byday) {
if (BYDAY_VALUES.indexOf(rrule.byday[d]) < 0) {
throw "Recurrence rrule 'byday' values must include only the following: 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'";
}
}
}
}
}
//TODO add time and time zone? use moment to format?
var start_date = new Date(begin);
var end_date = new Date(stop);
var now_date = new Date();
var start_year = ("0000" + (start_date.getFullYear().toString())).slice(-4);
var start_month = ("00" + ((start_date.getMonth() + 1).toString())).slice(-2);
var start_day = ("00" + ((start_date.getDate()).toString())).slice(-2);
var start_hours = ("00" + (start_date.getHours().toString())).slice(-2);
var start_minutes = ("00" + (start_date.getMinutes().toString())).slice(-2);
var start_seconds = ("00" + (start_date.getSeconds().toString())).slice(-2);
var end_year = ("0000" + (end_date.getFullYear().toString())).slice(-4);
var end_month = ("00" + ((end_date.getMonth() + 1).toString())).slice(-2);
var end_day = ("00" + ((end_date.getDate()).toString())).slice(-2);
var end_hours = ("00" + (end_date.getHours().toString())).slice(-2);
var end_minutes = ("00" + (end_date.getMinutes().toString())).slice(-2);
var end_seconds = ("00" + (end_date.getMinutes().toString())).slice(-2);
var now_year = ("0000" + (now_date.getFullYear().toString())).slice(-4);
var now_month = ("00" + ((now_date.getMonth() + 1).toString())).slice(-2);
var now_day = ("00" + ((now_date.getDate()).toString())).slice(-2);
var now_hours = ("00" + (now_date.getHours().toString())).slice(-2);
var now_minutes = ("00" + (now_date.getMinutes().toString())).slice(-2);
var now_seconds = ("00" + (now_date.getMinutes().toString())).slice(-2);
// Since some calendars don't add 0 second events, we need to remove time if there is none...
var start_time = '';
var end_time = '';
if (start_hours + start_minutes + start_seconds + end_hours + end_minutes + end_seconds != 0) {
start_time = 'T' + start_hours + start_minutes + start_seconds;
end_time = 'T' + end_hours + end_minutes + end_seconds;
}
var now_time = 'T' + now_hours + now_minutes + now_seconds;
var start = start_year + start_month + start_day + start_time;
var end = end_year + end_month + end_day + end_time;
var now = now_year + now_month + now_day + now_time;
// recurrence rrule vars
var rruleString;
if (rrule) {
if (rrule.rrule) {
rruleString = rrule.rrule;
} else {
rruleString = 'rrule:FREQ=' + rrule.freq;
if (rrule.until) {
var uDate = new Date(Date.parse(rrule.until)).toISOString();
rruleString += ';UNTIL=' + uDate.substring(0, uDate.length - 13).replace(/[-]/g, '') + '000000Z';
}
if (rrule.interval) {
rruleString += ';INTERVAL=' + rrule.interval;
}
if (rrule.count) {
rruleString += ';COUNT=' + rrule.count;
}
if (rrule.byday && rrule.byday.length > 0) {
rruleString += ';BYDAY=' + rrule.byday.join(',');
}
}
}
var stamp = new Date().toISOString();
var calendarEvent = [
'BEGIN:VEVENT',
'UID:' + calendarEvents.length + "@" + uidDomain,
'CLASS:PUBLIC',
'DESCRIPTION:' + description,
'DTSTAMP;VALUE=DATE-TIME:' + now,
'DTSTART;VALUE=DATE-TIME:' + start,
'DTEND;VALUE=DATE-TIME:' + end,
'LOCATION:' + location,
'SUMMARY;LANGUAGE=en-us:' + subject,
'TRANSP:TRANSPARENT',
'END:VEVENT'
];
if (rruleString) {
calendarEvent.splice(4, 0, rruleString);
}
calendarEvent = calendarEvent.join(SEPARATOR);
calendarEvents.push(calendarEvent);
return calendarEvent;
},
/**
* Download calendar using the saveAs function from filesave.js
* @param {string} filename Filename
* @param {string} ext Extention
*/
'download': function(filename, ext) {
if (calendarEvents.length < 1) {
return false;
}
ext = (typeof ext !== 'undefined') ? ext : '.ics';
filename = (typeof filename !== 'undefined') ? filename : 'calendar';
var calendar = calendarStart + SEPARATOR + calendarEvents.join(SEPARATOR) + calendarEnd;
var blob;
if (navigator.userAgent.indexOf('MSIE 10') === -1) { // chrome or firefox
blob = new Blob([calendar]);
} else { // ie
var bb = new BlobBuilder();
bb.append(calendar);
blob = bb.getBlob('text/x-vCalendar;charset=' + document.characterSet);
}
saveAs(blob, filename + ext);
return calendar;
},
/**
* Build and return the ical contents
*/
'build': function() {
if (calendarEvents.length < 1) {
return false;
}
var calendar = calendarStart + SEPARATOR + calendarEvents.join(SEPARATOR) + calendarEnd;
return calendar;
}
};
};
Vendored
+2 -2
View File
@@ -1,2 +1,2 @@
/*! ics.js Thu Dec 19 2013 14:54:06 */
var ics=function(){"use strict";if(navigator.userAgent.indexOf("MSIE")>-1&&-1==navigator.userAgent.indexOf("MSIE 10"))return console.log("Unsupported Browser"),void 0;var a=-1!==navigator.appVersion.indexOf("Win")?"\r\n":"\n",b=[],c=["BEGIN:VCALENDAR","VERSION:2.0"].join(a),d=a+"END:VCALENDAR";return{events:function(){return b},calendar:function(){return c+a+b.join(a)+d},addEvent:function(c,d,e,f,g){var h=new Date(f),i=new Date(g),j=("0000"+h.getFullYear().toString()).slice(-4),k=("00"+(h.getMonth()+1).toString()).slice(-2),l=("00"+h.getDate().toString()).slice(-2),m=j+k+l,n=("0000"+i.getFullYear().toString()).slice(-4),o=("00"+(i.getMonth()+1).toString()).slice(-2),p=("00"+i.getDate().toString()).slice(-2),q=n+o+p,r=["BEGIN:VEVENT","CLASS:PUBLIC","DESCRIPTION:"+d,"DTSTART;VALUE=DATE:"+m,"DTEND;VALUE=DATE:"+q,"LOCATION:"+e,"SUMMARY;LANGUAGE=en-us:"+c,"TRANSP:TRANSPARENT","END:VEVENT"].join(a);b.push(r)},download:function(e,f){f="undefined"!=typeof f?f:".ics",e="undefined"!=typeof e?e:"calendar";var g,h=c+a+b.join(a)+d;if(-1===navigator.userAgent.indexOf("MSIE 10"))g=new Blob([h]);else{var i=new BlobBuilder;i.append(h),g=i.getBlob("text/x-vCalendar;charset="+document.characterSet)}saveAs(g,e+f)}}};
/*! ics.js Wed Aug 20 2014 17:23:02 */
var ics=function(){"use strict";if(navigator.userAgent.indexOf("MSIE")>-1&&-1==navigator.userAgent.indexOf("MSIE 10"))return void console.log("Unsupported Browser");var a=-1!==navigator.appVersion.indexOf("Win")?"\r\n":"\n",b=[],c=["BEGIN:VCALENDAR","VERSION:2.0"].join(a),d=a+"END:VCALENDAR",e=["SU","MO","TU","WE","TH","FR","SA"];return{events:function(){return b},calendar:function(){return c+a+b.join(a)+d},addEvent:function(c,d,f,g,h,i){if("undefined"==typeof c||"undefined"==typeof d||"undefined"==typeof f||"undefined"==typeof g||"undefined"==typeof h)return!1;if(i&&!i.rule){if("YEARLY"!==i.freq&&"MONTHLY"!==i.freq&&"WEEKLY"!==i.freq&&"DAILY"!==i.freq)throw"Recurrence rule frequency must be provided and be one of the following: 'YEARLY', 'MONTHLY', 'WEEKLY', or 'DAILY'";if(i.until&&isNaN(Date.parse(i.until)))throw"Recurrence rule 'until' must be a valid date string";if(i.interval&&isNaN(parseInt(i.interval)))throw"Recurrence rule 'interval' must be an integer";if(i.count&&isNaN(parseInt(i.count)))throw"Recurrence rule 'count' must be an integer";if("undefined"!=typeof i.byday){if("[object Array]"!==Object.prototype.toString.call(i.byday))throw"Recurrence rule 'byday' must be an array";if(i.byday.length>7)throw"Recurrence rule 'byday' array must not be longer than the 7 days in a week";i.byday=i.byday.filter(function(a,b){return i.byday.indexOf(a)==b});for(var j in i.byday)if(e.indexOf(i.byday[j])<0)throw"Recurrence rule 'byday' values must include only the following: 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'"}}var k=new Date(g),l=new Date(h),m=("0000"+k.getFullYear().toString()).slice(-4),n=("00"+(k.getMonth()+1).toString()).slice(-2),o=("00"+k.getDate().toString()).slice(-2),p=("00"+k.getHours().toString()).slice(-2),q=("00"+k.getMinutes().toString()).slice(-2),r=("00"+k.getMinutes().toString()).slice(-2),s=("0000"+l.getFullYear().toString()).slice(-4),t=("00"+(l.getMonth()+1).toString()).slice(-2),u=("00"+l.getDate().toString()).slice(-2),v=("00"+l.getHours().toString()).slice(-2),w=("00"+l.getMinutes().toString()).slice(-2),x=("00"+l.getMinutes().toString()).slice(-2),y="",z="";q+r+w+x!==0&&(y="T"+p+q+r,z="T"+v+w+x);var A,B=m+n+o+y,C=s+t+u+z;if(i)if(i.rule)A=i.rule;else{if(A="RRULE:FREQ="+i.freq,i.until){var D=new Date(Date.parse(i.until)).toISOString();A+=";UNTIL="+D.substring(0,D.length-13).replace(/[-]/g,"")+"000000Z"}i.interval&&(A+=";INTERVAL="+i.interval),i.count&&(A+=";COUNT="+i.count),i.byday&&i.byday.length>0&&(A+=";BYDAY="+i.byday.join(","))}var E=(new Date).toISOString(),F=["BEGIN:VEVENT","CLASS:PUBLIC","DESCRIPTION:"+d,"DTSTART:"+B,"DTEND:"+C,"DTSTAMP:"+E.substring(0,E.length-13).replace(/[-]/g,"")+"000000Z","LOCATION:"+f,"SUMMARY;LANGUAGE=en-us:"+c,"TRANSP:TRANSPARENT","END:VEVENT"];return A&&F.splice(4,0,A),F=F.join(a),b.push(F),F},download:function(e,f){if(b.length<1)return!1;f="undefined"!=typeof f?f:".ics",e="undefined"!=typeof e?e:"calendar";var g,h=c+a+b.join(a)+d;if(-1===navigator.userAgent.indexOf("MSIE 10"))g=new Blob([h]);else{var i=new BlobBuilder;i.append(h),g=i.getBlob("text/x-vCalendar;charset="+document.characterSet)}return saveAs(g,e+f),h}}};
+7 -6
View File
@@ -1,15 +1,16 @@
{
"name": "ics.js",
"version": "0.1.1",
"version": "0.1.3",
"description": "A browser firendly .ics/.vcs file generator written entirely in javascript",
"main": "icsjs",
"main": "ics.js",
"dependencies": {
"bower": "~1.2.8",
"file-saver": "^1.3.3",
"grunt": "~0.4.2",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-jshint": "~0.7.2",
"grunt-contrib-uglify": "~0.2.7",
"grunt-mocha": "~0.4.6",
"bower": "~1.2.8"
"grunt-mocha": "~0.4.6"
},
"devDependencies": {},
"scripts": {
@@ -17,7 +18,7 @@
},
"repository": {
"type": "git",
"url": "https://github.com/khornberg/ics.js.git"
"url": "https://github.com/nwcell/ics.js.git"
},
"keywords": [
"ics",
@@ -30,6 +31,6 @@
],
"license": "MIT",
"bugs": {
"url": "https://github.com/khornberg/ics.js/issues"
"url": "https://github.com/nwcell/ics.js/issues"
}
}
+15 -15
View File
@@ -1,24 +1,24 @@
<!doctype html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Mocha Spec Runner</title>
<link rel="stylesheet" href="lib/mocha/mocha.css">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Mocha Spec Runner</title>
<link rel="stylesheet" href="lib/mocha/mocha.css">
</head>
<body>
<div id="mocha"></div>
<script src="lib/mocha/mocha.js"></script>
<script>mocha.setup('bdd')</script>
<!-- assertion framework -->
<script src="lib/chai.js"></script>
<script>var assert = chai.assert</script>
<div id="mocha"></div>
<script src="lib/mocha/mocha.js"></script>
<script>mocha.setup('bdd')</script>
<!-- assertion framework -->
<script src="lib/chai.js"></script>
<script>var assert = chai.assert</script>
<!-- include source files here... -->
<script src="../ics.js"></script>
<script>var cal = ics();</script>
<!-- include source files here... -->
<script src="../ics.js"></script>
<script>var cal = ics();</script>
<!-- include spec files here... -->
<script src="spec/test.js"></script>
<!-- include spec files here... -->
<script src="spec/test.js"></script>
</body>
</html>
+65 -39
View File
@@ -1,45 +1,71 @@
/*global describe, it, cal, assert */
(function () {
'use strict';
describe('Load ics Object', function () {
describe('Calendar Events Array', function () {
it('should be an empty array initially', function () {
assert.isArray(cal.events(), 'calendarEvents not an array');
assert.lengthOf(cal.events(), '0');
});
});
describe('Calendar String', function () {
it('should be a string with no events', function () {
assert.strictEqual(cal.calendar(), 'BEGIN:VCALENDAR\nVERSION:2.0\n\nEND:VCALENDAR', 'calendar does not match');
});
});
'use strict';
var stamp = new Date().toISOString();
stamp = stamp.substring(0, stamp.length - 13).replace(/[-]/g, '') + '000000Z';
describe('Load ics Object', function () {
describe('Calendar Events Array', function () {
it('should be an empty array initially', function () {
assert.isArray(cal.events(), 'calendarEvents not an array');
assert.lengthOf(cal.events(), '0');
});
});
describe('Add 1 Event', function () {
describe('Calendar Events Array', function () {
it('should have one event', function () {
cal.addEvent('Christmas', 'Christian holiday celebrating the birth of Jesus Christ', 'Bethlehem', '12/25/2013', '12/25/2013');
assert.isArray(cal.events(), 'calendarEvents not an array');
assert.lengthOf(cal.events(), '1');
assert.strictEqual(cal.events()[0], 'BEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:Christian holiday celebrating the birth of Jesus Christ\nDTSTART;VALUE=DATE:20131225\nDTEND;VALUE=DATE:20131225\nLOCATION:Bethlehem\nSUMMARY;LANGUAGE=en-us:Christmas\nTRANSP:TRANSPARENT\nEND:VEVENT');
});
});
describe('Calendar String', function () {
it('should have one event', function () {
assert.strictEqual(cal.calendar(), 'BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:Christian holiday celebrating the birth of Jesus Christ\nDTSTART;VALUE=DATE:20131225\nDTEND;VALUE=DATE:20131225\nLOCATION:Bethlehem\nSUMMARY;LANGUAGE=en-us:Christmas\nTRANSP:TRANSPARENT\nEND:VEVENT\nEND:VCALENDAR', 'calendar does not match');
});
});
describe('Calendar String of Single Digit Months', function () {
it('should have one event', function () {
cal.addEvent('Easter', 'Christian holiday celebrating the resurrection of Jesus Christ', 'Jerusalem', '04/20/2014', '04/20/2014');
assert.strictEqual(cal.events()[1], 'BEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:Christian holiday celebrating the resurrection of Jesus Christ\nDTSTART;VALUE=DATE:20140420\nDTEND;VALUE=DATE:20140420\nLOCATION:Jerusalem\nSUMMARY;LANGUAGE=en-us:Easter\nTRANSP:TRANSPARENT\nEND:VEVENT');
});
});
describe('Calendar String of Single Digit Day', function () {
it('should have one event', function () {
cal.addEvent('April Fools Day', 'What isn\'t is', 'America', '4/1/2014', '4/1/2014');
assert.strictEqual(cal.events()[2], 'BEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:What isn\'t is\nDTSTART;VALUE=DATE:20140401\nDTEND;VALUE=DATE:20140401\nLOCATION:America\nSUMMARY;LANGUAGE=en-us:April Fools Day\nTRANSP:TRANSPARENT\nEND:VEVENT');
});
});
describe('Calendar String', function () {
it('should be a string with no events', function () {
assert.strictEqual(cal.calendar(), 'BEGIN:VCALENDAR\nVERSION:2.0\n\nEND:VCALENDAR', 'calendar does not match');
});
});
});
describe('Add 1 Event', function () {
describe('Calendar Events Array', function () {
it('should have one event', function () {
cal.addEvent('Christmas', 'Christian holiday celebrating the birth of Jesus Christ', 'Bethlehem', '12/25/2013', '12/25/2013');
assert.isArray(cal.events(), 'calendarEvents not an array');
assert.lengthOf(cal.events(), '1');
assert.strictEqual(cal.events()[0], 'BEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:Christian holiday celebrating the birth of Jesus Christ\nDTSTART:20131225T000000\nDTEND:20131225T000000\nDTSTAMP:' + stamp + '\nLOCATION:Bethlehem\nSUMMARY;LANGUAGE=en-us:Christmas\nTRANSP:TRANSPARENT\nEND:VEVENT');
});
});
describe('Calendar String', function () {
it('should have one event', function () {
assert.strictEqual(cal.calendar(), 'BEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:Christian holiday celebrating the birth of Jesus Christ\nDTSTART:20131225T000000\nDTEND:20131225T000000\nDTSTAMP:' + stamp + '\nLOCATION:Bethlehem\nSUMMARY;LANGUAGE=en-us:Christmas\nTRANSP:TRANSPARENT\nEND:VEVENT\nEND:VCALENDAR', 'calendar does not match');
});
});
describe('Calendar String of Single Digit Months', function () {
it('should have one event', function () {
cal.addEvent('Easter', 'Christian holiday celebrating the resurrection of Jesus Christ', 'Jerusalem', '04/20/2014', '04/20/2014');
assert.strictEqual(cal.events()[1], 'BEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:Christian holiday celebrating the resurrection of Jesus Christ\nDTSTART:20140420T000000\nDTEND:20140420T000000\nDTSTAMP:' + stamp + '\nLOCATION:Jerusalem\nSUMMARY;LANGUAGE=en-us:Easter\nTRANSP:TRANSPARENT\nEND:VEVENT');
});
});
describe('Calendar String of Single Digit Day', function () {
it('should have one event', function () {
cal.addEvent('April Fools Day', 'What isn\'t is', 'America', '4/1/2014', '4/1/2014');
assert.strictEqual(cal.events()[2], 'BEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:What isn\'t is\nDTSTART:20140401T000000\nDTEND:20140401T000000\nDTSTAMP:' + stamp + '\nLOCATION:America\nSUMMARY;LANGUAGE=en-us:April Fools Day\nTRANSP:TRANSPARENT\nEND:VEVENT');
});
});
describe('Recurring Events', function () {
it('should add recurring events using frequency and until', function () {
cal.addEvent('Soccer Practice', 'Practice kicking the ball in the net! YAYY!!', 'Soccer field', '08/18/2014', '09/18/2014', {freq: 'WEEKLY', until: '08/18/2014'});
assert.strictEqual(cal.events()[3], 'BEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:Practice kicking the ball in the net! YAYY!!\nDTSTART:20140818T000000\nRRULE:FREQ=WEEKLY;UNTIL=20140818T000000Z\nDTEND:20140918T000000\nDTSTAMP:' + stamp + '\nLOCATION:Soccer field\nSUMMARY;LANGUAGE=en-us:Soccer Practice\nTRANSP:TRANSPARENT\nEND:VEVENT');
});
it('should add recurring events using interval and count', function () {
cal.addEvent('Soccer Practice', 'Practice kicking the ball in the net! YAYY!!', 'Soccer field', '08/18/2014', '09/18/2014', {freq: 'WEEKLY', interval: 2, count: 10});
assert.strictEqual(cal.events()[4], 'BEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:Practice kicking the ball in the net! YAYY!!\nDTSTART:20140818T000000\nRRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=10\nDTEND:20140918T000000\nDTSTAMP:' + stamp + '\nLOCATION:Soccer field\nSUMMARY;LANGUAGE=en-us:Soccer Practice\nTRANSP:TRANSPARENT\nEND:VEVENT');
});
it('should add recurring events using interval and until and byday', function () {
cal.addEvent('Soccer Practice', 'Practice kicking the ball in the net! YAYY!!', 'Soccer field', '08/18/2014', '09/18/2014', {freq: 'WEEKLY', interval: 1, until: '08/18/2014', byday: ['MO','WE','FR']});
assert.strictEqual(cal.events()[5], 'BEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:Practice kicking the ball in the net! YAYY!!\nDTSTART:20140818T000000\nRRULE:FREQ=WEEKLY;UNTIL=20140818T000000Z;INTERVAL=1;BYDAY=MO,WE,FR\nDTEND:20140918T000000\nDTSTAMP:' + stamp + '\nLOCATION:Soccer field\nSUMMARY;LANGUAGE=en-us:Soccer Practice\nTRANSP:TRANSPARENT\nEND:VEVENT');
});
it('should add recurring events using interval and until and ignore duplicates in byday array', function () {
cal.addEvent('Soccer Practice', 'Practice kicking the ball in the net! YAYY!!', 'Soccer field', '08/18/2014', '09/18/2014', {freq: 'WEEKLY', interval: 1, until: '08/18/2014', byday: ['MO', 'WE', 'MO']});
assert.strictEqual(cal.events()[6], 'BEGIN:VEVENT\nCLASS:PUBLIC\nDESCRIPTION:Practice kicking the ball in the net! YAYY!!\nDTSTART:20140818T000000\nRRULE:FREQ=WEEKLY;UNTIL=20140818T000000Z;INTERVAL=1;BYDAY=MO,WE\nDTEND:20140918T000000\nDTSTAMP:' + stamp + '\nLOCATION:Soccer field\nSUMMARY;LANGUAGE=en-us:Soccer Practice\nTRANSP:TRANSPARENT\nEND:VEVENT');
});
});
});
})();