├── CHANGELOG.md ├── renovate.json ├── babel.config.js ├── public ├── favicon.ico └── index.html ├── src ├── main.js ├── plugin.js ├── App.vue └── index.js ├── docs ├── README.md ├── .vuepress │ ├── plugin.js │ └── config.js ├── started.md └── installation.md ├── .gitignore ├── LICENSE ├── package.json ├── README.md └── dist ├── vue-ics.umd.min.js ├── vue-ics.esm.js ├── vue-ics.common.js └── vue-ics.umd.js /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | // TODO: release log here ... 2 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evildvl/vue-ics/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import './plugin' 4 | 5 | Vue.config.productionTip = false 6 | 7 | new Vue({ 8 | render: h => h(App), 9 | }).$mount('#app') 10 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | `VueIcs` plugin is plugin for [Vue.js](http://vuejs.org). 4 | Features include: 5 | 6 | - Feature1 7 | - Feature2 8 | - ... 9 | 10 | [Get started](./started/) or play with the [demo](https://github.com//vue-ics/tree/dev/demo) (see [`README.md`](https://github.com//vue-ics/) to run them). 11 | -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NOTE: 3 | * This file is plugin stub for main.js 4 | */ 5 | 6 | import Vue from 'vue' 7 | import plugin from './index' 8 | 9 | Vue.use(plugin) 10 | 11 | /* 12 | * NOTE: 13 | * If you want Vue instance of main.js to import something in your plugin as a Vue option, 14 | * you need to export it here. 15 | */ 16 | // export default plugin 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | public 4 | dist/*.gz 5 | dist/*.map 6 | coverage 7 | docs/.vuepress/dist 8 | 9 | # local env files 10 | .env.local 11 | .env.*.local 12 | 13 | # related test files 14 | /tests/e2e/reports 15 | /tests/e2e/videos 16 | /tests/e2e/screenshots 17 | 18 | # editor directories and files 19 | .idea 20 | .vscode 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw* 26 | -------------------------------------------------------------------------------- /docs/.vuepress/plugin.js: -------------------------------------------------------------------------------- 1 | const { version } = require('../../package.json') 2 | 3 | module.exports = (/*options, ctx*/) => ({ 4 | async enhanceAppFiles () { 5 | const code = `export default ({ Vue }) => { 6 | Vue.mixin({ 7 | computed: { 8 | $version () { 9 | return '${version}' 10 | } 11 | } 12 | }) 13 | }` 14 | return [{ 15 | name: 'vuepress-plugin-vue-cli-plugin-p11n', 16 | content: code 17 | }] 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-ics 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('./plugin.js') 4 | ], 5 | locales: { 6 | '/': { 7 | lang: 'en-US', 8 | title: 'VueIcs', 9 | description: 'VueIcs for Vue.js' 10 | } 11 | }, 12 | themeConfig: { 13 | repo: '/vue-ics', 14 | docsDir: 'docs', 15 | locales: { 16 | '/': { 17 | label: 'English', 18 | selectText: 'Languages', 19 | editLinkText: 'Edit this page on GitHub', 20 | nav: [{ 21 | text: 'Release Notes', 22 | link: 'https://github.com//vue-ics/releases' 23 | }], 24 | sidebar: [ 25 | '/installation.md', 26 | '/started.md', 27 | ] 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | > We will be using [ES2015](https://github.com/lukehoban/es6features) in the code samples in the guide. 4 | 5 | 6 | ## HTML 7 | 8 | ```html 9 | 10 | 11 | 12 |
13 | 14 |
15 | ``` 16 | 17 | ## JavaScript 18 | 19 | ```javascript 20 | // If using a module system (e.g. via Vue CLI), import Vue and vue-ics and then call Vue.use(vue-ics). 21 | // import Vue from 'vue' 22 | // import vue-ics from 'vue-ics' 23 | // 24 | // Vue.use(vue-ics) 25 | 26 | // NOTE: here the example 27 | 28 | // Now the app has started! 29 | new Vue({ }).$mount('#app') 30 | ``` 31 | 32 | Output the following: 33 | 34 | ```html 35 |
36 | 37 |
38 | ``` 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Direct Download / CDN 4 | 5 | https://unpkg.com/vue-ics/dist/vue-ics 6 | 7 | [unpkg.com](https://unpkg.com) provides NPM-based CDN links. The above link will always point to the latest release on NPM. You can also use a specific version/tag via URLs like https://unpkg.com/vue-ics@{{ $version }}/dist/vue-ics.js 8 | 9 | Include vue-ics after Vue and it will install itself automatically: 10 | 11 | ```html 12 | 13 | 14 | ``` 15 | 16 | ## NPM 17 | 18 | ```sh 19 | $ npm install vue-ics 20 | ``` 21 | 22 | ## Yarn 23 | 24 | ```sh 25 | $ yarn add vue-ics 26 | ``` 27 | 28 | When used with a module system, you must explicitly install the `vue-ics` via `Vue.use()`: 29 | 30 | ```javascript 31 | import Vue from 'vue' 32 | import vue-ics from 'vue-ics' 33 | 34 | Vue.use(vue-ics) 35 | ``` 36 | 37 | You don't need to do this when using global script tags. 38 | 39 | ## Dev Build 40 | 41 | You will have to clone directly from GitHub and build `vue-ics` yourself if 42 | you want to use the latest dev build. 43 | 44 | ```sh 45 | $ git clone https://github.com//vue-ics.git node_modules/vue-ics 46 | $ cd node_modules/vue-ics 47 | $ npm install 48 | $ npm run build 49 | ``` 50 | 51 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 44 | 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-ics", 3 | "description": "Vue.js plugin for generating ICalendar (.ics) files.", 4 | "version": "0.1.4", 5 | "author": "Stanislav Mihaylov ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/evildvl/vue-ics.git" 10 | }, 11 | "scripts": { 12 | "serve": "vue-cli-service serve", 13 | "build": "vue-cli-service build", 14 | "lint": "vue-cli-service lint", 15 | "demo": "vue-cli-service demo", 16 | "docs": "npm run docs:serve", 17 | "docs:build": "vue-cli-service docs --mode build", 18 | "docs:serve": "vue-cli-service docs --mode serve", 19 | "prepublish": "vue-cli-service lint && vue-cli-service build", 20 | "start": "vue-cli-service serve" 21 | }, 22 | "main": "dist/vue-ics.common.js", 23 | "module": "dist/vue-ics.esm.js", 24 | "unpkg": "dist/vue-ics.umd.min.js", 25 | "files": [ 26 | "dist/vue-ics.common.js", 27 | "dist/vue-ics.umd.min.js", 28 | "dist/vue-ics.umd.js", 29 | "dist/vue-ics.esm.js", 30 | "src" 31 | ], 32 | "dependencies": { 33 | "core-js": "^3.0.0", 34 | "file-saver": "^2.0.2" 35 | }, 36 | "devDependencies": { 37 | "@vue/cli-plugin-babel": "4.5.3", 38 | "@vue/cli-plugin-eslint": "4.5.3", 39 | "@vue/cli-service": "4.5.3", 40 | "babel-eslint": "10.1.0", 41 | "cz-conventional-changelog": "3.2.0", 42 | "eslint": "7.7.0", 43 | "eslint-plugin-vue": "6.2.2", 44 | "vue": "2.6.11", 45 | "vue-cli-plugin-p11n": "0.4.0", 46 | "vue-template-compiler": "2.6.11" 47 | }, 48 | "eslintConfig": { 49 | "root": true, 50 | "env": { 51 | "node": true 52 | }, 53 | "extends": [ 54 | "plugin:vue/essential", 55 | "eslint:recommended" 56 | ], 57 | "rules": {}, 58 | "parserOptions": { 59 | "parser": "babel-eslint" 60 | } 61 | }, 62 | "postcss": { 63 | "plugins": { 64 | "autoprefixer": {} 65 | } 66 | }, 67 | "browserslist": [ 68 | "> 1%", 69 | "last 2 versions" 70 | ], 71 | "jsdelivr": "dist/vue-ics.umd.min.js", 72 | "sideeffects": false, 73 | "config": { 74 | "commitizen": { 75 | "path": "./node_modules/cz-conventional-changelog" 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome) 2 | [![npm version](https://badge.fury.io/js/vue-ics.svg)](https://badge.fury.io/js/vue-ics) 3 | [![Total alerts](https://img.shields.io/lgtm/alerts/g/evildvl/vue-ics.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/evildvl/vue-ics/alerts/) 4 | [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/evildvl/vue-ics.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/evildvl/vue-ics/context:javascript) 5 | [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) 6 | 7 | # vue-ics 8 | 9 | Vue.js plugin for generating ICalendar (.ics) files. 10 | 11 | ## Installation 12 | 13 | ##### NPM 14 | 15 | ``` 16 | npm install vue-ics --save 17 | ``` 18 | 19 | ##### Vue 20 | 21 | ```javascript 22 | import Vue from 'vue' 23 | import ICS from 'vue-ics' 24 | 25 | Vue.use(ICS, options) 26 | ``` 27 | 28 | ## Initialising options (optional) 29 | {string} uidDomain - Your domain 30 | 31 | {string} prodId - Product ID 32 | 33 | ## Create event 34 | 35 | {string} language - Language in format en-us (default) 36 | 37 | {string} subject - Subject/Title of event 38 | 39 | {string} description - Description of event 40 | 41 | {string} location - Location of event 42 | 43 | {string} begin - Beginning date of event 44 | 45 | {string} stop - Ending date of event 46 | 47 | {string} url - URL (optional) 48 | 49 | {object} organizer - Organizer 50 | 51 |   {string} name - Organizer name 52 | 53 |   {string} email - Organizer email 54 | 55 | {RRule} rrule - Reccurence rule (optional) 56 | 57 | ```javascript 58 | this.$ics.addEvent(language, subject, description, location, begin, stop, url, organizer, rrule) 59 | ``` 60 | You can add one or more events. 61 | 62 | ## Reccurence rule 63 | You can add recurrence rule for your event. Event will be repeat as you want. 64 | ```javascript 65 | const rrule = { 66 | freq: 'WEEKLY', 67 | until: until, 68 | interval: 1 69 | } 70 | ``` 71 | 72 | Parameters: 73 | 74 | {string} freq - Required. The frequency of event recurrence. Can be DAILY, WEEKLY, MONTHLY, or YEARLY. 75 | 76 | {string | number | Date} until - date stringA date string representing the date on which to end repitition. Must be friendly to Date() 77 | 78 | {number} 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 79 | 80 | {array} byday - Which days of the week the event is to occur. An array containing any of SU, MO, TU, WE, TH, FR, SA 81 | 82 | ## Remove all events from the calendar 83 | 84 | ```javascript 85 | this.$ics.removeAllEvents() 86 | ``` 87 | 88 | ## Get calendar with events as string 89 | 90 | ```javascript 91 | this.$ics.calendar() 92 | ``` 93 | 94 | ##Download calendar file 95 | 96 | ```javascript 97 | this.$ics.download(fileName) 98 | ``` 99 | 100 | {string} fileName - name of file without extension (will be *.ics) 101 | -------------------------------------------------------------------------------- /dist/vue-ics.umd.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-ics v0.1.3 3 | * (c) 2020 Stanislav Mihaylov 4 | * Released under the MIT License. 5 | */ 6 | !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("file-saver")):"function"==typeof define&&define.amd?define(["file-saver"],t):(n=n||self,n.VueIcs=t(n.fileSaver))}(this,function(n){"use strict";function t(n){var t=0,e=["SU","MO","TU","WE","TH","FR","SA"];if("YEARLY"!==n.freq&&"MONTHLY"!==n.freq&&"WEEKLY"!==n.freq&&"DAILY"!==n.freq)throw t+=1,"Recurrence rrule frequency must be provided and be one of the following: 'YEARLY', 'MONTHLY', 'WEEKLY', or 'DAILY'";if(n.until&&isNaN(Date.parse(n.until)))throw t+=1,"Recurrence rrule 'until' must be a valid date string";if(n.interval&&isNaN(parseInt(n.interval)))throw t+=1,"Recurrence rrule 'interval' must be an integer";if(n.count&&isNaN(parseInt(n.count)))throw t+=1,"Recurrence rrule 'count' must be an integer";if(void 0!==n.byday){if("[object Array]"!==Object.prototype.toString.call(n.byday))throw t+=1,"Recurrence rrule 'byday' must be an array";if(n.byday.length>7)throw t+=1,"Recurrence rrule 'byday' array must not be longer than the 7 days in a week";n.byday=n.byday.filter(function(t,e){return n.byday.indexOf(t)==e});for(var c in n.byday)if(e.indexOf(n.byday[c])<0)throw t+=1,"Recurrence rrule 'byday' values must include only the following: 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'"}return 0===t}function e(n){return"\n".concat(n,"\n")}function c(){return a++}var o=[],a=0,r=function(a){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{uidDomain:"evildvl",prodId:"vueICS"};a.prototype.$ics={removeAllEvents:function(){o=[]},addEvent:function(){var n,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"en-us",a=arguments.length>1?arguments[1]:void 0,i=arguments.length>2?arguments[2]:void 0,l=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"none",u=arguments.length>4?arguments[4]:void 0,s=arguments.length>5?arguments[5]:void 0,d=arguments.length>6&&void 0!==arguments[6]?arguments[6]:null,g=arguments.length>7&&void 0!==arguments[7]?arguments[7]:null,f=arguments.length>8&&void 0!==arguments[8]?arguments[8]:null;if(void 0===a||void 0===i||void 0===l||void 0===u||void 0===s)throw"You need to specify function arguments";if(f&&t(f)){if(n="RRULE:FREQ=".concat(f.freq),f.until){var v=new Date(Date.parse(f.until)).toISOString();n+=";UNTIL=".concat(v.substring(0,v.length-13).replace(/[-]/g,""),"000000Z")}f.interval&&(n+=";INTERVAL=".concat(f.interval)),f.count&&(n+=";COUNT=".concat(f.count)),f.byday&&f.byday.length>0&&(n+=";BYDAY=".concat(f.byday.join(",")))}var y=new Date(u),E=new Date(s),S=new Date,D="".concat(S.getDay()).concat(S.getMonth()).concat(S.getFullYear(),"-").concat(S.getHours()).concat(S.getMinutes()).concat(S.getSeconds()).concat(c()),A="0000".concat(y.getFullYear().toString()).slice(-4),h="00".concat((y.getMonth()+1).toString()).slice(-2),N="00".concat(y.getDate().toString()).slice(-2),R="00".concat(y.getHours().toString()).slice(-2),T="00".concat(y.getMinutes().toString()).slice(-2),I="00".concat(y.getSeconds().toString()).slice(-2),b="0000".concat(E.getFullYear().toString()).slice(-4),p="00".concat((E.getMonth()+1).toString()).slice(-2),w="00".concat(E.getDate().toString()).slice(-2),L="00".concat(E.getHours().toString()).slice(-2),m="00".concat(E.getMinutes().toString()).slice(-2),M="00".concat(E.getSeconds().toString()).slice(-2),O="0000".concat(S.getFullYear().toString()).slice(-4),Y="00".concat((S.getMonth()+1).toString()).slice(-2),V="00".concat(S.getDate().toString()).slice(-2),U="00".concat(S.getHours().toString()).slice(-2),C="00".concat(S.getMinutes().toString()).slice(-2),H="00".concat(S.getSeconds().toString()).slice(-2),q="",F="";R+T+I+L+m+M!=0&&(q="T".concat(R).concat(T).concat(I),F="T".concat(L).concat(m).concat(M));var j="T".concat(U).concat(C).concat(H),x=A+h+N+q,G=b+p+w+F,B=O+Y+V+j,P="\n BEGIN:VEVENT\n UID:".concat(D,"@").concat(r.uidDomain,"\n ").concat(d?"URL:"+d:"","\n DESCRIPTION:").concat(i).concat(n?"\n"+n:"","\n DTSTAMP;VALUE=DATE-TIME:").concat(B,",\n DTSTART;VALUE=DATE-TIME:").concat(x,"\n DTEND;VALUE=DATE-TIME:").concat(G,"\n LOCATION:").concat(l,"\n ").concat(g?"ORGANIZER;CN="+g.name+":MAILTO:"+g.email:"","\n SUMMARY;LANGUAGE=").concat(e,":").concat(a,"\n END:VEVENT\n ");return o.push(P),P},calendar:function(){return e("\n BEGIN:VCALENDAR\n PRODID:".concat(r.prodId,"\n VERSION:2.0\n ").concat(o.join("\n"),"\n END:VCALENDAR\n\n ").replace(/^\s*[\r\n]/gm,"").replace(/^\s+/gm,""))},download:function(t){var c=e("\n BEGIN:VCALENDAR\n PRODID:".concat(r.prodId,"\n VERSION:2.0\n ").concat(o.join("\n"),"\n END:VCALENDAR\n\n ").replace(/^\s*[\r\n]/gm,"").replace(/^\s+/gm,"")),a=new Blob([c],{type:"text/x-vCalendar;charset=utf-8"});n.saveAs(a,"".concat(t,".ics"))}}},i={install:r,version:"0.1.3"};return"undefined"!=typeof window&&window.Vue&&window.Vue.use(i),i}); -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { saveAs } from 'file-saver' 2 | const version = '__VERSION__' 3 | 4 | /** 5 | * Reccurence rule 6 | * @typedef {Object} RRule 7 | * @property {string} freq - Required. The frequency of event recurrence. Can be DAILY, WEEKLY, MONTHLY, or YEARLY. 8 | * @property {string | number | Date} until - date stringA date string representing the date on which to end repitition. Must be friendly to Date() 9 | * @property {number} 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 10 | * @property {array} byday - Which days of the week the event is to occur. An array containing any of SU, MO, TU, WE, TH, FR, SA 11 | */ 12 | 13 | var Events = [] 14 | 15 | /** 16 | * Reccurence rule validation 17 | * @function 18 | * @param {RRule} rrule - Reccurence rule 19 | * @returns {boolean} 20 | */ 21 | function validateRepeatRule(rrule) { 22 | let counter = 0 23 | const BYDAY_VALUES = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'] 24 | if (rrule.freq !== 'YEARLY' && rrule.freq !== 'MONTHLY' && rrule.freq !== 'WEEKLY' && rrule.freq !== 'DAILY') { 25 | counter += 1 26 | throw "Recurrence rrule frequency must be provided and be one of the following: 'YEARLY', 'MONTHLY', 'WEEKLY', or 'DAILY'"; 27 | } 28 | 29 | if (rrule.until) { 30 | if (isNaN(Date.parse(rrule.until))) { 31 | counter += 1 32 | throw "Recurrence rrule 'until' must be a valid date string"; 33 | } 34 | } 35 | 36 | if (rrule.interval) { 37 | if (isNaN(parseInt(rrule.interval))) { 38 | counter += 1 39 | throw "Recurrence rrule 'interval' must be an integer"; 40 | } 41 | } 42 | 43 | if (rrule.count) { 44 | if (isNaN(parseInt(rrule.count))) { 45 | counter += 1 46 | throw "Recurrence rrule 'count' must be an integer"; 47 | } 48 | } 49 | 50 | if (typeof rrule.byday !== 'undefined') { 51 | if ((Object.prototype.toString.call(rrule.byday) !== '[object Array]')) { 52 | counter += 1 53 | throw "Recurrence rrule 'byday' must be an array"; 54 | } 55 | 56 | if (rrule.byday.length > 7) { 57 | counter += 1 58 | throw "Recurrence rrule 'byday' array must not be longer than the 7 days in a week"; 59 | } 60 | 61 | rrule.byday = rrule.byday.filter(function (elem, pos) { 62 | return rrule.byday.indexOf(elem) == pos; 63 | }); 64 | 65 | for (var d in rrule.byday) { 66 | if (BYDAY_VALUES.indexOf(rrule.byday[d]) < 0) { 67 | counter += 1 68 | throw "Recurrence rrule 'byday' values must include only the following: 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'"; 69 | } 70 | } 71 | } 72 | return (counter === 0) 73 | } 74 | 75 | /** 76 | * Helper function for appending CRLF at start and end of file according to RFC rules. 77 | * @function 78 | * @param {string} string - iCalendar source string 79 | * @return {string} 80 | */ 81 | function addCRLF(string) { 82 | return `\n${string}\n` 83 | } 84 | 85 | 86 | var counter = 0; 87 | 88 | function getUniqueNumber() { 89 | return counter++; 90 | } 91 | 92 | const install = (Vue, options = {uidDomain: 'evildvl', prodId: 'vueICS'}) => { 93 | Vue.prototype.$ics = { 94 | /** 95 | * Add event to the calendar 96 | * @function 97 | * @param {string} language Language in format en-us (default) 98 | * @param {string} subject Subject/Title of event 99 | * @param {string} description Description of event 100 | * @param {string} location Location of event 101 | * @param {string} begin Beginning date of event 102 | * @param {string} stop Ending date of event 103 | * @param {string} url URL 104 | * @param {object} organizer Organizer 105 | * @param {RRule} rrule Reccurence rule 106 | * @returns {string} event 107 | **/ 108 | 109 | removeAllEvents: () => { 110 | Events = []; 111 | }, 112 | addEvent: (language = 'en-us', subject, description, location = 'none', begin, stop, url = null, organizer = null, rrule = null) => { 113 | let rruleString 114 | 115 | if (typeof subject === 'undefined' || 116 | typeof description === 'undefined' || 117 | typeof location === 'undefined' || 118 | typeof begin === 'undefined' || 119 | typeof stop === 'undefined') { 120 | throw 'You need to specify function arguments' 121 | } 122 | if (rrule && validateRepeatRule(rrule)) { 123 | rruleString = `RRULE:FREQ=${rrule.freq}` 124 | if (rrule.until) { 125 | let untilDate = new Date(Date.parse(rrule.until)).toISOString(); 126 | rruleString += `;UNTIL=${untilDate.substring(0, untilDate.length - 13).replace(/[-]/g, '')}000000Z` 127 | } 128 | if (rrule.interval) rruleString += `;INTERVAL=${rrule.interval}` 129 | if (rrule.count) rruleString += `;COUNT=${rrule.count}` 130 | if (rrule.byday && rrule.byday.length > 0) rruleString += `;BYDAY=${rrule.byday.join(',')}` 131 | } 132 | 133 | let start_date = new Date(begin); 134 | let end_date = new Date(stop); 135 | let now_date = new Date(); 136 | const UID = `${now_date.getDay()}${now_date.getMonth()}${now_date.getFullYear()}-${now_date.getHours()}${now_date.getMinutes()}${now_date.getSeconds()}${getUniqueNumber()}` 137 | 138 | let start_year = (`0000${(start_date.getFullYear().toString())}`).slice(-4) 139 | let start_month = (`00${((start_date.getMonth() + 1).toString())}`).slice(-2) 140 | let start_day = (`00${((start_date.getDate()).toString())}`).slice(-2) 141 | let start_hours = (`00${(start_date.getHours().toString())}`).slice(-2) 142 | let start_minutes = (`00${(start_date.getMinutes().toString())}`).slice(-2) 143 | let start_seconds = (`00${(start_date.getSeconds().toString())}`).slice(-2) 144 | 145 | let end_year = (`0000${(end_date.getFullYear().toString())}`).slice(-4) 146 | let end_month = (`00${((end_date.getMonth() + 1).toString())}`).slice(-2) 147 | let end_day = (`00${((end_date.getDate()).toString())}`).slice(-2) 148 | let end_hours = (`00${(end_date.getHours().toString())}`).slice(-2) 149 | let end_minutes = (`00${(end_date.getMinutes().toString())}`).slice(-2) 150 | let end_seconds = (`00${(end_date.getSeconds().toString())}`).slice(-2) 151 | 152 | let now_year = (`0000${(now_date.getFullYear().toString())}`).slice(-4) 153 | let now_month = (`00${((now_date.getMonth() + 1).toString())}`).slice(-2) 154 | let now_day = (`00${((now_date.getDate()).toString())}`).slice(-2) 155 | let now_hours = (`00${(now_date.getHours().toString())}`).slice(-2) 156 | let now_minutes = (`00${(now_date.getMinutes().toString())}`).slice(-2) 157 | let now_seconds = (`00${(now_date.getSeconds().toString())}`).slice(-2) 158 | 159 | let start_time = ''; 160 | let end_time = ''; 161 | if (start_hours + start_minutes + start_seconds + end_hours + end_minutes + end_seconds != 0) { 162 | start_time = `T${start_hours}${start_minutes}${start_seconds}` 163 | end_time = `T${end_hours}${end_minutes}${end_seconds}` 164 | } 165 | let now_time = `T${now_hours}${now_minutes}${now_seconds}` 166 | 167 | let start = start_year + start_month + start_day + start_time 168 | let end = end_year + end_month + end_day + end_time 169 | let now = now_year + now_month + now_day + now_time 170 | 171 | const Event = ` 172 | BEGIN:VEVENT 173 | UID:${UID}@${options.uidDomain} 174 | ${(url) ? 'URL:' + url : ''} 175 | DESCRIPTION:${description}${(rruleString) ? '\n' + rruleString : ''} 176 | DTSTAMP;VALUE=DATE-TIME:${now}, 177 | DTSTART;VALUE=DATE-TIME:${start} 178 | DTEND;VALUE=DATE-TIME:${end} 179 | LOCATION:${location} 180 | ${(organizer) ? 'ORGANIZER;CN=' + organizer.name + ':MAILTO:' + organizer.email : ''} 181 | SUMMARY;LANGUAGE=${language}:${subject} 182 | END:VEVENT 183 | ` 184 | Events.push(Event) 185 | return Event 186 | }, 187 | /** 188 | * Returns calendar 189 | * @function 190 | * @return {string} Calendar in iCalendar format 191 | */ 192 | calendar: () => { 193 | return addCRLF(` 194 | BEGIN:VCALENDAR 195 | PRODID:${options.prodId} 196 | VERSION:2.0 197 | ${Events.join('\n')} 198 | END:VCALENDAR 199 | 200 | `.replace(/^\s*[\r\n]/gm, "").replace(/^\s+/gm, '')) 201 | }, 202 | /** 203 | * Download iCalendar file 204 | * @function 205 | * @param {string} filename - Name of the file without extension 206 | */ 207 | download: (filename) => { 208 | const Calendar = addCRLF(` 209 | BEGIN:VCALENDAR 210 | PRODID:${options.prodId} 211 | VERSION:2.0 212 | ${Events.join('\n')} 213 | END:VCALENDAR 214 | 215 | `.replace(/^\s*[\r\n]/gm, "").replace(/^\s+/gm, '')) 216 | var blob = new Blob([Calendar], {type: "text/x-vCalendar;charset=utf-8"}); 217 | saveAs(blob, `${filename}.ics`); 218 | } 219 | 220 | } 221 | } 222 | 223 | const plugin = { 224 | install, 225 | version 226 | } 227 | 228 | export default plugin 229 | 230 | if (typeof window !== 'undefined' && window.Vue) { 231 | window.Vue.use(plugin) 232 | } 233 | -------------------------------------------------------------------------------- /dist/vue-ics.esm.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-ics v0.1.3 3 | * (c) 2020 Stanislav Mihaylov 4 | * Released under the MIT License. 5 | */ 6 | import { saveAs } from 'file-saver'; 7 | 8 | var version = '0.1.3'; 9 | /** 10 | * Reccurence rule 11 | * @typedef {Object} RRule 12 | * @property {string} freq - Required. The frequency of event recurrence. Can be DAILY, WEEKLY, MONTHLY, or YEARLY. 13 | * @property {string | number | Date} until - date stringA date string representing the date on which to end repitition. Must be friendly to Date() 14 | * @property {number} 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 15 | * @property {array} byday - Which days of the week the event is to occur. An array containing any of SU, MO, TU, WE, TH, FR, SA 16 | */ 17 | 18 | var Events = []; 19 | /** 20 | * Reccurence rule validation 21 | * @function 22 | * @param {RRule} rrule - Reccurence rule 23 | * @returns {boolean} 24 | */ 25 | 26 | function validateRepeatRule(rrule) { 27 | var counter = 0; 28 | var BYDAY_VALUES = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']; 29 | 30 | if (rrule.freq !== 'YEARLY' && rrule.freq !== 'MONTHLY' && rrule.freq !== 'WEEKLY' && rrule.freq !== 'DAILY') { 31 | counter += 1; 32 | throw "Recurrence rrule frequency must be provided and be one of the following: 'YEARLY', 'MONTHLY', 'WEEKLY', or 'DAILY'"; 33 | } 34 | 35 | if (rrule.until) { 36 | if (isNaN(Date.parse(rrule.until))) { 37 | counter += 1; 38 | throw "Recurrence rrule 'until' must be a valid date string"; 39 | } 40 | } 41 | 42 | if (rrule.interval) { 43 | if (isNaN(parseInt(rrule.interval))) { 44 | counter += 1; 45 | throw "Recurrence rrule 'interval' must be an integer"; 46 | } 47 | } 48 | 49 | if (rrule.count) { 50 | if (isNaN(parseInt(rrule.count))) { 51 | counter += 1; 52 | throw "Recurrence rrule 'count' must be an integer"; 53 | } 54 | } 55 | 56 | if (typeof rrule.byday !== 'undefined') { 57 | if (Object.prototype.toString.call(rrule.byday) !== '[object Array]') { 58 | counter += 1; 59 | throw "Recurrence rrule 'byday' must be an array"; 60 | } 61 | 62 | if (rrule.byday.length > 7) { 63 | counter += 1; 64 | throw "Recurrence rrule 'byday' array must not be longer than the 7 days in a week"; 65 | } 66 | 67 | rrule.byday = rrule.byday.filter(function (elem, pos) { 68 | return rrule.byday.indexOf(elem) == pos; 69 | }); 70 | 71 | for (var d in rrule.byday) { 72 | if (BYDAY_VALUES.indexOf(rrule.byday[d]) < 0) { 73 | counter += 1; 74 | throw "Recurrence rrule 'byday' values must include only the following: 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'"; 75 | } 76 | } 77 | } 78 | 79 | return counter === 0; 80 | } 81 | /** 82 | * Helper function for appending CRLF at start and end of file according to RFC rules. 83 | * @function 84 | * @param {string} string - iCalendar source string 85 | * @return {string} 86 | */ 87 | 88 | 89 | function addCRLF(string) { 90 | return "\n".concat(string, "\n"); 91 | } 92 | 93 | var counter = 0; 94 | 95 | function getUniqueNumber() { 96 | return counter++; 97 | } 98 | 99 | var install = function install(Vue) { 100 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { 101 | uidDomain: 'evildvl', 102 | prodId: 'vueICS' 103 | }; 104 | Vue.prototype.$ics = { 105 | /** 106 | * Add event to the calendar 107 | * @function 108 | * @param {string} language Language in format en-us (default) 109 | * @param {string} subject Subject/Title of event 110 | * @param {string} description Description of event 111 | * @param {string} location Location of event 112 | * @param {string} begin Beginning date of event 113 | * @param {string} stop Ending date of event 114 | * @param {string} url URL 115 | * @param {object} organizer Organizer 116 | * @param {RRule} rrule Reccurence rule 117 | * @returns {string} event 118 | **/ 119 | removeAllEvents: function removeAllEvents() { 120 | Events = []; 121 | }, 122 | addEvent: function addEvent() { 123 | var language = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'en-us'; 124 | var subject = arguments.length > 1 ? arguments[1] : undefined; 125 | var description = arguments.length > 2 ? arguments[2] : undefined; 126 | var location = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'none'; 127 | var begin = arguments.length > 4 ? arguments[4] : undefined; 128 | var stop = arguments.length > 5 ? arguments[5] : undefined; 129 | var url = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null; 130 | var organizer = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null; 131 | var rrule = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null; 132 | var rruleString; 133 | 134 | if (typeof subject === 'undefined' || typeof description === 'undefined' || typeof location === 'undefined' || typeof begin === 'undefined' || typeof stop === 'undefined') { 135 | throw 'You need to specify function arguments'; 136 | } 137 | 138 | if (rrule && validateRepeatRule(rrule)) { 139 | rruleString = "RRULE:FREQ=".concat(rrule.freq); 140 | 141 | if (rrule.until) { 142 | var untilDate = new Date(Date.parse(rrule.until)).toISOString(); 143 | rruleString += ";UNTIL=".concat(untilDate.substring(0, untilDate.length - 13).replace(/[-]/g, ''), "000000Z"); 144 | } 145 | 146 | if (rrule.interval) rruleString += ";INTERVAL=".concat(rrule.interval); 147 | if (rrule.count) rruleString += ";COUNT=".concat(rrule.count); 148 | if (rrule.byday && rrule.byday.length > 0) rruleString += ";BYDAY=".concat(rrule.byday.join(',')); 149 | } 150 | 151 | var start_date = new Date(begin); 152 | var end_date = new Date(stop); 153 | var now_date = new Date(); 154 | var UID = "".concat(now_date.getDay()).concat(now_date.getMonth()).concat(now_date.getFullYear(), "-").concat(now_date.getHours()).concat(now_date.getMinutes()).concat(now_date.getSeconds()).concat(getUniqueNumber()); 155 | var start_year = "0000".concat(start_date.getFullYear().toString()).slice(-4); 156 | var start_month = "00".concat((start_date.getMonth() + 1).toString()).slice(-2); 157 | var start_day = "00".concat(start_date.getDate().toString()).slice(-2); 158 | var start_hours = "00".concat(start_date.getHours().toString()).slice(-2); 159 | var start_minutes = "00".concat(start_date.getMinutes().toString()).slice(-2); 160 | var start_seconds = "00".concat(start_date.getSeconds().toString()).slice(-2); 161 | var end_year = "0000".concat(end_date.getFullYear().toString()).slice(-4); 162 | var end_month = "00".concat((end_date.getMonth() + 1).toString()).slice(-2); 163 | var end_day = "00".concat(end_date.getDate().toString()).slice(-2); 164 | var end_hours = "00".concat(end_date.getHours().toString()).slice(-2); 165 | var end_minutes = "00".concat(end_date.getMinutes().toString()).slice(-2); 166 | var end_seconds = "00".concat(end_date.getSeconds().toString()).slice(-2); 167 | var now_year = "0000".concat(now_date.getFullYear().toString()).slice(-4); 168 | var now_month = "00".concat((now_date.getMonth() + 1).toString()).slice(-2); 169 | var now_day = "00".concat(now_date.getDate().toString()).slice(-2); 170 | var now_hours = "00".concat(now_date.getHours().toString()).slice(-2); 171 | var now_minutes = "00".concat(now_date.getMinutes().toString()).slice(-2); 172 | var now_seconds = "00".concat(now_date.getSeconds().toString()).slice(-2); 173 | var start_time = ''; 174 | var end_time = ''; 175 | 176 | if (start_hours + start_minutes + start_seconds + end_hours + end_minutes + end_seconds != 0) { 177 | start_time = "T".concat(start_hours).concat(start_minutes).concat(start_seconds); 178 | end_time = "T".concat(end_hours).concat(end_minutes).concat(end_seconds); 179 | } 180 | 181 | var now_time = "T".concat(now_hours).concat(now_minutes).concat(now_seconds); 182 | var start = start_year + start_month + start_day + start_time; 183 | var end = end_year + end_month + end_day + end_time; 184 | var now = now_year + now_month + now_day + now_time; 185 | var Event = "\n BEGIN:VEVENT\n UID:".concat(UID, "@").concat(options.uidDomain, "\n ").concat(url ? 'URL:' + url : '', "\n DESCRIPTION:").concat(description).concat(rruleString ? '\n' + rruleString : '', "\n DTSTAMP;VALUE=DATE-TIME:").concat(now, ",\n DTSTART;VALUE=DATE-TIME:").concat(start, "\n DTEND;VALUE=DATE-TIME:").concat(end, "\n LOCATION:").concat(location, "\n ").concat(organizer ? 'ORGANIZER;CN=' + organizer.name + ':MAILTO:' + organizer.email : '', "\n SUMMARY;LANGUAGE=").concat(language, ":").concat(subject, "\n END:VEVENT\n "); 186 | Events.push(Event); 187 | return Event; 188 | }, 189 | 190 | /** 191 | * Returns calendar 192 | * @function 193 | * @return {string} Calendar in iCalendar format 194 | */ 195 | calendar: function calendar() { 196 | return addCRLF("\n BEGIN:VCALENDAR\n PRODID:".concat(options.prodId, "\n VERSION:2.0\n ").concat(Events.join('\n'), "\n END:VCALENDAR\n\n ").replace(/^\s*[\r\n]/gm, "").replace(/^\s+/gm, '')); 197 | }, 198 | 199 | /** 200 | * Download iCalendar file 201 | * @function 202 | * @param {string} filename - Name of the file without extension 203 | */ 204 | download: function download(filename) { 205 | var Calendar = addCRLF("\n BEGIN:VCALENDAR\n PRODID:".concat(options.prodId, "\n VERSION:2.0\n ").concat(Events.join('\n'), "\n END:VCALENDAR\n\n ").replace(/^\s*[\r\n]/gm, "").replace(/^\s+/gm, '')); 206 | var blob = new Blob([Calendar], { 207 | type: "text/x-vCalendar;charset=utf-8" 208 | }); 209 | saveAs(blob, "".concat(filename, ".ics")); 210 | } 211 | }; 212 | }; 213 | 214 | var plugin = { 215 | install: install, 216 | version: version 217 | }; 218 | 219 | if (typeof window !== 'undefined' && window.Vue) { 220 | window.Vue.use(plugin); 221 | } 222 | 223 | export default plugin; 224 | -------------------------------------------------------------------------------- /dist/vue-ics.common.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-ics v0.1.3 3 | * (c) 2020 Stanislav Mihaylov 4 | * Released under the MIT License. 5 | */ 6 | 'use strict'; 7 | 8 | var fileSaver = require('file-saver'); 9 | 10 | var version = '0.1.3'; 11 | /** 12 | * Reccurence rule 13 | * @typedef {Object} RRule 14 | * @property {string} freq - Required. The frequency of event recurrence. Can be DAILY, WEEKLY, MONTHLY, or YEARLY. 15 | * @property {string | number | Date} until - date stringA date string representing the date on which to end repitition. Must be friendly to Date() 16 | * @property {number} 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 17 | * @property {array} byday - Which days of the week the event is to occur. An array containing any of SU, MO, TU, WE, TH, FR, SA 18 | */ 19 | 20 | var Events = []; 21 | /** 22 | * Reccurence rule validation 23 | * @function 24 | * @param {RRule} rrule - Reccurence rule 25 | * @returns {boolean} 26 | */ 27 | 28 | function validateRepeatRule(rrule) { 29 | var counter = 0; 30 | var BYDAY_VALUES = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']; 31 | 32 | if (rrule.freq !== 'YEARLY' && rrule.freq !== 'MONTHLY' && rrule.freq !== 'WEEKLY' && rrule.freq !== 'DAILY') { 33 | counter += 1; 34 | throw "Recurrence rrule frequency must be provided and be one of the following: 'YEARLY', 'MONTHLY', 'WEEKLY', or 'DAILY'"; 35 | } 36 | 37 | if (rrule.until) { 38 | if (isNaN(Date.parse(rrule.until))) { 39 | counter += 1; 40 | throw "Recurrence rrule 'until' must be a valid date string"; 41 | } 42 | } 43 | 44 | if (rrule.interval) { 45 | if (isNaN(parseInt(rrule.interval))) { 46 | counter += 1; 47 | throw "Recurrence rrule 'interval' must be an integer"; 48 | } 49 | } 50 | 51 | if (rrule.count) { 52 | if (isNaN(parseInt(rrule.count))) { 53 | counter += 1; 54 | throw "Recurrence rrule 'count' must be an integer"; 55 | } 56 | } 57 | 58 | if (typeof rrule.byday !== 'undefined') { 59 | if (Object.prototype.toString.call(rrule.byday) !== '[object Array]') { 60 | counter += 1; 61 | throw "Recurrence rrule 'byday' must be an array"; 62 | } 63 | 64 | if (rrule.byday.length > 7) { 65 | counter += 1; 66 | throw "Recurrence rrule 'byday' array must not be longer than the 7 days in a week"; 67 | } 68 | 69 | rrule.byday = rrule.byday.filter(function (elem, pos) { 70 | return rrule.byday.indexOf(elem) == pos; 71 | }); 72 | 73 | for (var d in rrule.byday) { 74 | if (BYDAY_VALUES.indexOf(rrule.byday[d]) < 0) { 75 | counter += 1; 76 | throw "Recurrence rrule 'byday' values must include only the following: 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'"; 77 | } 78 | } 79 | } 80 | 81 | return counter === 0; 82 | } 83 | /** 84 | * Helper function for appending CRLF at start and end of file according to RFC rules. 85 | * @function 86 | * @param {string} string - iCalendar source string 87 | * @return {string} 88 | */ 89 | 90 | 91 | function addCRLF(string) { 92 | return "\n".concat(string, "\n"); 93 | } 94 | 95 | var counter = 0; 96 | 97 | function getUniqueNumber() { 98 | return counter++; 99 | } 100 | 101 | var install = function install(Vue) { 102 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { 103 | uidDomain: 'evildvl', 104 | prodId: 'vueICS' 105 | }; 106 | Vue.prototype.$ics = { 107 | /** 108 | * Add event to the calendar 109 | * @function 110 | * @param {string} language Language in format en-us (default) 111 | * @param {string} subject Subject/Title of event 112 | * @param {string} description Description of event 113 | * @param {string} location Location of event 114 | * @param {string} begin Beginning date of event 115 | * @param {string} stop Ending date of event 116 | * @param {string} url URL 117 | * @param {object} organizer Organizer 118 | * @param {RRule} rrule Reccurence rule 119 | * @returns {string} event 120 | **/ 121 | removeAllEvents: function removeAllEvents() { 122 | Events = []; 123 | }, 124 | addEvent: function addEvent() { 125 | var language = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'en-us'; 126 | var subject = arguments.length > 1 ? arguments[1] : undefined; 127 | var description = arguments.length > 2 ? arguments[2] : undefined; 128 | var location = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'none'; 129 | var begin = arguments.length > 4 ? arguments[4] : undefined; 130 | var stop = arguments.length > 5 ? arguments[5] : undefined; 131 | var url = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null; 132 | var organizer = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null; 133 | var rrule = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null; 134 | var rruleString; 135 | 136 | if (typeof subject === 'undefined' || typeof description === 'undefined' || typeof location === 'undefined' || typeof begin === 'undefined' || typeof stop === 'undefined') { 137 | throw 'You need to specify function arguments'; 138 | } 139 | 140 | if (rrule && validateRepeatRule(rrule)) { 141 | rruleString = "RRULE:FREQ=".concat(rrule.freq); 142 | 143 | if (rrule.until) { 144 | var untilDate = new Date(Date.parse(rrule.until)).toISOString(); 145 | rruleString += ";UNTIL=".concat(untilDate.substring(0, untilDate.length - 13).replace(/[-]/g, ''), "000000Z"); 146 | } 147 | 148 | if (rrule.interval) rruleString += ";INTERVAL=".concat(rrule.interval); 149 | if (rrule.count) rruleString += ";COUNT=".concat(rrule.count); 150 | if (rrule.byday && rrule.byday.length > 0) rruleString += ";BYDAY=".concat(rrule.byday.join(',')); 151 | } 152 | 153 | var start_date = new Date(begin); 154 | var end_date = new Date(stop); 155 | var now_date = new Date(); 156 | var UID = "".concat(now_date.getDay()).concat(now_date.getMonth()).concat(now_date.getFullYear(), "-").concat(now_date.getHours()).concat(now_date.getMinutes()).concat(now_date.getSeconds()).concat(getUniqueNumber()); 157 | var start_year = "0000".concat(start_date.getFullYear().toString()).slice(-4); 158 | var start_month = "00".concat((start_date.getMonth() + 1).toString()).slice(-2); 159 | var start_day = "00".concat(start_date.getDate().toString()).slice(-2); 160 | var start_hours = "00".concat(start_date.getHours().toString()).slice(-2); 161 | var start_minutes = "00".concat(start_date.getMinutes().toString()).slice(-2); 162 | var start_seconds = "00".concat(start_date.getSeconds().toString()).slice(-2); 163 | var end_year = "0000".concat(end_date.getFullYear().toString()).slice(-4); 164 | var end_month = "00".concat((end_date.getMonth() + 1).toString()).slice(-2); 165 | var end_day = "00".concat(end_date.getDate().toString()).slice(-2); 166 | var end_hours = "00".concat(end_date.getHours().toString()).slice(-2); 167 | var end_minutes = "00".concat(end_date.getMinutes().toString()).slice(-2); 168 | var end_seconds = "00".concat(end_date.getSeconds().toString()).slice(-2); 169 | var now_year = "0000".concat(now_date.getFullYear().toString()).slice(-4); 170 | var now_month = "00".concat((now_date.getMonth() + 1).toString()).slice(-2); 171 | var now_day = "00".concat(now_date.getDate().toString()).slice(-2); 172 | var now_hours = "00".concat(now_date.getHours().toString()).slice(-2); 173 | var now_minutes = "00".concat(now_date.getMinutes().toString()).slice(-2); 174 | var now_seconds = "00".concat(now_date.getSeconds().toString()).slice(-2); 175 | var start_time = ''; 176 | var end_time = ''; 177 | 178 | if (start_hours + start_minutes + start_seconds + end_hours + end_minutes + end_seconds != 0) { 179 | start_time = "T".concat(start_hours).concat(start_minutes).concat(start_seconds); 180 | end_time = "T".concat(end_hours).concat(end_minutes).concat(end_seconds); 181 | } 182 | 183 | var now_time = "T".concat(now_hours).concat(now_minutes).concat(now_seconds); 184 | var start = start_year + start_month + start_day + start_time; 185 | var end = end_year + end_month + end_day + end_time; 186 | var now = now_year + now_month + now_day + now_time; 187 | var Event = "\n BEGIN:VEVENT\n UID:".concat(UID, "@").concat(options.uidDomain, "\n ").concat(url ? 'URL:' + url : '', "\n DESCRIPTION:").concat(description).concat(rruleString ? '\n' + rruleString : '', "\n DTSTAMP;VALUE=DATE-TIME:").concat(now, ",\n DTSTART;VALUE=DATE-TIME:").concat(start, "\n DTEND;VALUE=DATE-TIME:").concat(end, "\n LOCATION:").concat(location, "\n ").concat(organizer ? 'ORGANIZER;CN=' + organizer.name + ':MAILTO:' + organizer.email : '', "\n SUMMARY;LANGUAGE=").concat(language, ":").concat(subject, "\n END:VEVENT\n "); 188 | Events.push(Event); 189 | return Event; 190 | }, 191 | 192 | /** 193 | * Returns calendar 194 | * @function 195 | * @return {string} Calendar in iCalendar format 196 | */ 197 | calendar: function calendar() { 198 | return addCRLF("\n BEGIN:VCALENDAR\n PRODID:".concat(options.prodId, "\n VERSION:2.0\n ").concat(Events.join('\n'), "\n END:VCALENDAR\n\n ").replace(/^\s*[\r\n]/gm, "").replace(/^\s+/gm, '')); 199 | }, 200 | 201 | /** 202 | * Download iCalendar file 203 | * @function 204 | * @param {string} filename - Name of the file without extension 205 | */ 206 | download: function download(filename) { 207 | var Calendar = addCRLF("\n BEGIN:VCALENDAR\n PRODID:".concat(options.prodId, "\n VERSION:2.0\n ").concat(Events.join('\n'), "\n END:VCALENDAR\n\n ").replace(/^\s*[\r\n]/gm, "").replace(/^\s+/gm, '')); 208 | var blob = new Blob([Calendar], { 209 | type: "text/x-vCalendar;charset=utf-8" 210 | }); 211 | fileSaver.saveAs(blob, "".concat(filename, ".ics")); 212 | } 213 | }; 214 | }; 215 | 216 | var plugin = { 217 | install: install, 218 | version: version 219 | }; 220 | 221 | if (typeof window !== 'undefined' && window.Vue) { 222 | window.Vue.use(plugin); 223 | } 224 | 225 | module.exports = plugin; 226 | -------------------------------------------------------------------------------- /dist/vue-ics.umd.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * vue-ics v0.1.3 3 | * (c) 2020 Stanislav Mihaylov 4 | * Released under the MIT License. 5 | */ 6 | (function (global, factory) { 7 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('file-saver')) : 8 | typeof define === 'function' && define.amd ? define(['file-saver'], factory) : 9 | (global = global || self, global.VueIcs = factory(global.fileSaver)); 10 | }(this, function (fileSaver) { 'use strict'; 11 | 12 | var version = '0.1.3'; 13 | /** 14 | * Reccurence rule 15 | * @typedef {Object} RRule 16 | * @property {string} freq - Required. The frequency of event recurrence. Can be DAILY, WEEKLY, MONTHLY, or YEARLY. 17 | * @property {string | number | Date} until - date stringA date string representing the date on which to end repitition. Must be friendly to Date() 18 | * @property {number} 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 19 | * @property {array} byday - Which days of the week the event is to occur. An array containing any of SU, MO, TU, WE, TH, FR, SA 20 | */ 21 | 22 | var Events = []; 23 | /** 24 | * Reccurence rule validation 25 | * @function 26 | * @param {RRule} rrule - Reccurence rule 27 | * @returns {boolean} 28 | */ 29 | 30 | function validateRepeatRule(rrule) { 31 | var counter = 0; 32 | var BYDAY_VALUES = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']; 33 | 34 | if (rrule.freq !== 'YEARLY' && rrule.freq !== 'MONTHLY' && rrule.freq !== 'WEEKLY' && rrule.freq !== 'DAILY') { 35 | counter += 1; 36 | throw "Recurrence rrule frequency must be provided and be one of the following: 'YEARLY', 'MONTHLY', 'WEEKLY', or 'DAILY'"; 37 | } 38 | 39 | if (rrule.until) { 40 | if (isNaN(Date.parse(rrule.until))) { 41 | counter += 1; 42 | throw "Recurrence rrule 'until' must be a valid date string"; 43 | } 44 | } 45 | 46 | if (rrule.interval) { 47 | if (isNaN(parseInt(rrule.interval))) { 48 | counter += 1; 49 | throw "Recurrence rrule 'interval' must be an integer"; 50 | } 51 | } 52 | 53 | if (rrule.count) { 54 | if (isNaN(parseInt(rrule.count))) { 55 | counter += 1; 56 | throw "Recurrence rrule 'count' must be an integer"; 57 | } 58 | } 59 | 60 | if (typeof rrule.byday !== 'undefined') { 61 | if (Object.prototype.toString.call(rrule.byday) !== '[object Array]') { 62 | counter += 1; 63 | throw "Recurrence rrule 'byday' must be an array"; 64 | } 65 | 66 | if (rrule.byday.length > 7) { 67 | counter += 1; 68 | throw "Recurrence rrule 'byday' array must not be longer than the 7 days in a week"; 69 | } 70 | 71 | rrule.byday = rrule.byday.filter(function (elem, pos) { 72 | return rrule.byday.indexOf(elem) == pos; 73 | }); 74 | 75 | for (var d in rrule.byday) { 76 | if (BYDAY_VALUES.indexOf(rrule.byday[d]) < 0) { 77 | counter += 1; 78 | throw "Recurrence rrule 'byday' values must include only the following: 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'"; 79 | } 80 | } 81 | } 82 | 83 | return counter === 0; 84 | } 85 | /** 86 | * Helper function for appending CRLF at start and end of file according to RFC rules. 87 | * @function 88 | * @param {string} string - iCalendar source string 89 | * @return {string} 90 | */ 91 | 92 | 93 | function addCRLF(string) { 94 | return "\n".concat(string, "\n"); 95 | } 96 | 97 | var counter = 0; 98 | 99 | function getUniqueNumber() { 100 | return counter++; 101 | } 102 | 103 | var install = function install(Vue) { 104 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { 105 | uidDomain: 'evildvl', 106 | prodId: 'vueICS' 107 | }; 108 | Vue.prototype.$ics = { 109 | /** 110 | * Add event to the calendar 111 | * @function 112 | * @param {string} language Language in format en-us (default) 113 | * @param {string} subject Subject/Title of event 114 | * @param {string} description Description of event 115 | * @param {string} location Location of event 116 | * @param {string} begin Beginning date of event 117 | * @param {string} stop Ending date of event 118 | * @param {string} url URL 119 | * @param {object} organizer Organizer 120 | * @param {RRule} rrule Reccurence rule 121 | * @returns {string} event 122 | **/ 123 | removeAllEvents: function removeAllEvents() { 124 | Events = []; 125 | }, 126 | addEvent: function addEvent() { 127 | var language = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'en-us'; 128 | var subject = arguments.length > 1 ? arguments[1] : undefined; 129 | var description = arguments.length > 2 ? arguments[2] : undefined; 130 | var location = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'none'; 131 | var begin = arguments.length > 4 ? arguments[4] : undefined; 132 | var stop = arguments.length > 5 ? arguments[5] : undefined; 133 | var url = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : null; 134 | var organizer = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null; 135 | var rrule = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null; 136 | var rruleString; 137 | 138 | if (typeof subject === 'undefined' || typeof description === 'undefined' || typeof location === 'undefined' || typeof begin === 'undefined' || typeof stop === 'undefined') { 139 | throw 'You need to specify function arguments'; 140 | } 141 | 142 | if (rrule && validateRepeatRule(rrule)) { 143 | rruleString = "RRULE:FREQ=".concat(rrule.freq); 144 | 145 | if (rrule.until) { 146 | var untilDate = new Date(Date.parse(rrule.until)).toISOString(); 147 | rruleString += ";UNTIL=".concat(untilDate.substring(0, untilDate.length - 13).replace(/[-]/g, ''), "000000Z"); 148 | } 149 | 150 | if (rrule.interval) rruleString += ";INTERVAL=".concat(rrule.interval); 151 | if (rrule.count) rruleString += ";COUNT=".concat(rrule.count); 152 | if (rrule.byday && rrule.byday.length > 0) rruleString += ";BYDAY=".concat(rrule.byday.join(',')); 153 | } 154 | 155 | var start_date = new Date(begin); 156 | var end_date = new Date(stop); 157 | var now_date = new Date(); 158 | var UID = "".concat(now_date.getDay()).concat(now_date.getMonth()).concat(now_date.getFullYear(), "-").concat(now_date.getHours()).concat(now_date.getMinutes()).concat(now_date.getSeconds()).concat(getUniqueNumber()); 159 | var start_year = "0000".concat(start_date.getFullYear().toString()).slice(-4); 160 | var start_month = "00".concat((start_date.getMonth() + 1).toString()).slice(-2); 161 | var start_day = "00".concat(start_date.getDate().toString()).slice(-2); 162 | var start_hours = "00".concat(start_date.getHours().toString()).slice(-2); 163 | var start_minutes = "00".concat(start_date.getMinutes().toString()).slice(-2); 164 | var start_seconds = "00".concat(start_date.getSeconds().toString()).slice(-2); 165 | var end_year = "0000".concat(end_date.getFullYear().toString()).slice(-4); 166 | var end_month = "00".concat((end_date.getMonth() + 1).toString()).slice(-2); 167 | var end_day = "00".concat(end_date.getDate().toString()).slice(-2); 168 | var end_hours = "00".concat(end_date.getHours().toString()).slice(-2); 169 | var end_minutes = "00".concat(end_date.getMinutes().toString()).slice(-2); 170 | var end_seconds = "00".concat(end_date.getSeconds().toString()).slice(-2); 171 | var now_year = "0000".concat(now_date.getFullYear().toString()).slice(-4); 172 | var now_month = "00".concat((now_date.getMonth() + 1).toString()).slice(-2); 173 | var now_day = "00".concat(now_date.getDate().toString()).slice(-2); 174 | var now_hours = "00".concat(now_date.getHours().toString()).slice(-2); 175 | var now_minutes = "00".concat(now_date.getMinutes().toString()).slice(-2); 176 | var now_seconds = "00".concat(now_date.getSeconds().toString()).slice(-2); 177 | var start_time = ''; 178 | var end_time = ''; 179 | 180 | if (start_hours + start_minutes + start_seconds + end_hours + end_minutes + end_seconds != 0) { 181 | start_time = "T".concat(start_hours).concat(start_minutes).concat(start_seconds); 182 | end_time = "T".concat(end_hours).concat(end_minutes).concat(end_seconds); 183 | } 184 | 185 | var now_time = "T".concat(now_hours).concat(now_minutes).concat(now_seconds); 186 | var start = start_year + start_month + start_day + start_time; 187 | var end = end_year + end_month + end_day + end_time; 188 | var now = now_year + now_month + now_day + now_time; 189 | var Event = "\n BEGIN:VEVENT\n UID:".concat(UID, "@").concat(options.uidDomain, "\n ").concat(url ? 'URL:' + url : '', "\n DESCRIPTION:").concat(description).concat(rruleString ? '\n' + rruleString : '', "\n DTSTAMP;VALUE=DATE-TIME:").concat(now, ",\n DTSTART;VALUE=DATE-TIME:").concat(start, "\n DTEND;VALUE=DATE-TIME:").concat(end, "\n LOCATION:").concat(location, "\n ").concat(organizer ? 'ORGANIZER;CN=' + organizer.name + ':MAILTO:' + organizer.email : '', "\n SUMMARY;LANGUAGE=").concat(language, ":").concat(subject, "\n END:VEVENT\n "); 190 | Events.push(Event); 191 | return Event; 192 | }, 193 | 194 | /** 195 | * Returns calendar 196 | * @function 197 | * @return {string} Calendar in iCalendar format 198 | */ 199 | calendar: function calendar() { 200 | return addCRLF("\n BEGIN:VCALENDAR\n PRODID:".concat(options.prodId, "\n VERSION:2.0\n ").concat(Events.join('\n'), "\n END:VCALENDAR\n\n ").replace(/^\s*[\r\n]/gm, "").replace(/^\s+/gm, '')); 201 | }, 202 | 203 | /** 204 | * Download iCalendar file 205 | * @function 206 | * @param {string} filename - Name of the file without extension 207 | */ 208 | download: function download(filename) { 209 | var Calendar = addCRLF("\n BEGIN:VCALENDAR\n PRODID:".concat(options.prodId, "\n VERSION:2.0\n ").concat(Events.join('\n'), "\n END:VCALENDAR\n\n ").replace(/^\s*[\r\n]/gm, "").replace(/^\s+/gm, '')); 210 | var blob = new Blob([Calendar], { 211 | type: "text/x-vCalendar;charset=utf-8" 212 | }); 213 | fileSaver.saveAs(blob, "".concat(filename, ".ics")); 214 | } 215 | }; 216 | }; 217 | 218 | var plugin = { 219 | install: install, 220 | version: version 221 | }; 222 | 223 | if (typeof window !== 'undefined' && window.Vue) { 224 | window.Vue.use(plugin); 225 | } 226 | 227 | return plugin; 228 | 229 | })); 230 | --------------------------------------------------------------------------------