├── .agignore
├── .editorconfig
├── .eslintrc
├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
└── stale.yml
├── .gitignore
├── .htmlhintrc
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bower.json
├── dist
├── css
│ ├── angular-bootstrap-calendar.css
│ ├── angular-bootstrap-calendar.min.css
│ └── angular-bootstrap-calendar.min.css.map
└── js
│ ├── angular-bootstrap-calendar-tpls.js
│ ├── angular-bootstrap-calendar-tpls.min.js
│ ├── angular-bootstrap-calendar-tpls.min.js.map
│ ├── angular-bootstrap-calendar.js
│ ├── angular-bootstrap-calendar.min.js
│ └── angular-bootstrap-calendar.min.js.map
├── docs
├── docs.css
├── docs.js
└── examples
│ ├── all-day-events
│ ├── javascript.js
│ └── markup.html
│ ├── badge-total
│ ├── javascript.js
│ └── markup.html
│ ├── cell-is-open
│ ├── javascript.js
│ └── markup.html
│ ├── cell-modifier
│ ├── javascript.js
│ └── markup.html
│ ├── clickable-events
│ ├── javascript.js
│ └── markup.html
│ ├── custom-event-class
│ ├── javascript.js
│ └── markup.html
│ ├── custom-templates
│ ├── javascript.js
│ └── markup.html
│ ├── day-view-all-times
│ ├── javascript.js
│ └── markup.html
│ ├── day-view-segment-size
│ ├── javascript.js
│ └── markup.html
│ ├── day-view-split
│ ├── javascript.js
│ └── markup.html
│ ├── day-view-start-end
│ ├── javascript.js
│ └── markup.html
│ ├── disable-tooltips
│ ├── javascript.js
│ └── markup.html
│ ├── disabling-views
│ ├── javascript.js
│ └── markup.html
│ ├── draggable-events
│ ├── javascript.js
│ └── markup.html
│ ├── draggable-external-events
│ ├── javascript.js
│ └── markup.html
│ ├── editable-deletable-events
│ ├── javascript.js
│ └── markup.html
│ ├── examples.json
│ ├── exclude-weekdays
│ ├── javascript.js
│ └── markup.html
│ ├── grouping-events
│ ├── javascript.js
│ └── markup.html
│ ├── helpers.js
│ ├── i18n
│ ├── javascript.js
│ └── markup.html
│ ├── kitchen-sink
│ ├── javascript.js
│ └── markup.html
│ ├── optional-event-end-dates
│ ├── javascript.js
│ └── markup.html
│ ├── recurring-events
│ ├── javascript.js
│ └── markup.html
│ ├── resizable-events
│ ├── javascript.js
│ └── markup.html
│ ├── select-range
│ ├── javascript.js
│ └── markup.html
│ ├── side-time-position
│ ├── javascript.js
│ └── markup.html
│ ├── slide-box-disabled
│ ├── javascript.js
│ └── markup.html
│ ├── timespan-click
│ ├── javascript.js
│ └── markup.html
│ └── view-change-click
│ ├── javascript.js
│ └── markup.html
├── index.html
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
├── directives
│ ├── mwlCalendar.js
│ ├── mwlCalendarDay.js
│ ├── mwlCalendarHourList.js
│ ├── mwlCalendarMonth.js
│ ├── mwlCalendarSlideBox.js
│ ├── mwlCalendarWeek.js
│ ├── mwlCalendarYear.js
│ ├── mwlCollapseFallback.js
│ ├── mwlDateModifier.js
│ ├── mwlDragSelect.js
│ ├── mwlDraggable.js
│ ├── mwlDroppable.js
│ ├── mwlDynamicDirectiveTemplate.js
│ ├── mwlElementDimensions.js
│ └── mwlResizable.js
├── entry.js
├── filters
│ ├── calendarDate.js
│ ├── calendarLimitTo.js
│ ├── calendarTruncateEventTitle.js
│ └── calendarTrustAsHtml.js
├── less
│ ├── calendar.less
│ ├── day.less
│ ├── events.less
│ ├── grid-mixin.less
│ ├── grid.less
│ ├── month.less
│ ├── theme.less
│ ├── variables.less
│ └── week.less
├── services
│ ├── calendarConfig.js
│ ├── calendarEventTitle.js
│ ├── calendarHelper.js
│ ├── calendarTitle.js
│ ├── interact.js
│ └── moment.js
└── templates
│ ├── calendar.html
│ ├── calendarDayView.html
│ ├── calendarHourList.html
│ ├── calendarMonthCell.html
│ ├── calendarMonthCellEvents.html
│ ├── calendarMonthView.html
│ ├── calendarSlideBox.html
│ ├── calendarWeekView.html
│ └── calendarYearView.html
├── test
├── .eslintrc
└── unit
│ ├── config.spec.js
│ ├── directives
│ ├── mwlCalendar.spec.js
│ ├── mwlCalendarDay.spec.js
│ ├── mwlCalendarHourList.spec.js
│ ├── mwlCalendarMonth.spec.js
│ ├── mwlCalendarSlideBox.spec.js
│ ├── mwlCalendarWeek.spec.js
│ ├── mwlCalendarYear.spec.js
│ ├── mwlCollapseFallback.spec.js
│ ├── mwlDateModifier.spec.js
│ ├── mwlDragSelect.spec.js
│ ├── mwlDraggable.spec.js
│ ├── mwlDroppable.spec.js
│ ├── mwlDynamicDirectiveTemplate.spec.js
│ ├── mwlElementDimensions.spec.js
│ └── mwlResizable.spec.js
│ ├── entry.js
│ ├── filters
│ ├── calendarDate.spec.js
│ ├── calendarLimitTo.spec.js
│ ├── calendarTruncateEventTitle.spec.js
│ └── calendarTrustAsHtml.spec.js
│ └── services
│ ├── calendarEventTitle.spec.js
│ ├── calendarHelper.spec.js
│ ├── calendarTitle.spec.js
│ ├── interact.spec.js
│ └── moment.spec.js
├── webpack.config.build.js
└── webpack.config.js
/.agignore:
--------------------------------------------------------------------------------
1 | build
2 | dist
3 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "mwl",
3 | "rules": {
4 | "angular/controller-name": [2, "/[A-Z].*Ctrl/"],
5 | "angular/deferred": 2,
6 | "angular/directive-name": [2, "mwl"],
7 | "angular/empty-controller": 2,
8 | "angular/di-unused": 2
9 | },
10 | "env": {
11 | "node": true
12 | },
13 | "plugins": [
14 | "angular"
15 | ],
16 | "globals": {
17 | "EXCLUDE_TEMPLATES": true
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Reporting issues
2 | Please read this guide before filing issues. Thanks!
3 |
4 | ### Support queries
5 | Please use the following sources for any questions on how to use the module:
6 | * The [examples list](http://mattlewis92.github.io/angular-bootstrap-calendar/) for demos of how to use all features of the directive.
7 | * Search the [existing issues list](https://github.com/mattlewis92/angular-bootstrap-calendar/issues?q=is%3Aissue+is%3Aclosed), most of the time there has already been a similar issue filed.
8 | * For everything else please use [stackoverflow](http://stackoverflow.com/questions/ask/advice) or [gitter](https://gitter.im/mattlewis92/angular-bootstrap-calendar). The github issue tracker is primarily for bug reports + feature requests.
9 |
10 | ### Bug reports
11 | Please include a plunker or something similar that clearly reproduces your problem or your issue may be closed. You can use this as a [starter template](http://plnkr.co/edit/LE4F4U7AnnD3tjM9ZH4G?p=preview).
12 |
13 | ### Feature requests
14 | Please clearly describe what you require and your use case. A lot of the time you can use the existing API hooks + custom templates to add your own features. Please check the [examples list](http://mattlewis92.github.io/angular-bootstrap-calendar/) first to see if the feature you've requested has already been implemented. Features that can easily be implemented in userland are unlikely to be accepted.
15 |
16 | ### Pull requests
17 | Follow the [dev guide](https://github.com/mattlewis92/angular-bootstrap-calendar#development) for getting up and running. Please ensure all tests pass first by running `npm test`. This project uses the [angular commit conventions](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format). You can use the commit wizard for easily generating commit messages by running `npm run commit`.
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
6 | ### Bug description / Feature request:
7 |
8 |
9 | ### Link to minimally-working plunker that reproduces the issue (starter template: http://plnkr.co/edit/LE4F4U7AnnD3tjM9ZH4G?p=preview)
10 |
11 |
12 | ### Versions
13 |
14 | Angular:
15 |
16 | Calendar library:
17 |
18 | Browser name and version:
19 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | daysUntilStale: 60
3 | # Number of days of inactivity before a stale issue is closed
4 | daysUntilClose: 7
5 | # Issues with these labels will never be considered stale
6 | exemptLabels:
7 | - pinned
8 | - security
9 | # Label to use when marking an issue as stale
10 | staleLabel: wontfix
11 | # Comment to post when marking an issue as stale. Set to `false` to disable
12 | markComment: >
13 | This issue has been automatically marked as stale because it has not had
14 | recent activity. It will be closed if no further activity occurs. Thank you
15 | for your contributions.
16 | # Comment to post when closing a stale issue. Set to `false` to disable
17 | closeComment: false
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .tmp
3 | .idea
4 | build
5 | coverage
6 | *.log
7 |
--------------------------------------------------------------------------------
/.htmlhintrc:
--------------------------------------------------------------------------------
1 | {
2 | "tagname-lowercase": true,
3 | "attr-lowercase": true,
4 | "attr-value-double-quotes": true,
5 | "attr-value-not-empty": false,
6 | "attr-no-duplication": true,
7 | "doctype-first": false,
8 | "tag-pair": true,
9 | "tag-self-close": false,
10 | "spec-char-escape": true,
11 | "id-unique": true,
12 | "src-not-empty": true,
13 | "doctype-html5": true,
14 | "space-tab-mixed-disabled": true
15 | }
16 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - '8'
5 |
6 | script: npm test
7 |
8 | notifications:
9 | email: false
10 |
11 | env:
12 | - CI=true
13 |
14 | after_success:
15 | - npm run codecov
16 |
17 | cache:
18 | directories:
19 | - node_modules
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-bootstrap-calendar",
3 | "version": "1.0.0",
4 | "homepage": "https://github.com/mattlewis92/angular-bootstrap-calendar",
5 | "authors": [
6 | "Matt Lewis"
7 | ],
8 | "description": "A pure AngularJS bootstrap themed responsive calendar that can display events and has views for year, month, week and day",
9 | "main": [
10 | "dist/js/angular-bootstrap-calendar-tpls.js",
11 | "dist/css/angular-bootstrap-calendar.css"
12 | ],
13 | "keywords": [
14 | "AngularJS",
15 | "Angular",
16 | "Bootstrap",
17 | "Responsive",
18 | "Calendar"
19 | ],
20 | "license": "MIT",
21 | "ignore": [
22 | "**/.*",
23 | "node_modules",
24 | "test",
25 | "src/**/*.js",
26 | ".github",
27 | "docs",
28 | "index.html",
29 | "karma.conf.js",
30 | "webpack.config.build.js",
31 | "webpack.config.js"
32 | ],
33 | "dependencies": {
34 | "angular": ">=1.3.0",
35 | "moment": "^2.10.2",
36 | "bootstrap": "^3.3.4"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/docs/docs.css:
--------------------------------------------------------------------------------
1 | .content { padding-top: 70px; }
2 |
3 | /* Everything but the jumbotron gets side spacing for mobile first views */
4 | .header,
5 | .marketing,
6 | .footer {
7 | padding-left: 15px;
8 | padding-right: 15px;
9 | }
10 |
11 | /* Custom page header */
12 | .header {
13 | border-bottom: 1px solid #e5e5e5;
14 | }
15 | /* Make the masthead heading the same height as the navigation */
16 | .header h3 {
17 | margin-top: 0;
18 | margin-bottom: 0;
19 | line-height: 40px;
20 | padding-bottom: 19px;
21 | }
22 |
23 | h3 {
24 | margin-top: 0;
25 | margin-bottom: 20px;
26 | }
27 |
28 | .tab-pane {
29 | margin-top: 10px;
30 | }
31 |
32 | .sidebar-nav li {
33 | margin-bottom: 5px;
34 | }
35 |
36 | .sidebar-nav a:not(.active) {
37 | color: #777;
38 | }
39 |
40 | /* Custom page footer */
41 | .footer {
42 | padding-top: 19px;
43 | color: #777;
44 | border-top: 1px solid #e5e5e5;
45 | }
46 |
47 | /* Customize container */
48 | @media (min-width: 768px) {
49 | .container {
50 | max-width: 730px;
51 | }
52 | }
53 | .container-narrow > hr {
54 | margin: 30px 0;
55 | }
56 |
57 | /* Main marketing message and sign up button */
58 | .jumbotron {
59 | text-align: center;
60 | border-bottom: 1px solid #e5e5e5;
61 | }
62 | .jumbotron .btn {
63 | font-size: 21px;
64 | padding: 14px 24px;
65 | }
66 |
67 | /* Supporting marketing content */
68 | .marketing {
69 | margin: 40px 0;
70 | }
71 | .marketing p + h4 {
72 | margin-top: 28px;
73 | }
74 |
75 | /* Responsive: Portrait tablets and up */
76 | @media screen and (min-width: 768px) {
77 | /* Remove the padding we set earlier */
78 | .header,
79 | .marketing,
80 | .footer {
81 | padding-left: 0;
82 | padding-right: 0;
83 | }
84 | /* Space out the masthead */
85 | .header {
86 | margin-bottom: 30px;
87 | }
88 | /* Remove the bottom border on the jumbotron for visual effect */
89 | .jumbotron {
90 | border-bottom: 0;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/docs/docs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular
4 | .module('mwl.calendar.docs', ['mwl.calendar', 'ui.bootstrap', 'ngTouch', 'ngAnimate', 'oc.lazyLoad', 'hljs', 'colorpicker.module'])
5 | .controller('ExamplesCtrl', function($http, $rootScope, $compile, $q, $location, $ocLazyLoad, plunkGenerator, moment) {
6 |
7 | var vm = this;
8 |
9 | function loadFile(path) {
10 | return $http.get(path, {
11 | transformResponse: function(data) {
12 | return data;
13 | }
14 | });
15 | }
16 |
17 | var helpers = {
18 | templates: [
19 | 'modalContent.html',
20 | 'calendarControls.html'
21 | ]
22 | };
23 |
24 | loadFile('docs/examples/helpers.js').then(function(result) {
25 | helpers.scripts = result.data;
26 | });
27 |
28 | var previousScope;
29 |
30 | vm.loadExample = function(demo) {
31 | vm.activeExample = angular.copy(demo);
32 | vm.showDemoTab = true;
33 | $location.search('example', demo.key);
34 | var scriptPath = 'docs/examples/' + demo.key + '/javascript.js';
35 | var markupPath = 'docs/examples/' + demo.key + '/markup.html';
36 |
37 | loadFile(scriptPath).then(function(result) {
38 | vm.activeExample.javascript = result.data;
39 | });
40 |
41 | $q.all({
42 | markup: loadFile(markupPath),
43 | script: $ocLazyLoad.load(scriptPath)
44 | }).then(function(result) {
45 | vm.activeExample.markup = result.markup.data;
46 | var demoContainer = angular.element(document.getElementById('demoContainer'));
47 | demoContainer.html(vm.activeExample.markup);
48 | var scope = $rootScope.$new();
49 | $compile(demoContainer)(scope);
50 | if (previousScope) {
51 | previousScope.$destroy();
52 | }
53 | previousScope = scope;
54 | });
55 |
56 | };
57 |
58 | vm.editActiveExample = function() {
59 | plunkGenerator(angular.version.full, '3', '2', moment.version, helpers, vm.activeExample);
60 | };
61 |
62 | $http.get('docs/examples/examples.json').then(function(result) {
63 | vm.examples = result.data;
64 | if ($location.search().example) {
65 | var exampleToLoad = vm.examples.filter(function(example) {
66 | return example.key === $location.search().example;
67 | })[0];
68 | vm.loadExample(exampleToLoad);
69 | } else {
70 | vm.loadExample(vm.examples[0]);
71 | }
72 | });
73 |
74 | })
75 | .factory('plunkGenerator', function($templateCache, $window) {
76 |
77 | return function(ngVersion, bsVersion, uibVersion, momentVersion, helpers, content) {
78 |
79 | var scriptContent = function(content) {
80 | return "angular.module('mwl.calendar.docs', ['mwl.calendar', 'ngAnimate', 'ui.bootstrap', 'colorpicker.module']);" + "\n" + content;
81 | };
82 |
83 | $window.createPlunker.Plunker.create()
84 | .setDescription('http://mattlewis92.github.io/angular-bootstrap-calendar/')
85 | .addIndexHtmlAttribute('ng-app', 'mwl.calendar.docs')
86 | .addNpmPackage('moment', {version: momentVersion})
87 | .addNpmPackage('interactjs', {version: 1})
88 | .addNpmPackage('angular', {version: ngVersion, filename: 'angular.js'})
89 | .addNpmPackage('angular-animate', {version: ngVersion, filename: 'angular-animate.js'})
90 | .addNpmPackage('angular-ui-bootstrap', {version: uibVersion, filename: 'dist/ui-bootstrap-tpls.js'})
91 | .addNpmPackage('rrule', {version: 2})
92 | .addNpmPackage('angular-bootstrap-colorpicker', {version: 3})
93 | .addNpmPackage('angular-bootstrap-calendar')
94 | .addNpmPackage('bootstrap', {filename: 'dist/css/bootstrap.css', version: bsVersion})
95 | .addNpmPackage('angular-bootstrap-colorpicker', {version: 3, filename: 'css/colorpicker.min.css'})
96 | .addNpmPackage('angular-bootstrap-calendar', {filename: 'dist/css/angular-bootstrap-calendar.min.css'})
97 | .addFile({name: 'example.js', contents: scriptContent(content.javascript)})
98 | .addFile({name: 'helpers.js', contents: helpers.scripts})
99 | .setIndexBody(content.markup)
100 | .addFiles(helpers.templates.map(function(templateName) {
101 | return {name: templateName, contents: $templateCache.get(templateName)};
102 | }))
103 | .save();
104 | };
105 | })
106 | .config(function($touchProvider) {
107 | $touchProvider.ngClickOverrideEnabled(true);
108 | });
109 |
--------------------------------------------------------------------------------
/docs/examples/all-day-events/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('AllDayEventsCtrl', function(moment, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [
8 | {
9 | title: 'An all day event',
10 | color: calendarConfig.colorTypes.warning,
11 | startsAt: moment().startOf('week').subtract(2, 'days').add(8, 'hours').toDate(),
12 | endsAt: moment().startOf('week').add(1, 'week').add(9, 'hours').toDate(),
13 | allDay: true
14 | }, {
15 | title: 'A non all day event',
16 | color: calendarConfig.colorTypes.important,
17 | startsAt: moment().startOf('day').add(7, 'hours').toDate(),
18 | endsAt: moment().startOf('day').add(19, 'hours').toDate(),
19 | draggable: true,
20 | resizable: true
21 | }
22 | ];
23 |
24 | vm.calendarView = 'day';
25 | vm.viewDate = moment().toDate();
26 |
27 | });
28 |
--------------------------------------------------------------------------------
/docs/examples/all-day-events/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/docs/examples/badge-total/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('BadgeTotalCtrl', function(moment, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [
8 | {
9 | title: 'Increments the badge total on the day cell',
10 | color: calendarConfig.colorTypes.warning,
11 | startsAt: moment().startOf('month').toDate(),
12 | incrementsBadgeTotal: true
13 | },
14 | {
15 | title: 'Does not increment the badge total ',
16 | color: calendarConfig.colorTypes.info,
17 | startsAt: moment().startOf('month').toDate(),
18 | incrementsBadgeTotal: false
19 | }
20 | ];
21 |
22 | vm.calendarView = 'month';
23 | vm.viewDate = moment().startOf('month').toDate();
24 | vm.cellIsOpen = true;
25 |
26 | });
27 |
--------------------------------------------------------------------------------
/docs/examples/badge-total/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/examples/cell-is-open/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('CellIsOpenCtrl', function(moment, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [
8 | {
9 | title: 'Draggable event',
10 | color: calendarConfig.colorTypes.warning,
11 | startsAt: moment().startOf('month').toDate()
12 | },
13 | {
14 | title: 'Non-draggable event',
15 | color: calendarConfig.colorTypes.info,
16 | startsAt: moment().startOf('month').add(1, 'day').toDate()
17 | }
18 | ];
19 |
20 | vm.calendarView = 'month';
21 | vm.viewDate = moment().startOf('month').toDate();
22 | vm.cellIsOpen = true;
23 |
24 | vm.timespanClicked = function(date, cell) {
25 |
26 | if (vm.calendarView === 'month') {
27 | if ((vm.cellIsOpen && moment(date).startOf('day').isSame(moment(vm.viewDate).startOf('day'))) || cell.events.length === 0 || !cell.inMonth) {
28 | vm.cellIsOpen = false;
29 | } else {
30 | vm.cellIsOpen = true;
31 | vm.viewDate = date;
32 | }
33 | } else if (vm.calendarView === 'year') {
34 | if ((vm.cellIsOpen && moment(date).startOf('month').isSame(moment(vm.viewDate).startOf('month'))) || cell.events.length === 0) {
35 | vm.cellIsOpen = false;
36 | } else {
37 | vm.cellIsOpen = true;
38 | vm.viewDate = date;
39 | }
40 | }
41 |
42 | };
43 |
44 | });
45 |
--------------------------------------------------------------------------------
/docs/examples/cell-is-open/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | The slidebox is open closed .
4 |
5 |
6 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/docs/examples/cell-modifier/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('CellModifierCtrl', function(moment) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [];
8 | vm.calendarView = 'month';
9 | vm.viewDate = moment().startOf('month').toDate();
10 |
11 | vm.cellModifier = function(cell) {
12 | console.log(cell);
13 | if (cell.label % 2 === 1 && cell.inMonth) {
14 | cell.cssClass = 'odd-cell';
15 | }
16 | cell.label = '-' + cell.label + '-';
17 | };
18 |
19 | });
20 |
--------------------------------------------------------------------------------
/docs/examples/cell-modifier/markup.html:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/docs/examples/clickable-events/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('ClickableEventsCtrl', function(moment, alert, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [
8 | {
9 | title: 'Click me',
10 | color: calendarConfig.colorTypes.warning,
11 | startsAt: moment().startOf('month').toDate()
12 | },
13 | {
14 | title: 'Or click me',
15 | color: calendarConfig.colorTypes.info,
16 | startsAt: moment().startOf('month').toDate()
17 | }
18 | ];
19 |
20 | vm.calendarView = 'month';
21 | vm.viewDate = moment().startOf('month').toDate();
22 | vm.cellIsOpen = true;
23 |
24 | vm.eventClicked = function(event) {
25 | alert.show('Clicked', event);
26 | };
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/docs/examples/clickable-events/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/examples/custom-event-class/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('CustomEventClassCtrl', function(moment, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [
8 | {
9 | title: 'Has custom class',
10 | color: calendarConfig.colorTypes.warning,
11 | startsAt: moment().startOf('month').toDate(),
12 | cssClass: 'my-custom-class'
13 | }
14 | ];
15 |
16 | vm.calendarView = 'month';
17 | vm.viewDate = moment().startOf('month').toDate();
18 | vm.cellIsOpen = true;
19 |
20 | });
21 |
--------------------------------------------------------------------------------
/docs/examples/custom-event-class/markup.html:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/docs/examples/custom-templates/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('CustomTemplatesCtrl', function($scope, moment) {
4 |
5 | var vm = this;
6 | vm.events = [];
7 | vm.calendarView = 'month';
8 | vm.viewDate = moment().startOf('month').toDate();
9 | vm.cellModifier = function(cell) {
10 | cell.cssClass = 'custom-template-cell';
11 | };
12 | });
13 |
--------------------------------------------------------------------------------
/docs/examples/custom-templates/markup.html:
--------------------------------------------------------------------------------
1 |
17 |
18 |
23 |
24 |
25 |
26 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/docs/examples/day-view-all-times/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('DayViewAllTimesCtrl', function($scope, moment, calendarConfig) {
4 |
5 | var vm = this;
6 | vm.events = [];
7 | vm.calendarView = 'day';
8 | vm.viewDate = moment().startOf('month').toDate();
9 | var originalFormat = calendarConfig.dateFormats.hour;
10 | calendarConfig.dateFormats.hour = 'HH:mm';
11 |
12 | $scope.$on('$destroy', function() {
13 | calendarConfig.dateFormats.hour = originalFormat; // reset for other demos
14 | });
15 |
16 | });
17 |
--------------------------------------------------------------------------------
/docs/examples/day-view-all-times/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
7 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/examples/day-view-segment-size/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('DayViewStartEndCtrl', function(moment) {
4 |
5 | var vm = this;
6 | vm.events = [];
7 | vm.calendarView = 'day';
8 | vm.viewDate = moment().startOf('month').toDate();
9 |
10 | // note that this class is required to set the hour part height in the css
11 | vm.cellModifier = function(cell) {
12 | cell.cssClass = 'my-custom-class';
13 | }
14 |
15 | });
16 |
--------------------------------------------------------------------------------
/docs/examples/day-view-segment-size/markup.html:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/docs/examples/day-view-split/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('DayViewSplitCtrl', function(moment) {
4 |
5 | var vm = this;
6 | vm.events = [];
7 | vm.calendarView = 'day';
8 | vm.viewDate = moment().startOf('month').toDate();
9 |
10 | });
11 |
--------------------------------------------------------------------------------
/docs/examples/day-view-split/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/examples/day-view-start-end/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('DayViewStartEndCtrl', function(moment) {
4 |
5 | var vm = this;
6 | vm.events = [];
7 | vm.calendarView = 'day';
8 | vm.viewDate = moment().startOf('month').toDate();
9 |
10 | });
11 |
--------------------------------------------------------------------------------
/docs/examples/day-view-start-end/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/examples/disable-tooltips/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('DisableTooltipsCtrl', function($scope, moment, calendarConfig, calendarEventTitle) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [{
8 | title: 'An event',
9 | color: calendarConfig.colorTypes.warning,
10 | startsAt: moment().startOf('week').subtract(2, 'days').add(8, 'hours').toDate(),
11 | endsAt: moment().startOf('week').add(1, 'week').add(9, 'hours').toDate(),
12 | }];
13 | vm.calendarView = 'month';
14 | vm.viewDate = moment().startOf('month').toDate();
15 |
16 | var originalEventTitle = angular.copy(calendarEventTitle);
17 |
18 | calendarEventTitle.monthViewTooltip = calendarEventTitle.weekViewTooltip = calendarEventTitle.dayViewTooltip = function() {
19 | return '';
20 | };
21 |
22 | // required so other demos work as before
23 | $scope.$on('$destroy', function() {
24 | angular.extend(calendarEventTitle, originalEventTitle);
25 | });
26 |
27 | });
28 |
--------------------------------------------------------------------------------
/docs/examples/disable-tooltips/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
{{ vm.viewTitle }}
3 |
4 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/examples/disabling-views/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('DisableViewsCtrl', function(moment) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [];
8 | vm.calendarView = 'year';
9 | vm.viewDate = moment().startOf('month').toDate();
10 |
11 | vm.viewChangeClicked = function(nextView) {
12 | if (nextView === 'month') {
13 | return false;
14 | }
15 | };
16 |
17 | });
18 |
--------------------------------------------------------------------------------
/docs/examples/disabling-views/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Year
5 | Week
6 | Day
7 |
8 |
9 |
10 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/docs/examples/draggable-events/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('DraggableEventsCtrl', function(moment, alert, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [
8 | {
9 | title: 'Draggable event',
10 | color: calendarConfig.colorTypes.warning,
11 | startsAt: moment().startOf('month').toDate(),
12 | draggable: true
13 | },
14 | {
15 | title: 'Non-draggable event',
16 | color: calendarConfig.colorTypes.info,
17 | startsAt: moment().startOf('month').toDate(),
18 | draggable: false
19 | }
20 | ];
21 |
22 | vm.calendarView = 'month';
23 | vm.viewDate = moment().startOf('month').toDate();
24 | vm.cellIsOpen = true;
25 |
26 | vm.eventTimesChanged = function(event) {
27 | vm.viewDate = event.startsAt;
28 | alert.show('Dragged and dropped', event);
29 | };
30 |
31 | vm.timespanClicked = function(date, cell) {
32 |
33 | if (vm.calendarView === 'month') {
34 | if ((vm.cellIsOpen && moment(date).startOf('day').isSame(moment(vm.viewDate).startOf('day'))) || cell.events.length === 0 || !cell.inMonth) {
35 | vm.cellIsOpen = false;
36 | } else {
37 | vm.cellIsOpen = true;
38 | vm.viewDate = date;
39 | }
40 | } else if (vm.calendarView === 'year') {
41 | if ((vm.cellIsOpen && moment(date).startOf('month').isSame(moment(vm.viewDate).startOf('month'))) || cell.events.length === 0) {
42 | vm.cellIsOpen = false;
43 | } else {
44 | vm.cellIsOpen = true;
45 | vm.viewDate = date;
46 | }
47 | }
48 |
49 | };
50 |
51 | });
52 |
--------------------------------------------------------------------------------
/docs/examples/draggable-events/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/docs/examples/draggable-external-events/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('DraggableExternalEventsCtrl', function(moment, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [];
8 |
9 | vm.externalEvents = [
10 | {
11 | title: 'Event 1',
12 | type: 'warning',
13 | color: calendarConfig.colorTypes.warning,
14 | startsAt: moment().startOf('month').toDate(),
15 | draggable: true
16 | },
17 | {
18 | title: 'Event 2',
19 | type: 'danger',
20 | color: calendarConfig.colorTypes.important,
21 | startsAt: moment().startOf('month').toDate(),
22 | draggable: true
23 | }
24 | ];
25 |
26 | vm.calendarView = 'month';
27 | vm.viewDate = moment().startOf('month').toDate();
28 | vm.cellIsOpen = false;
29 |
30 | vm.eventDropped = function(event, start, end) {
31 | var externalIndex = vm.externalEvents.indexOf(event);
32 | if (externalIndex > -1) {
33 | vm.externalEvents.splice(externalIndex, 1);
34 | vm.events.push(event);
35 | }
36 | event.startsAt = start;
37 | if (end) {
38 | event.endsAt = end;
39 | }
40 | vm.viewDate = start;
41 | vm.cellIsOpen = true;
42 | };
43 |
44 | });
45 |
--------------------------------------------------------------------------------
/docs/examples/draggable-external-events/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
20 |
21 |
22 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/docs/examples/editable-deletable-events/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('EditableDeletableEventsCtrl', function(moment, alert, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [
8 | {
9 | title: 'Editable event',
10 | color: calendarConfig.colorTypes.warning,
11 | startsAt: moment().startOf('month').toDate(),
12 | actions: [{
13 | label: ' ',
14 | onClick: function(args) {
15 | alert.show('Edited', args.calendarEvent);
16 | }
17 | }]
18 | }, {
19 | title: 'Deletable event',
20 | color: calendarConfig.colorTypes.info,
21 | startsAt: moment().startOf('month').toDate(),
22 | actions: [{
23 | label: ' ',
24 | onClick: function(args) {
25 | alert.show('Deleted', args.calendarEvent);
26 | }
27 | }]
28 | }, {
29 | title: 'Non editable and deletable event',
30 | color: calendarConfig.colorTypes.important,
31 | startsAt: moment().startOf('month').toDate()
32 | }
33 | ];
34 |
35 | vm.calendarView = 'month';
36 | vm.viewDate = moment().startOf('month').toDate();
37 | vm.cellIsOpen = true;
38 |
39 | });
40 |
--------------------------------------------------------------------------------
/docs/examples/editable-deletable-events/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/examples/examples.json:
--------------------------------------------------------------------------------
1 | [{
2 | "key": "kitchen-sink",
3 | "label": "Kitchen sink"
4 | }, {
5 | "key": "optional-event-end-dates",
6 | "label": "Optional event end dates"
7 | }, {
8 | "key": "editable-deletable-events",
9 | "label": "Editable / deletable events"
10 | }, {
11 | "key": "draggable-events",
12 | "label": "Draggable events"
13 | }, {
14 | "key": "resizable-events",
15 | "label": "Resizable events"
16 | }, {
17 | "key": "badge-total",
18 | "label": "Badge total"
19 | }, {
20 | "key": "recurring-events",
21 | "label": "Recurring events"
22 | }, {
23 | "key": "custom-event-class",
24 | "label": "Custom event class"
25 | }, {
26 | "key": "clickable-events",
27 | "label": "Clickable events"
28 | }, {
29 | "key": "timespan-click",
30 | "label": "Timespan click"
31 | }, {
32 | "key": "select-range",
33 | "label": "Select range"
34 | }, {
35 | "key": "cell-is-open",
36 | "label": "Slidebox is open"
37 | }, {
38 | "key": "day-view-start-end",
39 | "label": "Day view start / end time"
40 | }, {
41 | "key": "exclude-weekdays",
42 | "label": "Exclude days"
43 | }, {
44 | "key": "day-view-split",
45 | "label": "Day view minute split"
46 | }, {
47 | "key": "view-change-click",
48 | "label": "Navigating between views"
49 | }, {
50 | "key": "cell-modifier",
51 | "label": "Cell modifier"
52 | }, {
53 | "key": "custom-templates",
54 | "label": "Custom templates"
55 | }, {
56 | "key": "i18n",
57 | "label": "Internationalization"
58 | }, {
59 | "key": "disabling-views",
60 | "label": "Disabling views"
61 | }, {
62 | "key": "draggable-external-events",
63 | "label": "Draggable external events"
64 | }, {
65 | "key": "slide-box-disabled",
66 | "label": "Disable the slidebox"
67 | }, {
68 | "key": "grouping-events",
69 | "label": "Group month view events"
70 | }, {
71 | "key": "all-day-events",
72 | "label": "All day events"
73 | }, {
74 | "key": "day-view-all-times",
75 | "label": "Show all times on day view"
76 | }, {
77 | "key": "side-time-position",
78 | "label": "Show time on the side of calendar"
79 | }, {
80 | "key": "disable-tooltips",
81 | "label": "Disable tooltips"
82 | }, {
83 | "key": "day-view-segment-size",
84 | "label": "Custom hour segment size in day view"
85 | }]
86 |
--------------------------------------------------------------------------------
/docs/examples/exclude-weekdays/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('ExcludeWeekdays', function(moment, calendarConfig) {
4 |
5 | var vm = this;
6 | vm.events = [
7 | {
8 | title: 'An event',
9 | color: calendarConfig.colorTypes.warning,
10 | startsAt: moment().startOf('week').subtract(2, 'days').add(8, 'hours').toDate(),
11 | endsAt: moment().startOf('week').add(1, 'week').add(9, 'hours').toDate()
12 | }, {
13 | title: ' Another event , with a html title',
14 | color: calendarConfig.colorTypes.info,
15 | startsAt: moment().subtract(1, 'day').toDate(),
16 | endsAt: moment().add(5, 'days').toDate()
17 | }, {
18 | title: 'This is a really long event title that occurs on every year',
19 | color: calendarConfig.colorTypes.important,
20 | startsAt: moment().startOf('day').add(7, 'hours').toDate(),
21 | endsAt: moment().startOf('day').add(19, 'hours').toDate()
22 | }
23 | ];
24 | vm.calendarView = 'week';
25 | vm.viewDate = new Date();
26 | vm.excludedDays = [0, 6];
27 |
28 | });
29 |
--------------------------------------------------------------------------------
/docs/examples/exclude-weekdays/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
{{ vm.calendarTitle }}
3 |
4 |
5 |
6 |
7 |
8 |
15 | Previous
16 |
17 |
23 | Today
24 |
25 |
32 | Next
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | Month
42 | Week
43 | Day
44 |
45 |
46 |
47 |
48 |
49 |
50 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/docs/examples/grouping-events/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('GroupingEventsCtrl', function($scope, moment, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | calendarConfig.templates.calendarMonthCell = 'groupedMonthEvents.html';
8 |
9 | $scope.$on('$destroy', function() {
10 | calendarConfig.templates.calendarMonthCell = 'mwl/calendarMonthCell.html';
11 | });
12 |
13 | vm.events = [
14 | {
15 | title: 'Event 1',
16 | type: 'warning',
17 | color: calendarConfig.colorTypes.warning,
18 | startsAt: moment().startOf('month').toDate()
19 | }, {
20 | title: 'Event 2',
21 | type: 'info',
22 | color: calendarConfig.colorTypes.info,
23 | startsAt: moment().startOf('month').toDate()
24 | }, {
25 | title: 'Event 3',
26 | type: 'info',
27 | color: calendarConfig.colorTypes.info,
28 | startsAt: moment().startOf('month').toDate()
29 | }, {
30 | title: 'Event 4',
31 | type: 'danger',
32 | color: calendarConfig.colorTypes.important,
33 | startsAt: moment().startOf('month').toDate()
34 | }, {
35 | title: 'Event 5',
36 | type: 'success',
37 | color: calendarConfig.colorTypes.success,
38 | startsAt: moment().startOf('month').toDate()
39 | }
40 | ];
41 |
42 | vm.calendarView = 'month';
43 | vm.viewDate = moment().startOf('month').toDate();
44 | vm.cellIsOpen = true;
45 |
46 | vm.groupEvents = function(cell) {
47 | cell.groups = {};
48 | cell.events.forEach(function(event) {
49 | cell.groups[event.type] = cell.groups[event.type] || [];
50 | cell.groups[event.type].push(event);
51 | });
52 | };
53 |
54 | });
55 |
--------------------------------------------------------------------------------
/docs/examples/grouping-events/markup.html:
--------------------------------------------------------------------------------
1 |
37 |
38 |
39 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/docs/examples/helpers.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .factory('alert', function($uibModal) {
4 |
5 | function show(action, event) {
6 | return $uibModal.open({
7 | templateUrl: 'modalContent.html',
8 | controller: function() {
9 | var vm = this;
10 | vm.action = action;
11 | vm.event = event;
12 | },
13 | controllerAs: 'vm'
14 | });
15 | }
16 |
17 | return {
18 | show: show
19 | };
20 |
21 | });
22 |
--------------------------------------------------------------------------------
/docs/examples/i18n/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('i18nCtrl', function($scope, $window, $ocLazyLoad, calendarConfig, moment) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [];
8 | vm.calendarView = 'month';
9 | vm.viewDate = moment().startOf('month').toDate();
10 |
11 | calendarConfig.dateFormatter = 'moment'; // use moment instead of angular for formatting dates
12 | var originali18n = angular.copy(calendarConfig.i18nStrings);
13 | calendarConfig.i18nStrings.weekNumber = 'Semaine {week}';
14 |
15 | $window.moment = $window.moment || moment;
16 | $ocLazyLoad.load('https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/locale/fr.js').then(function() {
17 | moment.locale('fr', {
18 | week: {
19 | dow: 1 // Monday is the first day of the week
20 | }
21 | });
22 | moment.locale('fr'); // change the locale to french
23 | });
24 |
25 | $scope.$on('$destroy', function() {
26 | moment.locale('en');
27 | calendarConfig.i18nStrings = originali18n;
28 | });
29 |
30 | });
31 |
--------------------------------------------------------------------------------
/docs/examples/i18n/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/examples/kitchen-sink/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs') //you will need to declare your module with the dependencies ['mwl.calendar', 'ui.bootstrap', 'ngAnimate']
3 | .controller('KitchenSinkCtrl', function(moment, alert, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | //These variables MUST be set as a minimum for the calendar to work
8 | vm.calendarView = 'month';
9 | vm.viewDate = new Date();
10 | var actions = [{
11 | label: ' ',
12 | onClick: function(args) {
13 | alert.show('Edited', args.calendarEvent);
14 | }
15 | }, {
16 | label: ' ',
17 | onClick: function(args) {
18 | alert.show('Deleted', args.calendarEvent);
19 | }
20 | }];
21 | vm.events = [
22 | {
23 | title: 'An event',
24 | color: calendarConfig.colorTypes.warning,
25 | startsAt: moment().startOf('week').subtract(2, 'days').add(8, 'hours').toDate(),
26 | endsAt: moment().startOf('week').add(1, 'week').add(9, 'hours').toDate(),
27 | draggable: true,
28 | resizable: true,
29 | actions: actions
30 | }, {
31 | title: ' Another event , with a html title',
32 | color: calendarConfig.colorTypes.info,
33 | startsAt: moment().subtract(1, 'day').toDate(),
34 | endsAt: moment().add(5, 'days').toDate(),
35 | draggable: true,
36 | resizable: true,
37 | actions: actions
38 | }, {
39 | title: 'This is a really long event title that occurs on every year',
40 | color: calendarConfig.colorTypes.important,
41 | startsAt: moment().startOf('day').add(7, 'hours').toDate(),
42 | endsAt: moment().startOf('day').add(19, 'hours').toDate(),
43 | recursOn: 'year',
44 | draggable: true,
45 | resizable: true,
46 | actions: actions
47 | }
48 | ];
49 |
50 | vm.cellIsOpen = true;
51 |
52 | vm.addEvent = function() {
53 | vm.events.push({
54 | title: 'New event',
55 | startsAt: moment().startOf('day').toDate(),
56 | endsAt: moment().endOf('day').toDate(),
57 | color: calendarConfig.colorTypes.important,
58 | draggable: true,
59 | resizable: true
60 | });
61 | };
62 |
63 | vm.eventClicked = function(event) {
64 | alert.show('Clicked', event);
65 | };
66 |
67 | vm.eventEdited = function(event) {
68 | alert.show('Edited', event);
69 | };
70 |
71 | vm.eventDeleted = function(event) {
72 | alert.show('Deleted', event);
73 | };
74 |
75 | vm.eventTimesChanged = function(event) {
76 | alert.show('Dropped or resized', event);
77 | };
78 |
79 | vm.toggle = function($event, field, event) {
80 | $event.preventDefault();
81 | $event.stopPropagation();
82 | event[field] = !event[field];
83 | };
84 |
85 | vm.timespanClicked = function(date, cell) {
86 |
87 | if (vm.calendarView === 'month') {
88 | if ((vm.cellIsOpen && moment(date).startOf('day').isSame(moment(vm.viewDate).startOf('day'))) || cell.events.length === 0 || !cell.inMonth) {
89 | vm.cellIsOpen = false;
90 | } else {
91 | vm.cellIsOpen = true;
92 | vm.viewDate = date;
93 | }
94 | } else if (vm.calendarView === 'year') {
95 | if ((vm.cellIsOpen && moment(date).startOf('month').isSame(moment(vm.viewDate).startOf('month'))) || cell.events.length === 0) {
96 | vm.cellIsOpen = false;
97 | } else {
98 | vm.cellIsOpen = true;
99 | vm.viewDate = date;
100 | }
101 | }
102 |
103 | };
104 |
105 | });
106 |
--------------------------------------------------------------------------------
/docs/examples/kitchen-sink/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
{{ vm.calendarTitle }}
3 |
4 |
5 |
6 |
7 |
8 |
9 |
15 | Previous
16 |
17 |
23 | Today
24 |
25 |
31 | Next
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Year
41 | Month
42 | Week
43 | Day
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
65 |
66 |
67 |
68 |
69 |
70 | Edit events
71 |
74 | Add new
75 |
76 |
77 |
78 |
79 |
171 |
172 |
--------------------------------------------------------------------------------
/docs/examples/optional-event-end-dates/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('OptionalEventEndDatesCtrl', function(moment, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [{
8 | title: 'No event end date',
9 | startsAt: moment().hours(3).minutes(0).toDate(),
10 | color: calendarConfig.colorTypes.info
11 | }, {
12 | title: 'No event end date',
13 | startsAt: moment().hours(5).minutes(0).toDate(),
14 | color: calendarConfig.colorTypes.warning
15 | }];
16 |
17 | vm.calendarView = 'day';
18 | vm.viewDate = new Date();
19 |
20 | });
21 |
--------------------------------------------------------------------------------
/docs/examples/optional-event-end-dates/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/examples/recurring-events/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('RecurringEventsCtrl', function($scope, moment, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [];
8 | vm.calendarView = 'month';
9 | vm.viewDate = moment().toDate();
10 | vm.cellIsOpen = true;
11 |
12 | var events = [{
13 | title: 'Recurs on the 5th of each month',
14 | color: calendarConfig.colorTypes.warning,
15 | rrule: {
16 | freq: RRule.MONTHLY,
17 | bymonthday: 5
18 | }
19 | }, {
20 | title: 'Recurs yearly on the 10th of the current month',
21 | color: calendarConfig.colorTypes.info,
22 | rrule: {
23 | freq: RRule.YEARLY,
24 | bymonth: moment().month() + 1,
25 | bymonthday: 10
26 | }
27 | }, {
28 | title: 'Recurs weekly on mondays',
29 | color: calendarConfig.colorTypes.success,
30 | rrule: {
31 | freq: RRule.WEEKLY,
32 | byweekday: [RRule.MO],
33 | }
34 | }];
35 |
36 | $scope.$watchGroup([
37 | 'vm.calendarView',
38 | 'vm.viewDate'
39 | ], function() {
40 |
41 | vm.events = [];
42 |
43 | events.forEach(function(event) {
44 |
45 | // Use the rrule library to generate recurring events: https://github.com/jkbrzt/rrule
46 | var rule = new RRule(angular.extend({}, event.rrule, {
47 | dtstart: moment(vm.viewDate).startOf(vm.calendarView).toDate(),
48 | until: moment(vm.viewDate).endOf(vm.calendarView).toDate()
49 | }));
50 |
51 | rule.all().forEach(function(date) {
52 | vm.events.push(angular.extend({}, event, {
53 | startsAt: new Date(date)
54 | }));
55 | });
56 |
57 | });
58 |
59 | });
60 |
61 | vm.timespanClicked = function(date, cell) {
62 |
63 | if (vm.calendarView === 'month') {
64 | if ((vm.cellIsOpen && moment(date).startOf('day').isSame(moment(vm.viewDate).startOf('day'))) || cell.events.length === 0 || !cell.inMonth) {
65 | vm.cellIsOpen = false;
66 | } else {
67 | vm.cellIsOpen = true;
68 | vm.viewDate = date;
69 | }
70 | } else if (vm.calendarView === 'year') {
71 | if ((vm.cellIsOpen && moment(date).startOf('month').isSame(moment(vm.viewDate).startOf('month'))) || cell.events.length === 0) {
72 | vm.cellIsOpen = false;
73 | } else {
74 | vm.cellIsOpen = true;
75 | vm.viewDate = date;
76 | }
77 | }
78 |
79 | };
80 |
81 | });
82 |
--------------------------------------------------------------------------------
/docs/examples/recurring-events/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
{{ vm.calendarTitle }}
3 |
4 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/docs/examples/resizable-events/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('ResizableEventsCtrl', function(moment, alert, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [
8 | {
9 | title: 'Resizable event',
10 | color: calendarConfig.colorTypes.warning,
11 | startsAt: moment().startOf('month').toDate(),
12 | endsAt: moment().startOf('month').add(1, 'hour').toDate(), //ends at is required
13 | resizable: true
14 | },
15 | {
16 | title: 'Non-resizeable event',
17 | color: calendarConfig.colorTypes.info,
18 | startsAt: moment().startOf('month').toDate(),
19 | endsAt: moment().startOf('month').add(1, 'hour').toDate(), //ends at is required
20 | resizable: false
21 | }
22 | ];
23 |
24 | vm.calendarView = 'week';
25 | vm.viewDate = moment().startOf('month').toDate();
26 |
27 | vm.eventTimesChanged = function(event) {
28 | alert.show('Resized', event);
29 | };
30 |
31 | });
32 |
--------------------------------------------------------------------------------
/docs/examples/resizable-events/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/examples/select-range/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('SelectRangeCtrl', function(moment) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [];
8 | vm.calendarView = 'day';
9 | vm.viewDate = moment().startOf('month').toDate();
10 |
11 | vm.rangeSelected = function(startDate, endDate) {
12 | vm.firstDateClicked = startDate;
13 | vm.lastDateClicked = endDate;
14 | };
15 |
16 | });
17 |
--------------------------------------------------------------------------------
/docs/examples/select-range/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Select range on a day on the view.
4 | You selected on this day: from {{ vm.firstDateClicked | date:'medium' }} to {{ vm.lastDateClicked | date:'medium' }}
5 |
6 |
7 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/docs/examples/side-time-position/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('SideTimePositionCtrl', function ($scope, moment, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | var actions = [{
8 | label: ' '
9 | }, {
10 | label: ' '
11 | }];
12 |
13 | vm.events = [{
14 | title: 'An event',
15 | startsAt: moment().hours(3).minutes(0).toDate(),
16 | endsAt: moment().hours(7).minutes(0).toDate(),
17 | actions: actions,
18 | color: calendarConfig.colorTypes.warning
19 | }, {
20 | title: 'Another event',
21 | startsAt: moment().hours(5).minutes(0).toDate(),
22 | endsAt: moment().hours(12).minutes(0).toDate(),
23 | actions: actions,
24 | color: calendarConfig.colorTypes.important
25 | }];
26 |
27 | vm.calendarView = 'day';
28 | vm.viewDate = new Date();
29 |
30 | });
31 |
--------------------------------------------------------------------------------
/docs/examples/side-time-position/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Side
5 |
10 |
11 |
12 |
13 |
14 |
Default
15 |
19 |
20 |
21 |
22 |
23 |
Hidden
24 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/docs/examples/slide-box-disabled/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('SlideBoxDisabledCtrl', function(moment, calendarConfig) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [
8 | {
9 | title: 'Event 1',
10 | color: calendarConfig.colorTypes.warning,
11 | startsAt: moment().startOf('month').toDate()
12 | },
13 | {
14 | title: 'Event 2',
15 | color: calendarConfig.colorTypes.info,
16 | startsAt: moment().startOf('month').toDate()
17 | }
18 | ];
19 |
20 | vm.calendarView = 'month';
21 | vm.viewDate = moment().startOf('month').toDate();
22 |
23 | });
24 |
--------------------------------------------------------------------------------
/docs/examples/slide-box-disabled/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/examples/timespan-click/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('TimespanClickCtrl', function(moment) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [];
8 | vm.calendarView = 'month';
9 | vm.viewDate = moment().startOf('month').toDate();
10 |
11 | vm.timespanClicked = function(date) {
12 | vm.lastDateClicked = date;
13 | };
14 |
15 | });
16 |
--------------------------------------------------------------------------------
/docs/examples/timespan-click/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Click on a day on the view.
4 | You clicked on this day: {{ vm.lastDateClicked | date:'medium' }}
5 |
6 |
7 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/docs/examples/view-change-click/javascript.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('mwl.calendar.docs')
3 | .controller('ViewChangeClickCtrl', function(moment) {
4 |
5 | var vm = this;
6 |
7 | vm.events = [];
8 | vm.calendarView = 'year';
9 | vm.viewDate = moment().startOf('month').toDate();
10 | vm.viewChangeEnabled = true;
11 |
12 | vm.viewChangeClicked = function(date, nextView) {
13 | console.log(date, nextView);
14 | return vm.viewChangeEnabled;
15 | };
16 |
17 | });
18 |
--------------------------------------------------------------------------------
/docs/examples/view-change-click/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
{{ vm.viewTitle }}
3 |
4 |
5 |
6 | Click on a month label to change the view to that month.
7 | Click on a day label to change the view to that day.
8 | There is no other view to navigate to.
9 |
10 |
11 |
12 | Enable the view change
13 |
14 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var webpack = require('webpack');
4 |
5 | module.exports = function(config) {
6 |
7 | var webpackConfig = {
8 | cache: true,
9 | devtool: 'inline-source-map',
10 | module: {
11 | rules: [{
12 | test: /\.js$/,
13 | loader: 'eslint-loader',
14 | exclude: /node_modules/,
15 | enforce: 'pre'
16 | }, {
17 | test: /\.html$/,
18 | loader: 'htmlhint-loader',
19 | exclude: /node_modules/,
20 | enforce: 'pre'
21 | }, {
22 | test: /\.html$/,
23 | loader: 'html-loader',
24 | exclude: /node_modules/
25 | }, {
26 | test: /\.less/,
27 | loader: 'null-loader',
28 | exclude: /node_modules/
29 | }, {
30 | test: /\.js$/,
31 | exclude: /(test|node_modules)/,
32 | loader: 'istanbul-instrumenter-loader',
33 | enforce: 'post'
34 | }]
35 | },
36 | plugins: [
37 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
38 | new webpack.DefinePlugin({
39 | EXCLUDE_TEMPLATES: false
40 | })
41 | ]
42 | };
43 |
44 | if (config.singleRun) {
45 | webpackConfig.plugins.push(new webpack.NoEmitOnErrorsPlugin());
46 | }
47 |
48 | config.set({
49 |
50 | // base path that will be used to resolve all patterns (eg. files, exclude)
51 | basePath: './',
52 |
53 | // frameworks to use
54 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
55 | frameworks: ['mocha', 'chai', 'chai-as-promised', 'sinon-chai', 'chai-things'],
56 |
57 | // list of files / patterns to load in the browser
58 | files: [
59 | 'test/unit/entry.js'
60 | ],
61 |
62 | // preprocess matching files before serving them to the browser
63 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
64 | preprocessors: {
65 | 'test/unit/entry.js': ['webpack', 'sourcemap']
66 | },
67 |
68 | coverageReporter: {
69 | dir: 'coverage',
70 | reporters: [{
71 | type: 'text-summary'
72 | }, {
73 | type: 'html',
74 | subdir: 'html'
75 | }, {
76 | type: 'lcovonly',
77 | subdir: '.'
78 | }]
79 | },
80 |
81 | webpack: webpackConfig,
82 |
83 | // test results reporter to use
84 | // possible values: 'dots', 'progress'
85 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
86 | reporters: ['progress', 'coverage'],
87 |
88 | // web server port
89 | port: 9876,
90 |
91 | // enable / disable colors in the output (reporters and logs)
92 | colors: true,
93 |
94 | // level of logging
95 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
96 | logLevel: config.LOG_INFO,
97 |
98 | // start these browsers
99 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
100 | browsers: ['PhantomJS']
101 | });
102 | };
103 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-bootstrap-calendar",
3 | "description": "A pure AngularJS bootstrap themed responsive calendar that can display events and has views for year, month, week and day",
4 | "version": "1.0.0",
5 | "homepage": "https://github.com/mattlewis92/angular-bootstrap-calendar",
6 | "license": "MIT",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/mattlewis92/angular-bootstrap-calendar.git"
10 | },
11 | "files": [
12 | "dist",
13 | "src/less"
14 | ],
15 | "peerDependencies": {
16 | "angular": ">=1.3.0",
17 | "moment": "2.x.x"
18 | },
19 | "devDependencies": {
20 | "angular": "^1.6.8",
21 | "angular-mocks": "^1.6.8",
22 | "bootstrap": "^3.3.6",
23 | "calendar-utils": "0.0.60",
24 | "codecov-lite": "^0.1.3",
25 | "commitizen": "^2.9.2",
26 | "concurrently": "^3.5.1",
27 | "conventional-changelog": "^1.1.7",
28 | "conventional-changelog-cli": "^1.3.5",
29 | "css-loader": "^0.28.7",
30 | "cz-conventional-changelog": "^2.1.0",
31 | "date-fns": "^1.29.0",
32 | "eslint": "^4.14.0",
33 | "eslint-config-mwl": "^0.5.1",
34 | "eslint-loader": "^1.9.0",
35 | "eslint-plugin-angular": "^3.1.1",
36 | "extract-text-webpack-plugin": "^3.0.2",
37 | "html-loader": "^0.5.1",
38 | "htmlhint-loader": "^1.3.0",
39 | "husky": "^0.14.3",
40 | "istanbul-instrumenter-loader": "^3.0.0",
41 | "karma": "^2.0.0",
42 | "karma-chai-plugins": "^0.9.0",
43 | "karma-coverage": "^1.1.0",
44 | "karma-mocha": "^1.3.0",
45 | "karma-phantomjs-launcher": "^1.0.3",
46 | "karma-sourcemap-loader": "^0.3.5",
47 | "karma-webpack": "^2.0.9",
48 | "less": "^2.7.3",
49 | "less-loader": "^4.0.5",
50 | "mocha": "^5.0.0",
51 | "moment": "^2.20.1",
52 | "ng-annotate-loader": "^0.6.0",
53 | "null-loader": "^0.1.1",
54 | "phantomjs-prebuilt": "^2.1.16",
55 | "style-loader": "^0.20.1",
56 | "validate-commit-msg": "^2.14.0",
57 | "webpack": "^3.10.0",
58 | "webpack-dev-server": "^2.9.7",
59 | "webpack-notifier": "^1.5.0"
60 | },
61 | "engines": {
62 | "node": ">=4.0.0"
63 | },
64 | "main": "dist/js/angular-bootstrap-calendar-tpls.js",
65 | "style": "dist/css/angular-bootstrap-calendar.css",
66 | "optionalDependencies": {
67 | "angular-touch": ">=1.3.0",
68 | "angular-ui-bootstrap": ">=0.14.0",
69 | "interactjs": "^1.2.0"
70 | },
71 | "scripts": {
72 | "test": "karma start --single-run",
73 | "test:watch": "karma start --auto-watch",
74 | "build:unmin": "webpack --config webpack.config.build.js",
75 | "build:min": "webpack -p --config webpack.config.build.js",
76 | "build:unmin:exclude-templates": "webpack --config webpack.config.build.js --env.excludeTemplates",
77 | "build:min:exclude-templates": "webpack -p --config webpack.config.build.js --env.excludeTemplates",
78 | "build": "concurrently --raw \"npm run build:unmin\" \"npm run build:min\" \"npm run build:unmin:exclude-templates\" \"npm run build:min:exclude-templates\"",
79 | "start": "concurrently --raw \"webpack-dev-server --open\" \"npm run test:watch\"",
80 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
81 | "commit": "git-cz",
82 | "release": "npm test && npm run build && npm run changelog",
83 | "commitmsg": "validate-commit-msg",
84 | "codecov": "cat coverage/lcov.info | codecov"
85 | },
86 | "config": {
87 | "commitizen": {
88 | "path": "node_modules/cz-conventional-changelog"
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/directives/mwlCalendar.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 | var LOG_PREFIX = 'Bootstrap calendar:';
5 |
6 | angular
7 | .module('mwl.calendar')
8 | .controller('MwlCalendarCtrl', function($scope, $log, $timeout, $attrs, $locale, moment, calendarTitle, calendarHelper) {
9 |
10 | var vm = this;
11 |
12 | vm.changeView = function(view, newDay) {
13 | vm.view = view;
14 | vm.viewDate = newDay;
15 | };
16 |
17 | vm.dateClicked = function(date) {
18 |
19 | var rawDate = moment(date).toDate();
20 |
21 | var nextView = {
22 | year: 'month',
23 | month: 'day',
24 | week: 'day'
25 | };
26 |
27 | if (vm.onViewChangeClick({calendarDate: rawDate, calendarNextView: nextView[vm.view]}) !== false) {
28 | vm.changeView(nextView[vm.view], rawDate);
29 | }
30 |
31 | };
32 |
33 | vm.$onInit = function() {
34 |
35 | if (vm.slideBoxDisabled) {
36 | $log.warn(LOG_PREFIX, 'The `slide-box-disabled` option is deprecated and will be removed in the next release. ' +
37 | 'Instead set `cell-auto-open-disabled` to true');
38 | }
39 |
40 | vm.events = vm.events || [];
41 | vm.excludedDays = vm.excludedDays || [];
42 |
43 | var previousDate = moment(vm.viewDate);
44 | var previousView = vm.view;
45 |
46 | function checkEventIsValid(event) {
47 | if (!event.startsAt) {
48 | $log.warn(LOG_PREFIX, 'Event is missing the startsAt field', event);
49 | } else if (!angular.isDate(event.startsAt)) {
50 | $log.warn(LOG_PREFIX, 'Event startsAt should be a javascript date object. Do `new Date(event.startsAt)` to fix it.', event);
51 | }
52 |
53 | if (event.endsAt) {
54 | if (!angular.isDate(event.endsAt)) {
55 | $log.warn(LOG_PREFIX, 'Event endsAt should be a javascript date object. Do `new Date(event.endsAt)` to fix it.', event);
56 | }
57 | if (moment(event.startsAt).isAfter(moment(event.endsAt))) {
58 | $log.warn(LOG_PREFIX, 'Event cannot start after it finishes', event);
59 | }
60 | }
61 | }
62 |
63 | function refreshCalendar() {
64 |
65 | if (calendarTitle[vm.view] && angular.isDefined($attrs.viewTitle)) {
66 | vm.viewTitle = calendarTitle[vm.view](vm.viewDate);
67 | }
68 |
69 | vm.events.forEach(function(event, index) {
70 | checkEventIsValid(event);
71 | event.calendarEventId = index;
72 | });
73 |
74 | //if on-timespan-click="calendarDay = calendarDate" is set then don't update the view as nothing needs to change
75 | var currentDate = moment(vm.viewDate);
76 | var shouldUpdate = true;
77 | if (
78 | previousDate.clone().startOf(vm.view).isSame(currentDate.clone().startOf(vm.view)) &&
79 | !previousDate.isSame(currentDate) &&
80 | vm.view === previousView
81 | ) {
82 | shouldUpdate = false;
83 | }
84 | previousDate = currentDate;
85 | previousView = vm.view;
86 |
87 | if (shouldUpdate) {
88 | // a $timeout is required as $broadcast is synchronous so if a new events array is set the calendar won't update
89 | $timeout(function() {
90 | $scope.$broadcast('calendar.refreshView');
91 | });
92 | }
93 | }
94 |
95 | calendarHelper.loadTemplates().then(function() {
96 | vm.templatesLoaded = true;
97 |
98 | var eventsWatched = false;
99 |
100 | //Refresh the calendar when any of these variables change.
101 | $scope.$watchGroup([
102 | 'vm.viewDate',
103 | 'vm.view',
104 | 'vm.cellIsOpen',
105 | function() {
106 | return moment.locale() + $locale.id; //Auto update the calendar when the locale changes
107 | }
108 | ], function() {
109 | if (!eventsWatched) {
110 | eventsWatched = true;
111 | //need to deep watch events hence why it isn't included in the watch group
112 | $scope.$watch('vm.events', refreshCalendar, true); //this will call refreshCalendar when the watcher starts (i.e. now)
113 | } else {
114 | refreshCalendar();
115 | }
116 | });
117 |
118 | }).catch(function(err) {
119 | $log.error('Could not load all calendar templates', err);
120 | });
121 |
122 | };
123 |
124 | if (angular.version.minor < 5) {
125 | vm.$onInit();
126 | }
127 |
128 | })
129 | .directive('mwlCalendar', function() {
130 |
131 | return {
132 | template: '
',
133 | restrict: 'E',
134 | scope: {
135 | events: '=',
136 | view: '=',
137 | viewTitle: '=?',
138 | viewDate: '=',
139 | cellIsOpen: '=?',
140 | cellAutoOpenDisabled: '=?',
141 | excludedDays: '=?',
142 | slideBoxDisabled: '=?',
143 | customTemplateUrls: '=?',
144 | draggableAutoScroll: '=?',
145 | onEventClick: '&',
146 | onEventTimesChanged: '&',
147 | onTimespanClick: '&',
148 | onDateRangeSelect: '&?',
149 | onViewChangeClick: '&',
150 | cellModifier: '&',
151 | dayViewStart: '@',
152 | dayViewSegmentSize: '@',
153 | dayViewEnd: '@',
154 | dayViewSplit: '@',
155 | dayViewEventChunkSize: '@',
156 | dayViewEventWidth: '@',
157 | templateScope: '=?',
158 | dayViewTimePosition: '@'
159 | },
160 | controller: 'MwlCalendarCtrl as vm',
161 | bindToController: true
162 | };
163 |
164 | });
165 |
--------------------------------------------------------------------------------
/src/directives/mwlCalendarDay.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlCalendarDayCtrl', function($scope, moment, calendarHelper, calendarEventTitle) {
8 |
9 | var vm = this;
10 |
11 | vm.calendarEventTitle = calendarEventTitle;
12 |
13 | function refreshView() {
14 |
15 | vm.timeHidden = vm.dayViewTimePosition === 'hidden';
16 | vm.dayViewTimePositionOffset = vm.dayViewTimePosition !== 'default' ? 0 : 60;
17 |
18 | vm.dayViewSplit = vm.dayViewSplit || 30;
19 | vm.dayViewHeight = calendarHelper.getDayViewHeight(
20 | vm.dayViewStart,
21 | vm.dayViewEnd,
22 | vm.dayViewSplit,
23 | vm.dayViewSegmentSize
24 | );
25 |
26 | var view = calendarHelper.getDayView(
27 | vm.events,
28 | vm.viewDate,
29 | vm.dayViewStart,
30 | vm.dayViewEnd,
31 | vm.dayViewSplit,
32 | vm.dayViewEventWidth,
33 | vm.dayViewSegmentSize
34 | );
35 |
36 | vm.allDayEvents = view.allDayEvents;
37 | vm.nonAllDayEvents = view.events;
38 | vm.viewWidth = view.width + 62;
39 |
40 | }
41 |
42 | $scope.$on('calendar.refreshView', refreshView);
43 |
44 | $scope.$watchGroup([
45 | 'vm.dayViewStart',
46 | 'vm.dayViewEnd',
47 | 'vm.dayViewSplit'
48 | ], refreshView);
49 |
50 | vm.eventDragComplete = function(event, minuteChunksMoved) {
51 | var minutesDiff = minuteChunksMoved * vm.dayViewSplit;
52 | var newStart = moment(event.startsAt).add(minutesDiff, 'minutes');
53 | var newEnd = moment(event.endsAt).add(minutesDiff, 'minutes');
54 | delete event.tempStartsAt;
55 |
56 | vm.onEventTimesChanged({
57 | calendarEvent: event,
58 | calendarNewEventStart: newStart.toDate(),
59 | calendarNewEventEnd: event.endsAt ? newEnd.toDate() : null
60 | });
61 | };
62 |
63 | vm.eventDragged = function(event, minuteChunksMoved) {
64 | var minutesDiff = minuteChunksMoved * vm.dayViewSplit;
65 | event.tempStartsAt = moment(event.startsAt).add(minutesDiff, 'minutes').toDate();
66 | };
67 |
68 | vm.eventResizeComplete = function(event, edge, minuteChunksMoved) {
69 | var minutesDiff = minuteChunksMoved * vm.dayViewSplit;
70 | var start = moment(event.startsAt);
71 | var end = moment(event.endsAt);
72 | if (edge === 'start') {
73 | start.add(minutesDiff, 'minutes');
74 | } else {
75 | end.add(minutesDiff, 'minutes');
76 | }
77 | delete event.tempStartsAt;
78 |
79 | vm.onEventTimesChanged({
80 | calendarEvent: event,
81 | calendarNewEventStart: start.toDate(),
82 | calendarNewEventEnd: end.toDate()
83 | });
84 | };
85 |
86 | vm.eventResized = function(event, edge, minuteChunksMoved) {
87 | var minutesDiff = minuteChunksMoved * vm.dayViewSplit;
88 | if (edge === 'start') {
89 | event.tempStartsAt = moment(event.startsAt).add(minutesDiff, 'minutes').toDate();
90 | }
91 | };
92 |
93 | })
94 | .directive('mwlCalendarDay', function() {
95 |
96 | return {
97 | template: '
',
98 | restrict: 'E',
99 | require: '^mwlCalendar',
100 | scope: {
101 | events: '=',
102 | viewDate: '=',
103 | onEventClick: '=',
104 | onEventTimesChanged: '=',
105 | onTimespanClick: '=',
106 | onDateRangeSelect: '=',
107 | dayViewStart: '=',
108 | dayViewEnd: '=',
109 | dayViewSplit: '=',
110 | dayViewEventChunkSize: '=',
111 | dayViewSegmentSize: '=',
112 | dayViewEventWidth: '=',
113 | customTemplateUrls: '=?',
114 | cellModifier: '=',
115 | templateScope: '=',
116 | dayViewTimePosition: '=',
117 | draggableAutoScroll: '='
118 | },
119 | controller: 'MwlCalendarDayCtrl as vm',
120 | bindToController: true
121 | };
122 |
123 | });
124 |
--------------------------------------------------------------------------------
/src/directives/mwlCalendarHourList.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 | var calendarUtils = require('calendar-utils');
5 |
6 | angular
7 | .module('mwl.calendar')
8 | .controller('MwlCalendarHourListCtrl', function($scope, $document, moment, calendarHelper) {
9 | var vm = this;
10 |
11 | // source: http://stackoverflow.com/questions/13382516/getting-scroll-bar-width-using-javascript
12 | function getScrollbarWidth() {
13 | var outer = $document[0].createElement('div');
14 | outer.style.visibility = 'hidden';
15 | outer.style.width = '100px';
16 | outer.style.msOverflowStyle = 'scrollbar'; // needed for WinJS apps
17 |
18 | $document[0].body.appendChild(outer);
19 |
20 | var widthNoScroll = outer.offsetWidth;
21 | // force scrollbars
22 | outer.style.overflow = 'scroll';
23 |
24 | // add innerdiv
25 | var inner = $document[0].createElement('div');
26 | inner.style.width = '100%';
27 | outer.appendChild(inner);
28 |
29 | var widthWithScroll = inner.offsetWidth;
30 |
31 | // remove divs
32 | outer.parentNode.removeChild(outer);
33 |
34 | return widthNoScroll - widthWithScroll;
35 | }
36 |
37 | vm.scrollBarWidth = getScrollbarWidth();
38 |
39 | function updateDays() {
40 |
41 | vm.dayViewSplit = parseInt(vm.dayViewSplit);
42 | var dayStart = (vm.dayViewStart || '00:00').split(':');
43 | var dayEnd = (vm.dayViewEnd || '23:59').split(':');
44 | vm.hourGrid = calendarUtils.getDayViewHourGrid({
45 | viewDate: vm.view === 'week' ? moment(vm.viewDate).startOf('week').toDate() : moment(vm.viewDate).toDate(),
46 | hourSegments: 60 / vm.dayViewSplit,
47 | dayStart: {
48 | hour: dayStart[0],
49 | minute: dayStart[1]
50 | },
51 | dayEnd: {
52 | hour: dayEnd[0],
53 | minute: dayEnd[1]
54 | }
55 | });
56 |
57 | vm.hourGrid.forEach(function(hour) {
58 | hour.segments.forEach(function(segment) {
59 |
60 | segment.date = moment(segment.date);
61 | segment.nextSegmentDate = segment.date.clone().add(vm.dayViewSplit, 'minutes');
62 |
63 | if (vm.view === 'week') {
64 |
65 | segment.days = [];
66 |
67 | for (var i = 0; i < 7; i++) {
68 | var day = {
69 | date: moment(segment.date).add(i, 'days')
70 | };
71 | day.nextSegmentDate = day.date.clone().add(vm.dayViewSplit, 'minutes');
72 | vm.cellModifier({calendarCell: day});
73 | segment.days.push(day);
74 | }
75 |
76 | } else {
77 | vm.cellModifier({calendarCell: segment});
78 | }
79 |
80 | });
81 | });
82 |
83 | }
84 |
85 | var originalLocale = moment.locale();
86 |
87 | $scope.$on('calendar.refreshView', function() {
88 |
89 | if (originalLocale !== moment.locale()) {
90 | originalLocale = moment.locale();
91 | updateDays();
92 | }
93 |
94 | });
95 |
96 | $scope.$watchGroup([
97 | 'vm.dayViewStart',
98 | 'vm.dayViewEnd',
99 | 'vm.dayViewSplit',
100 | 'vm.viewDate'
101 | ], function() {
102 | updateDays();
103 | });
104 |
105 | vm.eventDropped = function(event, date) {
106 | var newStart = moment(date);
107 | var newEnd = calendarHelper.adjustEndDateFromStartDiff(event.startsAt, newStart, event.endsAt);
108 |
109 | vm.onEventTimesChanged({
110 | calendarEvent: event,
111 | calendarDate: date,
112 | calendarNewEventStart: newStart.toDate(),
113 | calendarNewEventEnd: newEnd ? newEnd.toDate() : null
114 | });
115 | };
116 |
117 | vm.onDragSelectStart = function(date, dayIndex) {
118 | if (!vm.dateRangeSelect) {
119 | vm.dateRangeSelect = {
120 | active: true,
121 | startDate: date,
122 | endDate: date,
123 | dayIndex: dayIndex
124 | };
125 | }
126 | };
127 |
128 | vm.onDragSelectMove = function(date) {
129 | if (vm.dateRangeSelect) {
130 | vm.dateRangeSelect.endDate = date;
131 | }
132 | };
133 |
134 | vm.onDragSelectEnd = function(date) {
135 | if (vm.dateRangeSelect) {
136 | vm.dateRangeSelect.endDate = date;
137 | if (vm.dateRangeSelect.endDate > vm.dateRangeSelect.startDate) {
138 | vm.onDateRangeSelect({
139 | calendarRangeStartDate: vm.dateRangeSelect.startDate.toDate(),
140 | calendarRangeEndDate: vm.dateRangeSelect.endDate.toDate()
141 | });
142 | }
143 | delete vm.dateRangeSelect;
144 | }
145 | };
146 |
147 | })
148 | .directive('mwlCalendarHourList', function() {
149 |
150 | return {
151 | restrict: 'E',
152 | template: '
',
153 | controller: 'MwlCalendarHourListCtrl as vm',
154 | scope: {
155 | viewDate: '=',
156 | dayViewStart: '=',
157 | dayViewEnd: '=',
158 | dayViewSplit: '=',
159 | dayWidth: '=?',
160 | onTimespanClick: '=',
161 | onDateRangeSelect: '=',
162 | onEventTimesChanged: '=',
163 | customTemplateUrls: '=?',
164 | cellModifier: '=',
165 | templateScope: '=',
166 | view: '@'
167 | },
168 | bindToController: true
169 | };
170 |
171 | });
172 |
--------------------------------------------------------------------------------
/src/directives/mwlCalendarSlideBox.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlCalendarSlideBoxCtrl', function($scope, $timeout, calendarConfig, calendarEventTitle) {
8 |
9 | var vm = this;
10 | vm.calendarConfig = calendarConfig;
11 | vm.calendarEventTitle = calendarEventTitle;
12 |
13 | vm.isCollapsed = true;
14 | $scope.$watch('vm.isOpen', function(isOpen) {
15 | //events must be populated first to set the element height before animation will work
16 | $timeout(function() {
17 | vm.isCollapsed = !isOpen;
18 | });
19 | });
20 |
21 | })
22 | .directive('mwlCalendarSlideBox', function() {
23 |
24 | return {
25 | restrict: 'E',
26 | template: '
',
27 | replace: true,
28 | controller: 'MwlCalendarSlideBoxCtrl as vm',
29 | require: ['^?mwlCalendarMonth', '^?mwlCalendarYear'],
30 | link: function(scope, elm, attrs, ctrls) {
31 | scope.isMonthView = !!ctrls[0];
32 | scope.isYearView = !!ctrls[1];
33 | },
34 | scope: {
35 | isOpen: '=',
36 | events: '=',
37 | onEventClick: '=',
38 | cell: '=',
39 | customTemplateUrls: '=?',
40 | templateScope: '=',
41 | draggableAutoScroll: '='
42 | },
43 | bindToController: true
44 | };
45 |
46 | });
47 |
--------------------------------------------------------------------------------
/src/directives/mwlCalendarWeek.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlCalendarWeekCtrl', function($scope, moment, calendarHelper, calendarConfig, calendarEventTitle) {
8 |
9 | var vm = this;
10 |
11 | vm.showTimes = calendarConfig.showTimesOnWeekView;
12 | vm.calendarEventTitle = calendarEventTitle;
13 |
14 | $scope.$on('calendar.refreshView', function() {
15 | vm.dayViewSplit = vm.dayViewSplit || 30;
16 | vm.dayViewHeight = calendarHelper.getDayViewHeight(
17 | vm.dayViewStart,
18 | vm.dayViewEnd,
19 | vm.dayViewSplit
20 | );
21 | if (vm.showTimes) {
22 | vm.view = calendarHelper.getWeekViewWithTimes(
23 | vm.events,
24 | vm.viewDate,
25 | vm.dayViewStart,
26 | vm.dayViewEnd,
27 | vm.dayViewSplit
28 | );
29 | } else {
30 | vm.view = calendarHelper.getWeekView(vm.events, vm.viewDate, vm.excludedDays);
31 | }
32 | });
33 |
34 | vm.weekDragged = function(event, daysDiff, minuteChunksMoved) {
35 |
36 | var newStart = moment(event.startsAt).add(daysDiff, 'days');
37 | var newEnd = moment(event.endsAt).add(daysDiff, 'days');
38 |
39 | if (minuteChunksMoved) {
40 | var minutesDiff = minuteChunksMoved * vm.dayViewSplit;
41 | newStart = newStart.add(minutesDiff, 'minutes');
42 | newEnd = newEnd.add(minutesDiff, 'minutes');
43 | }
44 |
45 | delete event.tempStartsAt;
46 |
47 | vm.onEventTimesChanged({
48 | calendarEvent: event,
49 | calendarNewEventStart: newStart.toDate(),
50 | calendarNewEventEnd: event.endsAt ? newEnd.toDate() : null
51 | });
52 | };
53 |
54 | vm.eventDropped = function(event, date) {
55 | var daysDiff = moment(date).diff(moment(event.startsAt), 'days');
56 | vm.weekDragged(event, daysDiff);
57 | };
58 |
59 | vm.weekResized = function(event, edge, daysDiff) {
60 |
61 | var start = moment(event.startsAt);
62 | var end = moment(event.endsAt);
63 | if (edge === 'start') {
64 | start.add(daysDiff, 'days');
65 | } else {
66 | end.add(daysDiff, 'days');
67 | }
68 |
69 | vm.onEventTimesChanged({
70 | calendarEvent: event,
71 | calendarNewEventStart: start.toDate(),
72 | calendarNewEventEnd: end.toDate()
73 | });
74 |
75 | };
76 |
77 | vm.tempTimeChanged = function(event, minuteChunksMoved) {
78 | var minutesDiff = minuteChunksMoved * vm.dayViewSplit;
79 | event.tempStartsAt = moment(event.startsAt).add(minutesDiff, 'minutes').toDate();
80 | };
81 |
82 | })
83 | .directive('mwlCalendarWeek', function() {
84 |
85 | return {
86 | template: '
',
87 | restrict: 'E',
88 | require: '^mwlCalendar',
89 | scope: {
90 | events: '=',
91 | viewDate: '=',
92 | excludedDays: '=',
93 | onEventClick: '=',
94 | onEventTimesChanged: '=',
95 | dayViewStart: '=',
96 | dayViewEnd: '=',
97 | dayViewSplit: '=',
98 | dayViewEventChunkSize: '=',
99 | onTimespanClick: '=',
100 | onDateRangeSelect: '=',
101 | customTemplateUrls: '=?',
102 | cellModifier: '=',
103 | templateScope: '=',
104 | draggableAutoScroll: '='
105 | },
106 | controller: 'MwlCalendarWeekCtrl as vm',
107 | link: function(scope, element, attrs, calendarCtrl) {
108 | scope.vm.calendarCtrl = calendarCtrl;
109 | },
110 | bindToController: true
111 | };
112 |
113 | });
114 |
--------------------------------------------------------------------------------
/src/directives/mwlCalendarYear.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlCalendarYearCtrl', function($scope, moment, calendarHelper) {
8 |
9 | var vm = this;
10 | vm.openMonthIndex = null;
11 |
12 | function toggleCell() {
13 | vm.openRowIndex = null;
14 | vm.openMonthIndex = null;
15 |
16 | if (vm.cellIsOpen && vm.view) {
17 | vm.view.forEach(function(month, monthIndex) {
18 | if (moment(vm.viewDate).startOf('month').isSame(month.date)) {
19 | vm.openMonthIndex = monthIndex;
20 | vm.openRowIndex = Math.floor(monthIndex / 4);
21 | }
22 | });
23 | }
24 | }
25 |
26 | $scope.$on('calendar.refreshView', function() {
27 | vm.view = calendarHelper.getYearView(vm.events, vm.viewDate, vm.cellModifier);
28 |
29 | if (vm.cellAutoOpenDisabled) {
30 | toggleCell();
31 | } else if (!vm.cellAutoOpenDisabled && vm.cellIsOpen && vm.openMonthIndex === null) {
32 | //Auto open the calendar to the current day if set
33 | vm.openMonthIndex = null;
34 | vm.view.forEach(function(month) {
35 | if (moment(vm.viewDate).startOf('month').isSame(month.date)) {
36 | vm.monthClicked(month, true);
37 | }
38 | });
39 | }
40 |
41 | });
42 |
43 | vm.monthClicked = function(month, monthClickedFirstRun, $event) {
44 |
45 | if (!monthClickedFirstRun) {
46 | vm.onTimespanClick({
47 | calendarDate: month.date.toDate(),
48 | calendarCell: month,
49 | $event: $event
50 | });
51 | if ($event && $event.defaultPrevented) {
52 | return;
53 | }
54 | }
55 |
56 | if (!vm.cellAutoOpenDisabled) {
57 | vm.openRowIndex = null;
58 | var monthIndex = vm.view.indexOf(month);
59 | if (monthIndex === vm.openMonthIndex) { //the month has been clicked and is already open
60 | vm.openMonthIndex = null; //close the open month
61 | vm.cellIsOpen = false;
62 | } else {
63 | vm.openMonthIndex = monthIndex;
64 | vm.openRowIndex = Math.floor(monthIndex / 4);
65 | vm.cellIsOpen = true;
66 | }
67 | }
68 |
69 | };
70 |
71 | vm.handleEventDrop = function(event, newMonthDate) {
72 | var newStart = moment(event.startsAt)
73 | .year(moment(newMonthDate).year())
74 | .month(moment(newMonthDate).month());
75 | var newEnd = calendarHelper.adjustEndDateFromStartDiff(event.startsAt, newStart, event.endsAt);
76 |
77 | vm.onEventTimesChanged({
78 | calendarEvent: event,
79 | calendarDate: newMonthDate,
80 | calendarNewEventStart: newStart.toDate(),
81 | calendarNewEventEnd: newEnd ? newEnd.toDate() : null
82 | });
83 | };
84 |
85 | vm.$onInit = function() {
86 |
87 | if (vm.cellAutoOpenDisabled) {
88 | $scope.$watchGroup([
89 | 'vm.cellIsOpen',
90 | 'vm.viewDate'
91 | ], toggleCell);
92 | }
93 |
94 | };
95 |
96 | if (angular.version.minor < 5) {
97 | vm.$onInit();
98 | }
99 |
100 | })
101 | .directive('mwlCalendarYear', function() {
102 |
103 | return {
104 | template: '
',
105 | restrict: 'E',
106 | require: '^mwlCalendar',
107 | scope: {
108 | events: '=',
109 | viewDate: '=',
110 | onEventClick: '=',
111 | onEventTimesChanged: '=',
112 | cellIsOpen: '=',
113 | cellAutoOpenDisabled: '=',
114 | onTimespanClick: '=',
115 | cellModifier: '=',
116 | slideBoxDisabled: '=',
117 | customTemplateUrls: '=?',
118 | templateScope: '='
119 | },
120 | controller: 'MwlCalendarYearCtrl as vm',
121 | link: function(scope, element, attrs, calendarCtrl) {
122 | scope.vm.calendarCtrl = calendarCtrl;
123 | },
124 | bindToController: true
125 | };
126 |
127 | });
128 |
--------------------------------------------------------------------------------
/src/directives/mwlCollapseFallback.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlCollapseFallbackCtrl', function($scope, $attrs, $element) {
8 |
9 | $scope.$watch($attrs.mwlCollapseFallback, function(shouldCollapse) {
10 | if (shouldCollapse) {
11 | $element.addClass('ng-hide');
12 | } else {
13 | $element.removeClass('ng-hide');
14 | }
15 | });
16 |
17 | })
18 | .directive('mwlCollapseFallback', function($injector) {
19 |
20 | if ($injector.has('uibCollapseDirective')) {
21 | return {};
22 | }
23 |
24 | return {
25 | restrict: 'A',
26 | controller: 'MwlCollapseFallbackCtrl'
27 | };
28 |
29 | });
30 |
--------------------------------------------------------------------------------
/src/directives/mwlDateModifier.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlDateModifierCtrl', function($element, $attrs, $scope, moment) {
8 |
9 | var vm = this;
10 |
11 | function onClick() {
12 | if (angular.isDefined($attrs.setToToday)) {
13 | vm.date = new Date();
14 | } else if (angular.isDefined($attrs.increment)) {
15 | vm.date = moment(vm.date).add(1, vm.increment);
16 | if (vm.excludedDays && vm.increment.indexOf('day') > -1) {
17 | while (vm.excludedDays.indexOf(vm.date.day()) > -1) {
18 | vm.date.add(1, vm.increment);
19 | }
20 | }
21 | vm.date = vm.date.toDate();
22 | } else if (angular.isDefined($attrs.decrement)) {
23 | vm.date = moment(vm.date).subtract(1, vm.decrement);
24 | if (vm.excludedDays && vm.decrement.indexOf('day') > -1) {
25 | while (vm.excludedDays.indexOf(vm.date.day()) > -1) {
26 | vm.date.subtract(1, vm.decrement);
27 | }
28 | }
29 | vm.date = vm.date.toDate();
30 | }
31 | $scope.$apply();
32 | }
33 |
34 | $element.bind('click', onClick);
35 |
36 | $scope.$on('$destroy', function() {
37 | $element.unbind('click', onClick);
38 | });
39 |
40 | })
41 | .directive('mwlDateModifier', function() {
42 |
43 | return {
44 | restrict: 'A',
45 | controller: 'MwlDateModifierCtrl as vm',
46 | scope: {
47 | date: '=',
48 | increment: '=',
49 | decrement: '=',
50 | excludedDays: '=?'
51 | },
52 | bindToController: true
53 | };
54 |
55 | });
56 |
--------------------------------------------------------------------------------
/src/directives/mwlDragSelect.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlDragSelectCtrl', function($scope, $element, $parse, $attrs) {
8 |
9 | function handleMouseEvent(callbackName) {
10 | return function(event) {
11 | if (callbackName && event.button !== 2) {
12 | $parse(callbackName)($scope);
13 | $scope.$apply();
14 | }
15 | event.preventDefault();
16 | };
17 | }
18 |
19 | var onMouseDown = handleMouseEvent($attrs.onDragSelectStart);
20 | var onMouseMove = handleMouseEvent($attrs.onDragSelectMove);
21 | var onMouseUp = handleMouseEvent($attrs.onDragSelectEnd);
22 |
23 | function enableMouseListeners() {
24 | $element.on('mousedown', onMouseDown);
25 | $element.on('mousemove', onMouseMove);
26 | $element.on('mouseup', onMouseUp);
27 | }
28 |
29 | function disableMouseListeners() {
30 | $element.off('mousedown', onMouseDown);
31 | $element.off('mousemove', onMouseMove);
32 | $element.off('mouseup', onMouseUp);
33 | }
34 |
35 | $scope.$watch($attrs.mwlDragSelect, function(isEnabled) {
36 | if (isEnabled) {
37 | enableMouseListeners();
38 | } else {
39 | disableMouseListeners();
40 | }
41 | });
42 |
43 | $scope.$on('$destroy', function() {
44 | disableMouseListeners();
45 | });
46 |
47 | })
48 | .directive('mwlDragSelect', function() {
49 |
50 | return {
51 | restrict: 'A',
52 | controller: 'MwlDragSelectCtrl'
53 | };
54 |
55 | });
56 |
--------------------------------------------------------------------------------
/src/directives/mwlDraggable.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlDraggableCtrl', function($element, $scope, $window, $parse, $attrs, $timeout, interact) {
8 |
9 | if (!interact) {
10 | return;
11 | }
12 |
13 | var snap, snapGridDimensions;
14 | if ($attrs.snapGrid) {
15 | snapGridDimensions = $parse($attrs.snapGrid)($scope);
16 | snap = {
17 | targets: [
18 | interact.createSnapGrid(snapGridDimensions)
19 | ]
20 | };
21 | }
22 |
23 | function translateElement(elm, transformValue) {
24 | return elm
25 | .css('-ms-transform', transformValue)
26 | .css('-webkit-transform', transformValue)
27 | .css('transform', transformValue);
28 | }
29 |
30 | var autoScroll = $parse($attrs.autoScroll)($scope);
31 | if (typeof autoScroll === 'undefined') {
32 | autoScroll = true;
33 | }
34 |
35 | interact($element[0]).draggable({
36 | autoScroll: autoScroll,
37 | snap: snap,
38 | onstart: function(event) {
39 | angular.element(event.target).addClass('dragging-active');
40 | event.target.dropData = $parse($attrs.dropData)($scope);
41 | event.target.style.pointerEvents = 'none';
42 | if ($attrs.onDragStart) {
43 | $parse($attrs.onDragStart)($scope);
44 | $scope.$apply();
45 | }
46 | },
47 | onmove: function(event) {
48 |
49 | var elm = angular.element(event.target);
50 | var x = (parseFloat(elm.attr('data-x')) || 0) + (event.dx || 0);
51 | var y = (parseFloat(elm.attr('data-y')) || 0) + (event.dy || 0);
52 |
53 | switch ($parse($attrs.axis)($scope)) {
54 | case 'x':
55 | y = 0;
56 | break;
57 |
58 | case 'y':
59 | x = 0;
60 | break;
61 |
62 | default:
63 | }
64 |
65 | if ($window.getComputedStyle(elm[0]).position === 'static') {
66 | elm.css('position', 'relative');
67 | }
68 |
69 | translateElement(elm, 'translate(' + x + 'px, ' + y + 'px)')
70 | .css('z-index', 50)
71 | .attr('data-x', x)
72 | .attr('data-y', y);
73 |
74 | if ($attrs.onDrag) {
75 | $parse($attrs.onDrag)($scope, {x: x, y: y});
76 | $scope.$apply();
77 | }
78 |
79 | },
80 | onend: function(event) {
81 |
82 | var elm = angular.element(event.target);
83 | var x = elm.attr('data-x');
84 | var y = elm.attr('data-y');
85 |
86 | event.target.style.pointerEvents = 'auto';
87 | if ($attrs.onDragEnd) {
88 | $parse($attrs.onDragEnd)($scope, {x: x, y: y});
89 | $scope.$apply();
90 | }
91 |
92 | $timeout(function() {
93 | translateElement(elm, '')
94 | .css('z-index', 'auto')
95 | .removeAttr('data-x')
96 | .removeAttr('data-y')
97 | .removeClass('dragging-active');
98 | });
99 |
100 | }
101 | });
102 |
103 | $scope.$watch($attrs.mwlDraggable, function(enabled) {
104 | interact($element[0]).draggable({
105 | enabled: enabled
106 | });
107 | });
108 |
109 | $scope.$on('$destroy', function() {
110 | interact($element[0]).unset();
111 | });
112 |
113 | })
114 | .directive('mwlDraggable', function() {
115 |
116 | return {
117 | restrict: 'A',
118 | controller: 'MwlDraggableCtrl'
119 | };
120 |
121 | });
122 |
--------------------------------------------------------------------------------
/src/directives/mwlDroppable.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlDroppableCtrl', function($element, $scope, $parse, $attrs, interact) {
8 |
9 | if (!interact) {
10 | return;
11 | }
12 |
13 | var DROP_ACTIVE_CLASS = $attrs.dropActiveClass || 'drop-active';
14 | var INTERACT_OVERLAP_TYPE = $attrs.dropOverlap || 'pointer';
15 |
16 | interact($element[0]).dropzone({
17 | ondragenter: function(event) {
18 | angular.element(event.target).addClass(DROP_ACTIVE_CLASS);
19 | },
20 | ondragleave: function(event) {
21 | angular.element(event.target).removeClass(DROP_ACTIVE_CLASS);
22 | },
23 | ondropdeactivate: function(event) {
24 | angular.element(event.target).removeClass(DROP_ACTIVE_CLASS);
25 | },
26 | ondrop: function(event) {
27 | if (event.relatedTarget.dropData) {
28 | $parse($attrs.onDrop)($scope, {dropData: event.relatedTarget.dropData});
29 | $scope.$apply();
30 | }
31 | },
32 | overlap: INTERACT_OVERLAP_TYPE
33 | });
34 |
35 | $scope.$on('$destroy', function() {
36 | interact($element[0]).unset();
37 | });
38 |
39 | })
40 | .directive('mwlDroppable', function() {
41 |
42 | return {
43 | restrict: 'A',
44 | controller: 'MwlDroppableCtrl'
45 | };
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/src/directives/mwlDynamicDirectiveTemplate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlDynamicDirectiveTemplateCtrl', function($compile, $scope, $attrs, $element, $templateCache, $log, calendarConfig) {
8 |
9 | $scope.$watch($attrs.overrides, function(overrides) {
10 |
11 | var templateName = calendarConfig.templates[$attrs.name];
12 | if (
13 | overrides &&
14 | angular.isObject(overrides) &&
15 | overrides[$attrs.name]
16 | ) {
17 | if ($templateCache.get(overrides[$attrs.name])) {
18 | templateName = overrides[$attrs.name];
19 | } else {
20 | $log.warn('Bootstrap Calendar', 'The custom template for ' + overrides[$attrs.name] +
21 | ' was not found in the template cache. Please ensure it is pre-loaded via a script tag ' +
22 | ' or ' +
23 | 'via a tool such as https://www.npmjs.com/package/gulp-angular-templatecache');
24 | }
25 | }
26 | var template = $templateCache.get(templateName);
27 | $element.html(template);
28 | $compile($element.contents())($scope);
29 |
30 | });
31 |
32 | })
33 | .directive('mwlDynamicDirectiveTemplate', function() {
34 |
35 | return {
36 | restrict: 'A',
37 | controller: 'MwlDynamicDirectiveTemplateCtrl'
38 | };
39 |
40 | });
41 |
--------------------------------------------------------------------------------
/src/directives/mwlElementDimensions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlElementDimensionsCtrl', function($element, $scope, $parse, $attrs, $window) {
8 |
9 | function setDimensions() {
10 | $parse($attrs.mwlElementDimensions).assign($scope, {
11 | width: $element[0].offsetWidth - 1,
12 | height: $element[0].offsetHeight
13 | });
14 | $scope.$applyAsync();
15 | }
16 |
17 | var win = angular.element($window);
18 |
19 | win.bind('resize', setDimensions);
20 |
21 | setDimensions();
22 |
23 | $scope.$on('$destroy', function() {
24 | win.unbind('resize', setDimensions);
25 | });
26 |
27 | })
28 | .directive('mwlElementDimensions', function() {
29 |
30 | return {
31 | restrict: 'A',
32 | controller: 'MwlElementDimensionsCtrl'
33 | };
34 |
35 | });
36 |
--------------------------------------------------------------------------------
/src/directives/mwlResizable.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .controller('MwlResizableCtrl', function($element, $scope, $parse, $attrs, $timeout, interact) {
8 |
9 | if (!interact) {
10 | return;
11 | }
12 |
13 | var snap, snapGridDimensions;
14 | if ($attrs.snapGrid) {
15 | snapGridDimensions = $parse($attrs.snapGrid)($scope);
16 | snap = {
17 | targets: [
18 | interact.createSnapGrid(snapGridDimensions)
19 | ]
20 | };
21 | }
22 |
23 | var originalDimensions = {};
24 | var originalDimensionsStyle = {};
25 | var resizeEdge;
26 |
27 | function getUnitsResized(edge, elm) {
28 | var unitsResized = {};
29 | unitsResized.edge = edge;
30 | if (edge === 'start') {
31 | unitsResized.x = elm.data('x');
32 | unitsResized.y = elm.data('y');
33 | } else if (edge === 'end') {
34 | unitsResized.x = parseFloat(elm.css('width').replace('px', '')) - originalDimensions.width;
35 | unitsResized.y = parseFloat(elm.css('height').replace('px', '')) - originalDimensions.height;
36 | }
37 | return unitsResized;
38 | }
39 |
40 | interact($element[0]).resizable({
41 | edges: $parse($attrs.resizeEdges)($scope),
42 | snap: snap,
43 | onstart: function(event) {
44 |
45 | resizeEdge = 'end';
46 | var elm = angular.element(event.target);
47 | originalDimensions.height = elm[0].offsetHeight;
48 | originalDimensions.width = elm[0].offsetWidth;
49 | originalDimensionsStyle.height = elm.css('height');
50 | originalDimensionsStyle.width = elm.css('width');
51 |
52 | },
53 | onmove: function(event) {
54 |
55 | if (event.rect.width > 0 && event.rect.height > 0) {
56 | var elm = angular.element(event.target);
57 | var x = parseFloat(elm.data('x') || 0);
58 | var y = parseFloat(elm.data('y') || 0);
59 |
60 | elm.css({
61 | width: event.rect.width + 'px',
62 | height: event.rect.height + 'px'
63 | });
64 |
65 | // translate when resizing from top or left edges
66 | x += event.deltaRect.left;
67 | y += event.deltaRect.top;
68 |
69 | elm.css('transform', 'translate(' + x + 'px,' + y + 'px)');
70 |
71 | elm.data('x', x);
72 | elm.data('y', y);
73 |
74 | if (event.deltaRect.left !== 0 || event.deltaRect.top !== 0) {
75 | resizeEdge = 'start';
76 | }
77 |
78 | if ($attrs.onResize) {
79 | $parse($attrs.onResize)($scope, getUnitsResized(resizeEdge, elm));
80 | $scope.$apply();
81 | }
82 |
83 | }
84 |
85 | },
86 | onend: function(event) {
87 |
88 | var elm = angular.element(event.target);
89 | var unitsResized = getUnitsResized(resizeEdge, elm);
90 |
91 | $timeout(function() {
92 | elm
93 | .data('x', null)
94 | .data('y', null)
95 | .css({
96 | transform: '',
97 | width: originalDimensionsStyle.width,
98 | height: originalDimensionsStyle.height
99 | });
100 | });
101 |
102 | if ($attrs.onResizeEnd) {
103 | $parse($attrs.onResizeEnd)($scope, unitsResized);
104 | $scope.$apply();
105 | }
106 |
107 | }
108 | });
109 |
110 | $scope.$watch($attrs.mwlResizable, function(enabled) {
111 | interact($element[0]).resizable({
112 | enabled: enabled
113 | });
114 | });
115 |
116 | $scope.$on('$destroy', function() {
117 | interact($element[0]).unset();
118 | });
119 |
120 | })
121 | .directive('mwlResizable', function() {
122 |
123 | return {
124 | restrict: 'A',
125 | controller: 'MwlResizableCtrl'
126 | };
127 |
128 | });
129 |
--------------------------------------------------------------------------------
/src/entry.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | require('./less/calendar.less');
4 |
5 | var angular = require('angular');
6 |
7 | function requireAll(r) {
8 | r.keys().forEach(r);
9 | }
10 |
11 | var templates = {};
12 |
13 | if (EXCLUDE_TEMPLATES === false) {
14 |
15 | var templatesContext = require.context('./templates', false, /\.html/);
16 |
17 | templatesContext.keys().forEach(function(templateName) {
18 | var templateNameWithoutPrefix = templateName.replace('./', '');
19 | var cacheTemplateName = 'mwl/' + templateNameWithoutPrefix;
20 | var configTemplateName = templateNameWithoutPrefix.replace('.html', '');
21 | templates[configTemplateName] = {
22 | cacheTemplateName: cacheTemplateName,
23 | template: templatesContext(templateName)
24 | };
25 | });
26 |
27 | }
28 |
29 | module.exports = angular
30 | .module('mwl.calendar', [])
31 | .config(function(calendarConfig) {
32 | angular.forEach(templates, function(template, templateName) {
33 | if (!calendarConfig.templates[templateName]) {
34 | calendarConfig.templates[templateName] = template.cacheTemplateName;
35 | }
36 | });
37 | })
38 | .run(function($templateCache, $interpolate) {
39 |
40 | angular.forEach(templates, function(template) {
41 | if (!$templateCache.get(template.cacheTemplateName)) {
42 | var templateContents = template.template
43 | .replace('{{', $interpolate.startSymbol())
44 | .replace('}}', $interpolate.endSymbol());
45 | $templateCache.put(template.cacheTemplateName, templateContents);
46 | }
47 | });
48 |
49 | }).name;
50 |
51 | requireAll(require.context('./directives', true, /\.js$/));
52 | requireAll(require.context('./filters', true, /\.js$/));
53 | requireAll(require.context('./services', true, /\.js$/));
54 |
--------------------------------------------------------------------------------
/src/filters/calendarDate.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .filter('calendarDate', function(calendarHelper, calendarConfig) {
8 |
9 | function calendarDate(date, format, getFromConfig) {
10 |
11 | if (getFromConfig === true) {
12 | format = calendarConfig.dateFormats[format];
13 | }
14 |
15 | return calendarHelper.formatDate(date, format);
16 |
17 | }
18 |
19 | // This is stateful because the locale can change as well
20 | // as calendarConfig.dateFormats which would change the value outside of this filter
21 | calendarDate.$stateful = true;
22 |
23 | return calendarDate;
24 |
25 | });
26 |
--------------------------------------------------------------------------------
/src/filters/calendarLimitTo.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .filter('calendarLimitTo', function(limitToFilter) {
8 |
9 | if (angular.version.minor >= 4) { //1.4+ supports the begin attribute
10 | return limitToFilter;
11 | }
12 |
13 | //Copied from the angular source. Only 1.4 has the begin functionality.
14 | return function(input, limit, begin) {
15 | if (Math.abs(Number(limit)) === Infinity) {
16 | limit = Number(limit);
17 | } else {
18 | limit = parseInt(limit);
19 | }
20 | if (isNaN(limit)) {
21 | return input;
22 | }
23 |
24 | if (angular.isNumber(input)) {
25 | input = input.toString();
26 | }
27 | if (!angular.isArray(input) && !angular.isString(input)) {
28 | return input;
29 | }
30 |
31 | begin = (!begin || isNaN(begin)) ? 0 : parseInt(begin);
32 | begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
33 |
34 | if (limit >= 0) {
35 | return input.slice(begin, begin + limit);
36 | } else if (begin === 0) {
37 | return input.slice(limit, input.length);
38 | } else {
39 | return input.slice(Math.max(0, begin + limit), begin);
40 | }
41 | };
42 |
43 | });
44 |
--------------------------------------------------------------------------------
/src/filters/calendarTruncateEventTitle.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .filter('calendarTruncateEventTitle', function() {
8 |
9 | return function(string, length, boxHeight) {
10 | if (!string) {
11 | return '';
12 | }
13 |
14 | //Only truncate if if actually needs truncating
15 | if (string.length >= length && string.length / 20 > boxHeight / 30) {
16 | return string.substr(0, length) + '...';
17 | } else {
18 | return string;
19 | }
20 | };
21 |
22 | });
23 |
--------------------------------------------------------------------------------
/src/filters/calendarTrustAsHtml.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .filter('calendarTrustAsHtml', function($sce) {
8 |
9 | return function(text) {
10 | return $sce.trustAsHtml(text);
11 | };
12 |
13 | });
14 |
--------------------------------------------------------------------------------
/src/less/calendar.less:
--------------------------------------------------------------------------------
1 | @import "variables.less";
2 | @import "grid.less";
3 | @import "theme.less";
4 | @import "month.less";
5 | @import "week.less";
6 | @import "day.less";
7 | @import "events.less";
8 |
9 |
--------------------------------------------------------------------------------
/src/less/day.less:
--------------------------------------------------------------------------------
1 | .cal-day-box {
2 | text-wrap: none;
3 | overflow-x: auto !important;
4 | overflow-y: hidden;
5 |
6 | .cal-day-hour-part {
7 | height: 30px;
8 | box-sizing: border-box;
9 | -moz-box-sizing: border-box;
10 | -webkit-box-sizing: border-box;
11 | border-bottom: thin dashed @borderColor;
12 |
13 | .cal-day-hour-part-time {
14 | width: 60px;
15 | text-align: center;
16 | float: left;
17 | }
18 |
19 | .cal-day-hour-part-spacer {
20 | height: 30px;
21 | display: inline-block;
22 | }
23 |
24 | &:hover { background-color: @dayHover; }
25 | }
26 | .cal-day-hour-part-selected {
27 | background-color: @borderColor;
28 | }
29 | .cal-day-hour {
30 | .day-highlight {
31 | height: 30px;
32 | }
33 |
34 | background-color: @eventBorderColor;
35 | &:nth-child(odd) {
36 | background-color: @rowHover;
37 | }
38 | }
39 | .cal-hours {
40 | font-weight: bold;
41 | font-size: 12px;
42 | }
43 |
44 | .cal-day-panel {
45 | position: relative;
46 | padding-left: 60px;
47 | border: solid 1px #e1e1e1;
48 | }
49 | .cal-day-panel-hour {
50 | position: absolute;
51 | width: 100%;
52 | margin-left: -60px;
53 | }
54 | .day-event {
55 | position: absolute;
56 | width: 150px;
57 | overflow: hidden;
58 | padding: 2px 3px !important;
59 |
60 | a {
61 | font-size: 12px;
62 | text-overflow: ellipsis;
63 | }
64 |
65 | }
66 | .day-highlight {
67 | padding-top: 2px;
68 | padding-left: 8px;
69 | padding-right: 8px;
70 | box-sizing: border-box;
71 | -moz-box-sizing: border-box;
72 | -webkit-box-sizing: border-box;
73 | border: 1px solid @eventStandardColor;
74 | margin: 1px 1px;
75 | overflow: hidden;
76 | text-overflow: ellipsis;
77 | }
78 | }
79 |
80 | mwl-calendar-day.time-hidden {
81 | .cal-day-hour-part-time {
82 | display: none;
83 | }
84 | }
85 |
86 | mwl-calendar-day.time-on-side {
87 | .cal-day-box {
88 | overflow: visible !important;
89 | margin-left: @sideMargin;
90 | }
91 | .cal-day-panel {
92 | min-width: initial !important;
93 | }
94 | .cal-day-hour-part-time {
95 | margin-left: -@sideMargin;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/less/events.less:
--------------------------------------------------------------------------------
1 | mwl-calendar {
2 |
3 | .event {
4 | display: block;
5 | background-color: @eventStandardColor;
6 | width: @eventSize;
7 | height: @eventSize;
8 | margin-right: @eventMargin;
9 | margin-bottom: @eventMargin;
10 | -webkit-box-shadow: inset 0px 0px 5px 0px rgba(0, 0, 0, 0.4);
11 | box-shadow: inset 0px 0px 5px 0px rgba(0, 0, 0, 0.4);
12 | border-radius: @eventBorderRadius;
13 | border: @eventBorderSize solid @eventBorderColor;
14 | }
15 |
16 | .event-block {
17 | display: block;
18 | background-color: #c3c3c3;
19 | width: 20px;
20 | height: 100%;
21 | }
22 |
23 | .cal-event-list .event.pull-left {
24 | margin-top: 3px;
25 | }
26 |
27 | .day-highlight:hover,
28 | .day-highlight {
29 | background-color: @eventHiliteStandart;
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/src/less/grid-mixin.less:
--------------------------------------------------------------------------------
1 | .gridForWeekOf(@days) {
2 | @oneDay: 100% / @days;
3 | @oneDayIE: 0.9993781095 * @oneDay;
4 | //magic constant, have no idea from where it comes from
5 |
6 | .generate-day();
7 |
8 | .generate-day(@i:1) when (@i =< @days) {
9 | .cal-row-fluid .cal-cell@{i} {
10 | width: (@i * @oneDay);
11 | *width: (@i * @oneDayIE);
12 | }
13 |
14 | .cal-row-fluid .cal-offset@{i},
15 | .cal-row-fluid .cal-offset@{i}:first-child,
16 | .cal-week-box .cal-offset@{i} {
17 | margin-left: (@i * @oneDay);
18 | *margin-left: (@i * @oneDayIE);
19 | }
20 | .generate-day(@i + 1);
21 | }
22 |
23 | }
24 |
25 | .gridForWeeks(@i:1) when (@i =< 7) {
26 | .cal-week-box,
27 | .cal-month-box
28 | {
29 | &.cal-grid-@{i} {
30 | .gridForWeekOf(@i);
31 | }
32 | }
33 |
34 | .gridForWeeks(@i + 1);
35 | }
36 |
--------------------------------------------------------------------------------
/src/less/grid.less:
--------------------------------------------------------------------------------
1 | @import "grid-mixin.less";
2 |
3 | mwl-calendar {
4 |
5 | [class*="cal-cell"] {
6 | float: left;
7 | margin-left: 0;
8 | min-height: 1px;
9 | }
10 |
11 | .cal-row-fluid {
12 | width: 100%;
13 | *zoom: 1;
14 | }
15 |
16 | .cal-row-fluid:before,
17 | .cal-row-fluid:after {
18 | display: table;
19 | content: "";
20 | line-height: 0;
21 | }
22 |
23 | .cal-row-fluid:after {
24 | clear: both;
25 | }
26 |
27 | .cal-row-fluid [class*="cal-cell"] {
28 | display: block;
29 | width: 100%;
30 | -webkit-box-sizing: border-box;
31 | -moz-box-sizing: border-box;
32 | box-sizing: border-box;
33 | float: left;
34 | margin-left: 0%;
35 | *margin-left: -0.05213764337851929%;
36 | }
37 |
38 | .cal-row-fluid [class*="cal-cell"]:first-child {
39 | margin-left: 0;
40 | }
41 |
42 | .cal-row-fluid .controls-row [class*="cal-cell"] + [class*="cal-cell"] {
43 | margin-left: 0%;
44 | }
45 |
46 | .gridForWeeks();
47 |
48 | [class*="cal-cell"].hide,
49 | .cal-row-fluid [class*="cal-cell"].hide {
50 | display: none;
51 | }
52 |
53 | [class*="cal-cell"].pull-right,
54 | .cal-row-fluid [class*="cal-cell"].pull-right {
55 | float: right;
56 | }
57 |
58 |
59 | }
--------------------------------------------------------------------------------
/src/less/month.less:
--------------------------------------------------------------------------------
1 | mwl-calendar .cal-month-box {
2 | .cal-row-fluid {
3 | border-right: @borderSizevert @borderStyle @borderColor;
4 | border-left: @borderSizevert @borderStyle @borderColor;
5 | }
6 |
7 | .cal-row-head {
8 | border-left: none;
9 | border-right: none;
10 | [class*="cal-cell"] {
11 | border: none;
12 | overflow: hidden;
13 | min-height: unset;
14 | text-overflow: ellipsis;
15 | }
16 | }
17 |
18 | .cal-month-day {
19 | position: relative;
20 | display: block;
21 | width: 100%;
22 | .cal-events-num {
23 | margin-left: 10px;
24 | margin-top: 18px;
25 | }
26 | }
27 |
28 | .cal-week-box-cell {
29 | position: absolute;
30 | width: 70px;
31 | left: -71px;
32 | top: -1px;
33 | padding: 8px 5px;
34 | cursor: pointer;
35 | }
36 |
37 | .cal-slide-box {
38 | position: relative;
39 | }
40 |
41 | .cal-slide-tick {
42 | position: absolute;
43 | width: 16px;
44 | margin-left: -7px;
45 | height: 9px;
46 | top: -1px;
47 | z-index: 1;
48 | }
49 | .cal-slide-tick.tick-month1 {
50 | left: 12.5%;
51 | }
52 | .cal-slide-tick.tick-month2 {
53 | left: 37.5%;
54 | }
55 | .cal-slide-tick.tick-month3 {
56 | left: 62.5%;
57 | }
58 | .cal-slide-tick.tick-month4 {
59 | left: 87.5%;
60 | }
61 |
62 | .cal-slide-tick.tick-day1 {
63 | left: 7.14285714285715%;
64 | }
65 | .cal-slide-tick.tick-day2 {
66 | left: 21.42857142857143%;
67 | }
68 | .cal-slide-tick.tick-day3 {
69 | left: 35.71428571428572%;
70 | }
71 | .cal-slide-tick.tick-day4 {
72 | left: 50%;
73 | }
74 | .cal-slide-tick.tick-day5 {
75 | left: 64.2857142857143%;
76 | }
77 | .cal-slide-tick.tick-day6 {
78 | left: 78.57142857142859%;
79 | }
80 | .cal-slide-tick.tick-day7 {
81 | left: 92.85714285714285%;
82 | }
83 | .events-list {
84 | position: absolute;
85 | bottom: 0;
86 | left: 0;
87 | z-index: 50;
88 | }
89 | .cal-slide-content ul.unstyled {
90 | margin-bottom: 0;
91 | }
92 | .cal-slide-content ul li.dragging-active .event-item {
93 | color: black;
94 | }
95 |
96 | .cal-day-selected {
97 | background-color: #ededed;
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/less/theme.less:
--------------------------------------------------------------------------------
1 | mwl-calendar {
2 | .cal-row-head {
3 | [class*="cal-cell"] {
4 | border: none;
5 | padding: 5px 0;
6 | text-align: center;
7 | font-weight: bolder;
8 | }
9 | [class*="cal-cell"] small {
10 | font-weight: normal;
11 | }
12 | }
13 | .cal-year-box .row:hover,
14 | .cal-row-fluid:hover {
15 | background-color: @rowHover;
16 | }
17 | .cal-month-day {
18 | height: @rowHeightMonth;
19 | }
20 |
21 | [class*="cal-cell"]:hover,
22 | .cell-focus,
23 | [class*="cal-cell"] .drop-active,
24 | .cal-cell.drop-active,
25 | .cal-week-box .cal-cell1.drop-active,
26 | .cal-day-hour-part.drop-active {
27 | background-color: @dayHover;
28 | }
29 |
30 | .cal-year-box [class*="span"],
31 | .cal-month-box [class*="cal-cell"] {
32 | min-height: @rowHeightMonth;
33 | position: relative;
34 | }
35 |
36 | .cal-year-box,
37 | .cal-month-box {
38 | [class*="span"] + [class*="span"],
39 | [class*="cal-cell"] + [class*="cal-cell"] {
40 | border-left: @borderSizevert @borderStyle @borderColor;
41 | }
42 | }
43 |
44 | .cal-year-box [class*="span"] {
45 | min-height: @rowHeightYear;
46 | }
47 |
48 | .cal-year-box .row,
49 | .cal-month-box .cal-row-fluid {
50 | border-bottom: @borderSizehoriz @borderStyle @borderColor;
51 | margin-left: 0px;
52 | margin-right: 0px;
53 | }
54 |
55 | .cal-year-box,
56 | .cal-week-box {
57 | border-top: @borderSizehoriz @borderStyle @borderColor;
58 | border-bottom: @borderSizehoriz @borderStyle @borderColor;
59 | border-right: @borderSizevert @borderStyle @borderColor;
60 | border-left: @borderSizevert @borderStyle @borderColor;
61 | border-radius: 2px;
62 | }
63 |
64 | span[data-cal-date] {
65 | font-size: 1.2em;
66 | font-weight: normal;
67 | opacity: 0.5;
68 | transition: all .3s ease-in-out;
69 | -webkit-transition: all .1s ease-in-out;
70 | -moz-transition: all .1s ease-in-out;
71 | -ms-transition: all .1s ease-in-out;
72 | -o-transition: all .1s ease-in-out;
73 | margin-top: 15px;
74 | margin-right: 15px;
75 | }
76 | span[data-cal-date]:hover {
77 | opacity: 1;
78 | }
79 |
80 | .cal-day-outmonth span[data-cal-date] {
81 | opacity: 0.1;
82 | cursor: default;
83 | }
84 |
85 | .cal-day-today {
86 | background-color: #e8fde7;
87 | }
88 |
89 | .cal-day-today span[data-cal-date] {
90 | color: darkgreen;
91 | }
92 | .cal-month-box .cal-day-today span[data-cal-date] {
93 | font-size: 1.9em;
94 | }
95 | .cal-day-holiday span[data-cal-date] {
96 | color: #800080;
97 | }
98 |
99 | .cal-day-weekend span[data-cal-date] {
100 | color: darkred;
101 | }
102 |
103 | .cal-week-box-cell {
104 | border: @borderSize @borderStyle @borderColor;
105 | border-right: 0px;
106 | border-radius: 5px 0 0 5px;
107 | background-color: @rowHover;
108 | text-align: right;
109 | }
110 |
111 | .cal-day-tick {
112 | border: @borderSize @borderStyle @borderColor;
113 | border-top: 0px solid;
114 | border-radius: 0 0 5px 5px;
115 | background-color: @dayHover;
116 | text-align: center;
117 |
118 | .fa {
119 | display: none;
120 | }
121 | }
122 |
123 | .cal-day-tick {
124 | position: absolute;
125 | right: 50%;
126 | bottom: -21px;
127 | padding: 0px 5px;
128 | cursor: pointer;
129 | z-index: 5;
130 | text-align: center;
131 | width: 26px;
132 | margin-right: -17px;
133 | }
134 |
135 | .cal-slide-box {
136 | border-top: 0px solid #8c8c8c;
137 | }
138 |
139 | .cal-slide-content {
140 | padding: 20px;
141 | color: #ffffff;
142 | background-color: #555555;
143 | -webkit-box-shadow: inset 0px 0px 15px 0px rgba(0, 0, 0, 0.5);
144 | box-shadow: inset 0px 0px 15px 0px rgba(0, 0, 0, 0.5);
145 | }
146 |
147 | .cal-slide-content a.event-item {
148 | color: #ffffff;
149 | font-weight: normal;
150 | }
151 |
152 | a.event-item-edit,
153 | a.event-item-delete,
154 | a.event-item-action {
155 | padding-left: 5px;
156 | }
157 |
158 | .cal-year-box .cal-slide-content a.event-item,
159 | .cal-year-box a.event-item-edit,
160 | .cal-year-box a.event-item-delete,
161 | .cal-year-box a.event-item-action {
162 | position: relative;
163 | top: -3px;
164 | }
165 |
166 | .events-list {
167 | max-height: @rowHeightMonth - 53px;
168 | padding-left: 5px;
169 | }
170 | .cal-column {
171 | border-left: @borderSize @borderStyle @borderColor;
172 | }
173 | a.cal-event-week {
174 | text-decoration: none;
175 | color: #151515;
176 | }
177 | .badge-important {
178 | background-color: #b94a48;
179 | }
180 |
181 | .pointer {
182 | cursor: pointer;
183 | }
184 |
185 | .cal-year-box:last-child {
186 | border-bottom: 0px;
187 | }
188 |
189 | .cal-context {
190 | width: 100%;
191 | }
192 |
193 | .cal-events-num {
194 | margin-top: 20px;
195 | }
196 |
197 | @media (max-width: 991px) {
198 |
199 | .cal-year-box [class*="span"]:nth-child(2) {
200 | border-right: 0px;
201 | }
202 |
203 | .cal-year-box [class*="span"]:nth-child(1), .cal-year-box [class*="span"]:nth-child(2) {
204 | border-bottom: 1px solid #e1e1e1;
205 | }
206 |
207 | }
208 |
209 | }
210 |
--------------------------------------------------------------------------------
/src/less/variables.less:
--------------------------------------------------------------------------------
1 | @rowHeightMonth: 100px;
2 | @rowHeightYear: 60px;
3 |
4 | // Events
5 | @eventStandardColor: #c3c3c3;
6 | @eventHiliteStandart: #dddddd;
7 |
8 | // MONTH
9 | @rowHover: #fafafa;
10 | @dayHover: darken(@rowHover, 5%);
11 | @borderColor: darken(@rowHover, 10%);
12 | @borderSize: 1px;
13 | @borderSizehoriz: 1px;
14 | @borderSizevert: 1px;
15 | @borderStyle: solid;
16 |
17 | @eventBorderSize: 1px;
18 | @eventBorderColor: #ffffff;
19 | @eventBorderRadius:8px;
20 | @eventMargin: 2px;
21 | @eventSize: 12px;
22 | @sideMargin: 55px;
--------------------------------------------------------------------------------
/src/less/week.less:
--------------------------------------------------------------------------------
1 | .cal-week-box {
2 | position: relative;
3 |
4 | [data-event-class] {
5 | white-space: nowrap;
6 | height: 30px;
7 | line-height: 30px;
8 | text-overflow: ellipsis;
9 | overflow: hidden;
10 | padding-top: 0px !important;
11 | margin-top: 0px !important;
12 | margin-bottom: 0px !important;
13 | font-size: 12px;
14 | padding: 0 3px !important;
15 | }
16 | .cal-day-panel {
17 | border: 0px !important;
18 | }
19 |
20 | .cal-row-head {
21 | border-bottom: 1px solid #e1e1e1;
22 | }
23 | }
24 |
25 | .cal-week-box:not(.cal-day-box) {
26 |
27 | .cal-row-fluid {
28 | margin-bottom: 2px;
29 | }
30 |
31 | .cal-row-fluid:hover,
32 | [class*="cal-cell"]:hover {
33 | background-color: inherit !important;
34 | }
35 |
36 | [data-event-class] {
37 | margin-left: 2px;
38 | margin-right: 2px;
39 | }
40 |
41 | .border-left-rounded {
42 | border-top-left-radius: 5px;
43 | border-bottom-left-radius: 5px;
44 | }
45 |
46 | .border-right-rounded {
47 | border-top-right-radius: 5px;
48 | border-bottom-right-radius: 5px;
49 | }
50 |
51 | }
52 |
53 | .cal-week-box.cal-day-box {
54 | .cal-row-head {
55 | padding-left: 60px;
56 | }
57 | .cal-day-panel {
58 | overflow-x: hidden;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/services/calendarConfig.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .constant('calendarConfig', {
8 | allDateFormats: {
9 | angular: {
10 | date: {
11 | hour: 'ha',
12 | day: 'd MMM',
13 | month: 'MMMM',
14 | weekDay: 'EEEE',
15 | time: 'HH:mm',
16 | datetime: 'MMM d, h:mm a'
17 | },
18 | title: {
19 | day: 'EEEE d MMMM, yyyy',
20 | week: 'Week {week} of {year}',
21 | month: 'MMMM yyyy',
22 | year: 'yyyy'
23 | }
24 | },
25 | moment: {
26 | date: {
27 | hour: 'ha',
28 | day: 'D MMM',
29 | month: 'MMMM',
30 | weekDay: 'dddd',
31 | time: 'HH:mm',
32 | datetime: 'MMM D, h:mm a'
33 | },
34 | title: {
35 | day: 'dddd D MMMM, YYYY',
36 | week: 'Week {week} of {year}',
37 | month: 'MMMM YYYY',
38 | year: 'YYYY'
39 | }
40 | }
41 | },
42 | get dateFormats() {
43 | return this.allDateFormats[this.dateFormatter].date;
44 | },
45 | get titleFormats() {
46 | return this.allDateFormats[this.dateFormatter].title;
47 | },
48 | dateFormatter: 'angular',
49 | showTimesOnWeekView: false,
50 | displayAllMonthEvents: false,
51 | i18nStrings: {
52 | weekNumber: 'Week {week}'
53 | },
54 | templates: {},
55 | colorTypes: {
56 | info: {
57 | primary: '#1e90ff',
58 | secondary: '#d1e8ff'
59 | },
60 | important: {
61 | primary: '#ad2121',
62 | secondary: '#fae3e3'
63 | },
64 | warning: {
65 | primary: '#e3bc08',
66 | secondary: '#fdf1ba'
67 | },
68 | inverse: {
69 | primary: '#1b1b1b',
70 | secondary: '#c1c1c1'
71 | },
72 | special: {
73 | primary: '#800080',
74 | secondary: '#ffe6ff'
75 | },
76 | success: {
77 | primary: '#006400',
78 | secondary: '#caffca'
79 | }
80 | }
81 | });
82 |
--------------------------------------------------------------------------------
/src/services/calendarEventTitle.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .factory('calendarEventTitle', function(calendarDateFilter, calendarTruncateEventTitleFilter) {
8 |
9 | function yearView(event) {
10 | return event.title + ' (' + calendarDateFilter(event.startsAt, 'datetime', true) + ')';
11 | }
12 |
13 | function monthView(event) {
14 | return event.title + ' (' + calendarDateFilter(event.startsAt, 'time', true) + ')';
15 | }
16 |
17 | function monthViewTooltip(event) {
18 | return calendarDateFilter(event.startsAt, 'time', true) + ' - ' + event.title;
19 | }
20 |
21 | function weekView(event) {
22 | return event.title;
23 | }
24 |
25 | function weekViewTooltip(event) {
26 | return event.title;
27 | }
28 |
29 | function dayView(event) {
30 | return event.allDay ? event.title : calendarTruncateEventTitleFilter(event.title, 20, event.height);
31 | }
32 |
33 | function dayViewTooltip(event) {
34 | return event.title;
35 | }
36 |
37 | return {
38 | yearView: yearView,
39 | monthView: monthView,
40 | monthViewTooltip: monthViewTooltip,
41 | weekView: weekView,
42 | weekViewTooltip: weekViewTooltip,
43 | dayView: dayView,
44 | dayViewTooltip: dayViewTooltip
45 | };
46 |
47 | });
48 |
--------------------------------------------------------------------------------
/src/services/calendarTitle.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | angular
6 | .module('mwl.calendar')
7 | .factory('calendarTitle', function(moment, calendarConfig, calendarHelper) {
8 |
9 | function day(viewDate) {
10 | return calendarHelper.formatDate(viewDate, calendarConfig.titleFormats.day);
11 | }
12 |
13 | function week(viewDate) {
14 | return calendarConfig.titleFormats.week
15 | .replace('{week}', moment(viewDate).isoWeek())
16 | .replace('{year}', moment(viewDate).startOf('isoweek').format('YYYY'));
17 | }
18 |
19 | function month(viewDate) {
20 | return calendarHelper.formatDate(viewDate, calendarConfig.titleFormats.month);
21 | }
22 |
23 | function year(viewDate) {
24 | return calendarHelper.formatDate(viewDate, calendarConfig.titleFormats.year);
25 | }
26 |
27 | return {
28 | day: day,
29 | week: week,
30 | month: month,
31 | year: year
32 | };
33 |
34 | });
35 |
--------------------------------------------------------------------------------
/src/services/interact.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 | var interact;
5 | try {
6 | interact = require('interactjs');
7 | } catch (e) {
8 | /* istanbul ignore next */
9 | interact = null;
10 | }
11 |
12 | angular
13 | .module('mwl.calendar')
14 | .constant('interact', interact);
15 |
--------------------------------------------------------------------------------
/src/services/moment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 | var moment = require('moment');
5 |
6 | angular
7 | .module('mwl.calendar')
8 | .constant('moment', moment);
9 |
--------------------------------------------------------------------------------
/src/templates/calendar.html:
--------------------------------------------------------------------------------
1 |
5 |
6 |
The value passed to the view attribute of the calendar is not set
7 |
8 |
The value passed to view-date attribute of the calendar is not set
9 |
10 |
23 |
24 |
25 |
41 |
42 |
43 |
60 |
61 |
62 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/src/templates/calendarDayView.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
13 |
14 | -
15 |
16 |
17 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
45 |
46 |
47 |
71 |
72 |
73 | ,
74 |
75 |
76 |
80 |
81 |
82 |
83 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/src/templates/calendarHourList.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/templates/calendarMonthCell.html:
--------------------------------------------------------------------------------
1 |
19 |
20 |
24 |
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/templates/calendarMonthCellEvents.html:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/src/templates/calendarMonthView.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
20 |
21 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/templates/calendarSlideBox.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
18 |
19 |
20 |
21 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/templates/calendarWeekView.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
14 |
15 |
16 |
17 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
45 |
46 |
47 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/src/templates/calendarYearView.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
18 |
19 |
20 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "mwl/test",
3 | "rules": {
4 | "no-undefined": 0
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/test/unit/config.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | describe('calendar config', function() {
6 |
7 | beforeEach(angular.mock.module('mwl.calendar', function($interpolateProvider) {
8 | $interpolateProvider.startSymbol('[[');
9 | $interpolateProvider.endSymbol(']]');
10 | }));
11 |
12 | var $templateCache;
13 |
14 | beforeEach(inject(function(_$templateCache_) {
15 | $templateCache = _$templateCache_;
16 | }));
17 |
18 | it('should replace the interpolation symbol with the user configured one in templates', function() {
19 | expect($templateCache.get('mwl/calendarMonthCell.html').indexOf('class="cal-month-day [[ day.cssClass ]]"') > -1).to.be.true;
20 | });
21 |
22 | });
23 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlCalendarDay.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular'),
4 | moment = require('moment');
5 |
6 | describe('mwlCalendarDay directive', function() {
7 | var MwlCalendarCtrl,
8 | element,
9 | scope,
10 | $rootScope,
11 | directiveScope,
12 | showModal,
13 | calendarHelper,
14 | template =
15 | ' ';
25 | var calendarDay = new Date(2015, 4, 1);
26 |
27 | function prepareScope(vm) {
28 | //These variables MUST be set as a minimum for the calendar to work
29 | vm.viewDate = calendarDay;
30 | vm.dayViewStart = '06:00';
31 | vm.dayViewEnd = '22:59';
32 | vm.dayViewsplit = 30;
33 | vm.cellModifier = sinon.spy();
34 | vm.events = [
35 | {
36 | calendarEventId: 0,
37 | title: 'An event',
38 | type: 'warning',
39 | startsAt: moment(calendarDay).startOf('week').subtract(2, 'days').add(8, 'hours').toDate(),
40 | endsAt: moment(calendarDay).startOf('week').add(1, 'week').add(9, 'hours').toDate(),
41 | draggable: true,
42 | resizable: true
43 | }, {
44 | calendarEventId: 1,
45 | title: ' Another event , with a html title',
46 | type: 'info',
47 | startsAt: moment(calendarDay).subtract(1, 'day').toDate(),
48 | endsAt: moment(calendarDay).add(5, 'days').toDate(),
49 | draggable: true,
50 | resizable: true
51 | }, {
52 | calendarEventId: 2,
53 | title: 'This is a really long event title that occurs on every year',
54 | type: 'important',
55 | startsAt: moment(calendarDay).startOf('day').add(7, 'hours').toDate(),
56 | endsAt: moment(calendarDay).startOf('day').add(19, 'hours').toDate(),
57 | recursOn: 'year',
58 | draggable: true,
59 | resizable: true
60 | }
61 | ];
62 |
63 | showModal = sinon.spy();
64 |
65 | vm.onEventClick = function(event) {
66 | showModal('Clicked', event);
67 | };
68 |
69 | vm.onEventTimesChanged = function(event) {
70 | showModal('Dropped or resized', event);
71 | };
72 | }
73 |
74 | beforeEach(angular.mock.module('mwl.calendar'));
75 |
76 | beforeEach(angular.mock.inject(function($compile, _$rootScope_, _calendarHelper_) {
77 | $rootScope = _$rootScope_;
78 | calendarHelper = _calendarHelper_;
79 | scope = $rootScope.$new();
80 | prepareScope(scope);
81 |
82 | element = $compile(template)(scope);
83 | scope.$apply();
84 | directiveScope = element.isolateScope();
85 | MwlCalendarCtrl = directiveScope.vm;
86 | }));
87 |
88 | it('should get the new day view when calendar refreshes', function() {
89 | sinon.stub(calendarHelper, 'getDayViewHeight').returns(1000);
90 | var event1 = {event: 'event1'};
91 | var event2 = {event: 'event2', allDay: true};
92 | sinon.stub(calendarHelper, 'getDayView').returns({events: [{event: event1}], allDayEvents: [event2]});
93 | scope.$broadcast('calendar.refreshView');
94 | expect(calendarHelper.getDayViewHeight).to.have.been.calledWith('06:00', '22:59', 30);
95 | expect(MwlCalendarCtrl.dayViewHeight).to.equal(1000);
96 | expect(calendarHelper.getDayView).to.have.been.calledWith(scope.events, scope.viewDate, '06:00', '22:59', 30);
97 | expect(MwlCalendarCtrl.nonAllDayEvents).to.eql([{event: event1}]);
98 | expect(MwlCalendarCtrl.allDayEvents).to.eql([event2]);
99 | });
100 |
101 | it('should call the callback function when you finish dragging and event', function() {
102 | MwlCalendarCtrl.eventDragComplete(scope.events[0], 1);
103 | expect(showModal).to.have.been.calledWith('Dropped or resized', {
104 | calendarEvent: scope.events[0],
105 | calendarNewEventStart: new Date(2015, 3, 24, 8, 30),
106 | calendarNewEventEnd: new Date(2015, 4, 3, 9, 30)
107 | });
108 | });
109 |
110 | it('should call the callback function when you finish dragging an event with no endsAt', function() {
111 | delete scope.events[0].endsAt;
112 | MwlCalendarCtrl.eventDragComplete(scope.events[0], 1);
113 | expect(showModal).to.have.been.calledWith('Dropped or resized', {
114 | calendarEvent: scope.events[0],
115 | calendarNewEventStart: new Date(2015, 3, 24, 8, 30),
116 | calendarNewEventEnd: null
117 | });
118 | });
119 |
120 | it('should update the temporary start position while dragging', function() {
121 | MwlCalendarCtrl.eventDragged(scope.events[0], 1);
122 | expect(scope.events[0].tempStartsAt).to.eql(new Date(2015, 3, 24, 8, 30));
123 | });
124 |
125 | it('should call the callback function when you finish resizing and event', function() {
126 | MwlCalendarCtrl.eventResizeComplete(scope.events[0], 'start', 1);
127 | expect(showModal).to.have.been.calledWith('Dropped or resized', {
128 | calendarEvent: scope.events[0],
129 | calendarNewEventStart: new Date(2015, 3, 24, 8, 30),
130 | calendarNewEventEnd: new Date(2015, 4, 3, 9, 0)
131 | });
132 |
133 | MwlCalendarCtrl.eventResizeComplete(scope.events[0], 'end', 1);
134 | expect(showModal).to.have.been.calledWith('Dropped or resized', {
135 | calendarEvent: scope.events[0],
136 | calendarNewEventStart: new Date(2015, 3, 24, 8, 0),
137 | calendarNewEventEnd: new Date(2015, 4, 3, 9, 30)
138 | });
139 | });
140 |
141 | it('should update the temporary start position while resizing', function() {
142 | MwlCalendarCtrl.eventResized(scope.events[0], 'start', 1);
143 | expect(scope.events[0].tempStartsAt).to.eql(new Date(2015, 3, 24, 8, 30));
144 | });
145 |
146 | it('should update the events when the day view split changes', function() {
147 | scope.viewDate = moment(calendarDay).startOf('day').add(1, 'hour').toDate();
148 | scope.$apply();
149 | scope.$broadcast('calendar.refreshView');
150 | scope.dayViewSplit = 15;
151 | scope.$apply();
152 | expect(MwlCalendarCtrl.nonAllDayEvents[0].height).to.equal(2038);
153 | });
154 |
155 | });
156 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlCalendarSlideBox.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular'),
4 | moment = require('moment');
5 |
6 | describe('mwlCalendarSlideBox directive', function() {
7 | var element,
8 | scope,
9 | $rootScope,
10 | directiveScope,
11 | template =
12 | ' ';
16 |
17 | function prepareScope(vm) {
18 | //These variables MUST be set as a minimum for the calendar to work
19 | vm.isOpen = false;
20 | vm.events = [
21 | {
22 | calendarEventId: 0,
23 | title: 'An event',
24 | type: 'warning',
25 | startsAt: moment().startOf('week').subtract(2, 'days').add(8, 'hours').toDate(),
26 | endsAt: moment().startOf('week').add(1, 'week').add(9, 'hours').toDate(),
27 | draggable: true,
28 | resizable: true
29 | }, {
30 | calendarEventId: 1,
31 | title: ' Another event , with a html title',
32 | type: 'info',
33 | startsAt: moment().subtract(1, 'day').toDate(),
34 | endsAt: moment().add(5, 'days').toDate(),
35 | draggable: true,
36 | resizable: true
37 | }, {
38 | calendarEventId: 2,
39 | title: 'This is a really long event title that occurs on every year',
40 | type: 'important',
41 | startsAt: moment().startOf('day').add(7, 'hours').toDate(),
42 | endsAt: moment().startOf('day').add(19, 'hours').toDate(),
43 | recursOn: 'year',
44 | draggable: true,
45 | resizable: true
46 | }
47 | ];
48 | }
49 |
50 | beforeEach(angular.mock.module('mwl.calendar'));
51 |
52 | beforeEach(angular.mock.inject(function($compile, _$rootScope_) {
53 | $rootScope = _$rootScope_;
54 | scope = $rootScope.$new();
55 | prepareScope(scope);
56 |
57 | element = $compile(template)(scope);
58 | scope.$apply();
59 | directiveScope = element.isolateScope();
60 | }));
61 |
62 | it('should initialise scope properties', function() {
63 | expect(directiveScope.isMonthView).to.be.false;
64 | expect(directiveScope.isYearView).to.be.false;
65 | });
66 |
67 | });
68 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlCalendarYear.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular'),
4 | moment = require('moment');
5 |
6 | describe('mwlCalendarYear directive', function() {
7 | var MwlCalendarCtrl,
8 | element,
9 | scope,
10 | $rootScope,
11 | directiveScope,
12 | showModal,
13 | calendarHelper,
14 | template =
15 | ' ';
27 | var calendarDay = new Date(2015, 4, 1);
28 |
29 | function prepareScope(vm) {
30 | //These variables MUST be set as a minimum for the calendar to work
31 | vm.viewDate = calendarDay;
32 | vm.cellIsOpen = false;
33 | vm.dayViewStart = '06:00';
34 | vm.dayViewEnd = '22:59';
35 | vm.dayViewsplit = 30;
36 | vm.events = [
37 | {
38 | title: 'An event',
39 | type: 'warning',
40 | startsAt: moment(calendarDay).startOf('week').subtract(2, 'days').add(8, 'hours').toDate(),
41 | endsAt: moment(calendarDay).startOf('week').add(1, 'week').add(9, 'hours').toDate(),
42 | draggable: true,
43 | resizable: true
44 | }, {
45 | title: ' Another event , with a html title',
46 | type: 'info',
47 | startsAt: moment(calendarDay).subtract(1, 'day').toDate(),
48 | endsAt: moment(calendarDay).add(5, 'days').toDate(),
49 | draggable: true,
50 | resizable: true
51 | }, {
52 | title: 'This is a really long event title that occurs on every year',
53 | type: 'important',
54 | startsAt: moment(calendarDay).startOf('day').add(7, 'hours').toDate(),
55 | endsAt: moment(calendarDay).startOf('day').add(19, 'hours').toDate(),
56 | recursOn: 'year',
57 | draggable: true,
58 | resizable: true
59 | }
60 | ];
61 |
62 | showModal = sinon.spy();
63 |
64 | vm.onEventClick = function(event) {
65 | showModal('Clicked', event);
66 | };
67 |
68 | vm.onTimeSpanClick = function(event) {
69 | showModal('Day clicked', event);
70 | };
71 |
72 | vm.onEventTimesChanged = function(event) {
73 | showModal('Dropped or resized', event);
74 | };
75 | }
76 |
77 | beforeEach(angular.mock.module('mwl.calendar'));
78 |
79 | beforeEach(angular.mock.inject(function($compile, _$rootScope_, _calendarHelper_) {
80 | $rootScope = _$rootScope_;
81 | calendarHelper = _calendarHelper_;
82 | scope = $rootScope.$new();
83 | prepareScope(scope);
84 | element = angular.element(template);
85 | element.data('$mwlCalendarController', {});
86 | element = $compile(element)(scope);
87 | scope.$apply();
88 | directiveScope = element.isolateScope();
89 | MwlCalendarCtrl = directiveScope.vm;
90 | }));
91 |
92 | it('should get the new year view when calendar refreshes and show the list of events for the current month if required', function() {
93 | var yearView = [{date: moment(calendarDay), inMonth: true}];
94 | sinon.stub(calendarHelper, 'getYearView').returns(yearView);
95 | scope.$broadcast('calendar.refreshView');
96 | expect(calendarHelper.getYearView).to.have.been.calledWith(scope.events, scope.viewDate);
97 | expect(MwlCalendarCtrl.view).to.equal(yearView);
98 | });
99 |
100 | it('should toggle the event list for the selected month ', function() {
101 | MwlCalendarCtrl.view = [{date: moment(calendarDay), inMonth: true}];
102 | //Open event list
103 | MwlCalendarCtrl.cellIsOpen = true;
104 | scope.$apply();
105 | expect(MwlCalendarCtrl.openRowIndex).to.equal(0);
106 | expect(MwlCalendarCtrl.openMonthIndex).to.equal(0);
107 |
108 | //Close event list
109 | MwlCalendarCtrl.cellIsOpen = false;
110 | scope.$apply();
111 | expect(MwlCalendarCtrl.openRowIndex).to.equal(null);
112 | expect(MwlCalendarCtrl.openMonthIndex).to.equal(null);
113 | });
114 |
115 | it('should call the on time span clicked callback', function() {
116 | scope.onTimeSpanClick = sinon.spy();
117 | scope.$apply();
118 | MwlCalendarCtrl.view = [{date: moment(calendarDay), inMonth: true}];
119 | MwlCalendarCtrl.monthClicked(MwlCalendarCtrl.view[0], false);
120 | expect(scope.onTimeSpanClick).to.have.been.calledWith({
121 | calendarDate: MwlCalendarCtrl.view[0].date.toDate(),
122 | calendarCell: MwlCalendarCtrl.view[0],
123 | $event: undefined
124 | });
125 | });
126 |
127 | it('should call the callback function when you finish dropping an event', function() {
128 | MwlCalendarCtrl.handleEventDrop(scope.events[0], new Date(2015, 11, 1));
129 | expect(showModal).to.have.been.calledWith('Dropped or resized', {
130 | calendarEvent: scope.events[0],
131 | calendarDate: new Date(2015, 11, 1),
132 | calendarNewEventStart: new Date(2015, 11, 24, 8, 0),
133 | calendarNewEventEnd: new Date(2016, 0, 2, 9, 0)
134 | });
135 | });
136 |
137 | it('should call the callback function when you finish dropping an event with no end date', function() {
138 | delete scope.events[0].endsAt;
139 | MwlCalendarCtrl.handleEventDrop(scope.events[0], new Date(2015, 11, 1));
140 | expect(showModal).to.have.been.calledWith('Dropped or resized', {
141 | calendarEvent: scope.events[0],
142 | calendarDate: new Date(2015, 11, 1),
143 | calendarNewEventStart: new Date(2015, 11, 24, 8, 0),
144 | calendarNewEventEnd: null
145 | });
146 | });
147 |
148 | });
149 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlCollapseFallback.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | describe('mwlCollapseFallback directive', function() {
6 | var element,
7 | scope,
8 | $rootScope,
9 | template = '
';
10 |
11 | function prepareScope(vm) {
12 | //These variables MUST be set as a minimum for the calendar to work
13 | vm.isCollapsed = true;
14 | }
15 |
16 | beforeEach(angular.mock.module('mwl.calendar'));
17 |
18 | describe('when acting as a fallback', function() {
19 | beforeEach(angular.mock.inject(function($compile, _$rootScope_) {
20 | $rootScope = _$rootScope_;
21 | scope = $rootScope.$new();
22 | prepareScope(scope);
23 | element = $compile(template)(scope);
24 | scope.$apply();
25 | }));
26 |
27 | it('should initialise toggle the ng-hide class', function() {
28 | expect(element.hasClass('ng-hide')).to.be.true;
29 | scope.isCollapsed = false;
30 | scope.$apply();
31 | expect(element.hasClass('ng-hide')).to.be.false;
32 | });
33 | });
34 |
35 | describe('when it is not required', function() {
36 | beforeEach(angular.mock.module(function($provide) {
37 | $provide.value('uibCollapseDirective', {});
38 | }));
39 |
40 | beforeEach(angular.mock.inject(function($compile, _$rootScope_) {
41 | $rootScope = _$rootScope_;
42 | scope = $rootScope.$new();
43 | sinon.spy(scope, '$watch');
44 | prepareScope(scope);
45 | element = $compile(template)(scope);
46 | scope.$apply();
47 | }));
48 |
49 | it('should not set any watches', function() {
50 | expect(scope.$watch).not.to.have.been.called;
51 | });
52 | });
53 |
54 | });
55 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlDateModifier.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | describe('mwlDateModifier directive', function() {
6 | var element,
7 | scope,
8 | $rootScope,
9 | $compile,
10 | template =
11 | ' ';
19 |
20 | function prepareScope(vm) {
21 | //These variables MUST be set as a minimum for the calendar to work
22 | vm.date = new Date(2015, 0, 5);
23 | vm.increment = 'days';
24 | vm.decrement = 'months';
25 | }
26 |
27 | beforeEach(angular.mock.module('mwl.calendar'));
28 |
29 | beforeEach(angular.mock.inject(function(_$compile_, _$rootScope_) {
30 | $compile = _$compile_;
31 | $rootScope = _$rootScope_;
32 | scope = $rootScope.$new();
33 | prepareScope(scope);
34 | }));
35 |
36 | it('should increment the date by one unit of the provided attribute value', function() {
37 | element = angular.element(template).removeAttr('set-to-today');
38 | element = $compile(element)(scope);
39 | scope.$apply();
40 | element.triggerHandler('click');
41 | expect(scope.date).to.eql(new Date(2015, 0, 6));
42 | });
43 |
44 | it('should decrement the date by one unit of the provided attribute value', function() {
45 | element = angular.element(template).removeAttr('set-to-today').removeAttr('increment');
46 | element = $compile(element)(scope);
47 | scope.$apply();
48 | element.triggerHandler('click');
49 | expect(scope.date).to.eql(new Date(2014, 11, 5));
50 | });
51 |
52 | it('should set the date to today', function() {
53 | element = $compile(template)(scope);
54 | scope.$apply();
55 | element.triggerHandler('click');
56 | expect(scope.date.toString()).to.equal((new Date()).toString());
57 | });
58 |
59 | it('should skip the excluded days when incrementing days', function() {
60 | element = angular.element(template).removeAttr('set-to-today');
61 | scope.date = new Date(2015, 0, 2);
62 | scope.excludedDays = [0, 6];
63 | element = $compile(element)(scope);
64 | scope.$apply();
65 | element.triggerHandler('click');
66 | expect(scope.date.toString()).to.equal((new Date(2015, 0, 5)).toString());
67 | });
68 |
69 | it('should skip the excluded days when decrementing days', function() {
70 | element = angular.element(template).removeAttr('set-to-today').removeAttr('increment');
71 | scope.excludedDays = [0, 6];
72 | scope.decrement = 'days';
73 | element = $compile(element)(scope);
74 | scope.$apply();
75 | element.triggerHandler('click');
76 | expect(scope.date.toString()).to.equal((new Date(2015, 0, 2)).toString());
77 | });
78 |
79 | });
80 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlDragSelect.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | describe('mwlDragSelect directive', function() {
6 |
7 | beforeEach(angular.mock.module('mwl.calendar'));
8 |
9 | var elm, scope;
10 | beforeEach(angular.mock.inject(function($compile, $rootScope, $document) {
11 | scope = $rootScope.$new();
12 | scope.onStart = sinon.spy();
13 | scope.onMove = sinon.spy();
14 | scope.onEnd = sinon.spy();
15 | elm = $compile(
16 | '
'
18 | )(scope);
19 | $document.find('body').append(elm);
20 | scope.$apply();
21 | }));
22 |
23 | afterEach(function() {
24 | scope.$destroy();
25 | elm.remove();
26 | });
27 |
28 | function triggerEvent(type) {
29 | /* global document */
30 | var event = document.createEvent('Event');
31 | event.initEvent(type, true, true);
32 | elm[0].dispatchEvent(event);
33 | }
34 |
35 | describe('isEnabled = true', function() {
36 |
37 | beforeEach(function() {
38 | scope.isEnabled = true;
39 | scope.$apply();
40 | });
41 |
42 | it('should call the mousedown callback when the drag select', function() {
43 | triggerEvent('mousedown');
44 | expect(scope.onStart).to.have.been.calledOnce;
45 | });
46 |
47 | it('should call the mousemove callback when the drag select', function() {
48 | triggerEvent('mousemove');
49 | expect(scope.onMove).to.have.been.calledOnce;
50 | });
51 |
52 | it('should call the mouseup callback when the drag select', function() {
53 | triggerEvent('mouseup');
54 | expect(scope.onEnd).to.have.been.calledOnce;
55 | });
56 |
57 | it('should not fire drag events when the right mouse button is pressed', function() {
58 | var event = document.createEvent('Event');
59 | event.initEvent('mousedown', true, true);
60 | event.button = 2;
61 | elm[0].dispatchEvent(event);
62 |
63 | expect(scope.onStart).not.to.have.been.called;
64 | });
65 |
66 | });
67 |
68 | describe('isEnabled = false', function() {
69 |
70 | beforeEach(function() {
71 | scope.isEnabled = false;
72 | scope.$apply();
73 | });
74 |
75 | it('should not call the mousedown callback when the drag select', function() {
76 | triggerEvent('mousedown');
77 | expect(scope.onStart).not.to.have.been.called;
78 | });
79 |
80 | it('should not call the mousemove callback when the drag select', function() {
81 | triggerEvent('mousemove');
82 | expect(scope.onMove).not.to.have.been.called;
83 | });
84 |
85 | it('should not call the mouseup callback when the drag select', function() {
86 | triggerEvent('mouseup');
87 | expect(scope.onEnd).not.to.have.been.called;
88 | });
89 |
90 | });
91 |
92 | });
93 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlDraggable.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | describe('mwlDraggable directive', function() {
6 | var element,
7 | scope,
8 | $rootScope,
9 | interact,
10 | interactInstance,
11 | draggableOptions,
12 | elementTarget,
13 | $window,
14 | $compile,
15 | $timeout,
16 | template =
17 | '
';
26 |
27 | function prepareScope(vm) {
28 | //These variables MUST be set as a minimum for the calendar to work
29 | vm.draggable = true;
30 | vm.dropData = 'myData';
31 | vm.onDragStart = sinon.spy();
32 | vm.onDrag = sinon.spy();
33 | vm.onDragEnd = sinon.spy();
34 | }
35 |
36 | beforeEach(angular.mock.module('mwl.calendar'));
37 |
38 | beforeEach(angular.mock.module(function($provide) {
39 | interact = sinon.stub();
40 | interact.createSnapGrid = sinon.spy();
41 | $provide.constant('interact', interact);
42 | }));
43 |
44 | beforeEach(angular.mock.inject(function(_$compile_, _$rootScope_, _$window_, _$timeout_) {
45 | $window = _$window_;
46 | $compile = _$compile_;
47 | $rootScope = _$rootScope_;
48 | $timeout = _$timeout_;
49 | scope = $rootScope.$new();
50 | prepareScope(scope);
51 |
52 | interactInstance = {
53 | draggable: sinon.spy(),
54 | unset: sinon.spy()
55 | };
56 | interact.returns(interactInstance);
57 | element = $compile(template)(scope);
58 | draggableOptions = interactInstance.draggable.args[0][0];
59 |
60 | elementTarget = angular.element('
');
61 | angular.element($window.document.body).append(elementTarget);
62 | }));
63 |
64 | afterEach(function() {
65 | elementTarget.remove();
66 | });
67 |
68 | it('should initialise interact', function() {
69 | expect(interact.createSnapGrid).to.have.been.calledWith({x: 30, y: 30});
70 | expect(interact).to.have.been.calledWith(element[0]);
71 | });
72 |
73 | it('should handle on drag start', function() {
74 | var event = {
75 | target: elementTarget[0]
76 | };
77 |
78 | draggableOptions.onstart(event);
79 | expect(angular.element(event.target).hasClass('dragging-active')).to.be.true;
80 | expect(angular.element(event.target).css('pointerEvents')).to.equal('none');
81 | expect(scope.onDragStart).to.have.been.called;
82 | });
83 |
84 | it('should handle on drag move', function() {
85 | var event = {
86 | target: elementTarget[0],
87 | dx: 0,
88 | dy: 30
89 | };
90 |
91 | draggableOptions.onstart(event);
92 | draggableOptions.onmove(event);
93 | expect(angular.element(event.target).css('z-index')).to.equal('50');
94 | expect(angular.element(event.target).attr('data-y')).to.equal('30');
95 | expect(angular.element(event.target).attr('data-x')).to.equal('0');
96 | expect(scope.onDrag).to.have.been.calledWith(0, 1);
97 | });
98 |
99 | it('should handle on drag end', function() {
100 | var event = {
101 | target: elementTarget[0],
102 | dx: 0,
103 | dy: 180
104 | };
105 |
106 | draggableOptions.onstart(event);
107 | draggableOptions.onmove(event);
108 | draggableOptions.onend(event);
109 | $timeout.flush();
110 | expect(angular.element(event.target).hasClass('dragging-active')).to.be.false;
111 | expect(angular.element(event.target).css('pointerEvents')).to.equal('auto');
112 | expect(angular.element(event.target).css('transform')).to.equal('');
113 | expect(angular.element(event.target).css('-webkit-transform')).to.equal('');
114 | expect(angular.element(event.target).css('-ms-transform')).to.equal('');
115 | expect(angular.element(event.target).css('z-index')).to.equal('auto');
116 | expect(scope.onDragEnd).to.have.been.calledWith(0, 6);
117 | });
118 |
119 | it('should unset interact when scope gets destroyed', function() {
120 | scope.$destroy();
121 | expect(interactInstance.unset).to.have.been.called;
122 | });
123 |
124 | it('should disable dragging on the event', function() {
125 | scope.draggable = false;
126 | scope.$apply();
127 | expect(interactInstance.draggable).to.have.been.calledWith({enabled: false});
128 | });
129 |
130 | });
131 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlDroppable.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | describe('mwlDroppable directive', function() {
6 | var element,
7 | scope,
8 | $rootScope,
9 | interact,
10 | interactInstance,
11 | dropzoneOptions,
12 | $compile,
13 | template = '
';
14 |
15 | function prepareScope(vm) {
16 | //These variables MUST be set as a minimum for the calendar to work
17 | vm.dropData = 'myData';
18 | vm.onDrop = sinon.spy();
19 | }
20 |
21 | beforeEach(angular.mock.module('mwl.calendar'));
22 |
23 | beforeEach(angular.mock.module(function($provide) {
24 | interact = sinon.stub();
25 | $provide.constant('interact', interact);
26 | }));
27 |
28 | beforeEach(angular.mock.inject(function(_$compile_, _$rootScope_) {
29 | $compile = _$compile_;
30 | $rootScope = _$rootScope_;
31 | scope = $rootScope.$new();
32 | prepareScope(scope);
33 |
34 | interactInstance = {
35 | dropzone: sinon.spy(),
36 | unset: sinon.spy()
37 | };
38 | interact.returns(interactInstance);
39 | element = $compile(template)(scope);
40 | dropzoneOptions = interactInstance.dropzone.args[0][0];
41 | }));
42 |
43 | it('should initialise interact', function() {
44 | expect(interact).to.have.been.calledWith(element[0]);
45 | });
46 |
47 | it('should handle on drag enter', function() {
48 | var event = {
49 | target: angular.element('
')[0],
50 | relatedTarget: {dropData: scope.dropData}
51 | };
52 |
53 | dropzoneOptions.ondragenter(event);
54 | expect(angular.element(event.target).hasClass('drop-active')).to.be.true;
55 | });
56 |
57 | it('should handle on drag leave', function() {
58 | var event = {
59 | target: angular.element('
')[0],
60 | relatedTarget: {dropData: scope.dropData}
61 | };
62 |
63 | dropzoneOptions.ondragenter(event);
64 | dropzoneOptions.ondragleave(event);
65 | expect(angular.element(event.target).hasClass('drop-active')).to.be.false;
66 | });
67 |
68 | it('should handle on drop', function() {
69 | var event = {
70 | target: angular.element('
')[0],
71 | relatedTarget: {dropData: scope.dropData}
72 | };
73 |
74 | dropzoneOptions.ondrop(event);
75 | expect(scope.onDrop).to.have.been.calledWith('myData');
76 | });
77 |
78 | it('should handle on drop deactivate', function() {
79 | var event = {
80 | target: angular.element('
')[0],
81 | relatedTarget: {dropData: scope.dropData}
82 | };
83 |
84 | dropzoneOptions.ondragenter(event);
85 | dropzoneOptions.ondropdeactivate(event);
86 | expect(angular.element(event.target).hasClass('drop-active')).to.be.false;
87 | });
88 |
89 | it('should unset interact when scope gets destroyed', function() {
90 | scope.$destroy();
91 | expect(interactInstance.unset).to.have.been.called;
92 | });
93 |
94 | });
95 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlDynamicDirectiveTemplate.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | describe('dynamicDirectiveTemplate directive', function() {
6 |
7 | beforeEach(angular.mock.module('mwl.calendar'));
8 |
9 | var scope, elm, calendarConfig, $templateCache, $log;
10 | beforeEach(inject(function($rootScope, $compile, _$templateCache_, _calendarConfig_, _$log_) {
11 | $templateCache = _$templateCache_;
12 | calendarConfig = _calendarConfig_;
13 | $log = _$log_;
14 | scope = $rootScope.$new();
15 | calendarConfig.templates = {
16 | foo: 'foo.html'
17 | };
18 | scope.baz = 'baz';
19 | $templateCache.put('foo.html', 'foo {{ baz }}');
20 | elm = $compile('
')(scope);
21 | scope.$apply();
22 | }));
23 |
24 | afterEach(function() {
25 | elm.remove();
26 | scope.$destroy();
27 | });
28 |
29 | it('should fallback to the default template if no custom templates are set', function() {
30 | expect(elm.text()).to.equal('foo baz');
31 | });
32 |
33 | it('should fallback to the default template if the custom template name doesnt exist in the cache', function() {
34 | scope.overrides = {
35 | foo: 'bam.html'
36 | };
37 | scope.$apply();
38 | expect(elm.text()).to.equal('foo baz');
39 | });
40 |
41 | it('should use the custom template', function() {
42 | $templateCache.put('bar.html', 'bar {{ baz }}');
43 | scope.overrides = {
44 | foo: 'bar.html'
45 | };
46 | scope.$apply();
47 | expect(elm.text()).to.equal('bar baz');
48 | });
49 |
50 | it('should log a warning when the template is not in the cache', function() {
51 | $log.warn = sinon.spy();
52 | scope.overrides = {
53 | foo: 'bam.html'
54 | };
55 | scope.$apply();
56 | expect($log.warn).to.have.been.calledOnce;
57 | });
58 |
59 | });
60 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlElementDimensions.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | describe('mwlElementDimensions directive', function() {
6 | var element,
7 | scope,
8 | $rootScope,
9 | $window,
10 | template = '
';
11 |
12 | function prepareScope(vm) {
13 | //These variables MUST be set as a minimum for the calendar to work
14 | vm.elementDimensions = {};
15 | }
16 |
17 | beforeEach(angular.mock.module('mwl.calendar'));
18 |
19 | beforeEach(angular.mock.inject(function($compile, _$rootScope_, _$window_) {
20 | $window = _$window_;
21 | $rootScope = _$rootScope_;
22 | scope = $rootScope.$new();
23 | prepareScope(scope);
24 | element = angular.element(template);
25 | angular.element($window.document.body).append(element);
26 | element = $compile(element)(scope);
27 | scope.$apply();
28 | }));
29 |
30 | afterEach(function() {
31 | element.remove();
32 | });
33 |
34 | it('should initialise scope properties', function() {
35 | expect(scope.elementDimensions).to.eql({width: 99, height: 50});
36 | });
37 |
38 | it('should update the element dimensions when the window is resized', function() {
39 | element[0].style.width = '150px';
40 | element[0].style.height = '20px';
41 | $window.dispatchEvent(new $window.Event('resize'));
42 | expect(scope.elementDimensions).to.eql({width: 149, height: 20});
43 | });
44 |
45 | });
46 |
--------------------------------------------------------------------------------
/test/unit/directives/mwlResizable.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | describe('mwlresizable directive', function() {
6 | var element,
7 | scope,
8 | $rootScope,
9 | interact,
10 | interactInstance,
11 | resizableOptions,
12 | $compile,
13 | $timeout,
14 | template =
15 | '
';
23 |
24 | function prepareScope(vm) {
25 | //These variables MUST be set as a minimum for the calendar to work
26 | vm.resizable = true;
27 | vm.dropData = 'myData';
28 | vm.onResizeStart = sinon.spy();
29 | vm.onResize = sinon.spy();
30 | vm.onResizeEnd = sinon.spy();
31 | }
32 |
33 | beforeEach(angular.mock.module('mwl.calendar'));
34 |
35 | beforeEach(angular.mock.module(function($provide) {
36 | interact = sinon.stub();
37 | interact.createSnapGrid = sinon.spy();
38 | $provide.constant('interact', interact);
39 | }));
40 |
41 | beforeEach(angular.mock.inject(function(_$compile_, _$rootScope_, _$timeout_) {
42 | $compile = _$compile_;
43 | $rootScope = _$rootScope_;
44 | $timeout = _$timeout_;
45 | scope = $rootScope.$new();
46 | prepareScope(scope);
47 |
48 | interactInstance = {
49 | resizable: sinon.spy(),
50 | unset: sinon.spy()
51 | };
52 | interact.returns(interactInstance);
53 | element = $compile(template)(scope);
54 | resizableOptions = interactInstance.resizable.args[0][0];
55 | }));
56 |
57 | it('should initialise interact', function() {
58 | expect(interact.createSnapGrid).to.have.been.calledWith({y: 30});
59 | expect(interact).to.have.been.calledWith(element[0]);
60 | });
61 |
62 | it('should handle on resize start', function() {
63 | var event = {
64 | target: angular.element('
')[0]
65 | };
66 |
67 | resizableOptions.onstart(event);
68 | });
69 |
70 | it('should handle on resize move', function() {
71 | var event = {
72 | target: angular.element('
')[0],
73 | rect: {
74 | width: 100,
75 | height: 50
76 | },
77 | deltaRect: {
78 | left: 0,
79 | top: -30
80 | }
81 | };
82 |
83 | resizableOptions.onstart(event);
84 | resizableOptions.onmove(event);
85 | expect(angular.element(event.target).data('y')).to.equal(-30);
86 | expect(angular.element(event.target).data('x')).to.equal(0);
87 | expect(scope.onResize).to.have.been.calledWith(0, -1);
88 |
89 | event = {
90 | target: angular.element('
')[0],
91 | rect: {
92 | width: 100,
93 | height: 50
94 | },
95 | deltaRect: {
96 | left: 0,
97 | top: 60
98 | }
99 | };
100 |
101 | scope.onResize.reset();
102 | resizableOptions.onmove(event);
103 | expect(angular.element(event.target).data('y')).to.equal(60);
104 | expect(angular.element(event.target).data('x')).to.equal(0);
105 | expect(scope.onResize).to.have.been.calledWith(0, 2);
106 | });
107 |
108 | it('should not resize the element to height 0', function() {
109 |
110 | var event = {
111 | target: angular.element('
')[0],
112 | rect: {
113 | width: 100,
114 | height: 0
115 | },
116 | deltaRect: {
117 | left: 0,
118 | top: -30
119 | }
120 | };
121 | resizableOptions.onmove(event);
122 | expect(angular.element(event.target).data('x')).to.be.undefined;
123 | expect(angular.element(event.target).data('y')).to.be.undefined;
124 | expect(angular.element(event.target).css('height')).to.equal('');
125 | expect(angular.element(event.target).css('width')).to.equal('');
126 | expect(angular.element(event.target).css('transform')).to.be.undefined;
127 | expect(scope.onResize).not.to.have.been.called;
128 |
129 | });
130 |
131 | it('should not resize the element to width 0', function() {
132 |
133 | var event = {
134 | target: angular.element('
')[0],
135 | rect: {
136 | width: 0,
137 | height: 100
138 | },
139 | deltaRect: {
140 | left: 0,
141 | top: -30
142 | }
143 | };
144 | resizableOptions.onmove(event);
145 | expect(angular.element(event.target).data('x')).to.be.undefined;
146 | expect(angular.element(event.target).data('y')).to.be.undefined;
147 | expect(angular.element(event.target).css('height')).to.equal('');
148 | expect(angular.element(event.target).css('width')).to.equal('');
149 | expect(angular.element(event.target).css('transform')).to.be.undefined;
150 | expect(scope.onResize).not.to.have.been.called;
151 |
152 | });
153 |
154 | it('should handle on resize end', function() {
155 | var event = {
156 | target: angular.element('
')[0],
157 | rect: {
158 | width: 1,
159 | height: 120
160 | },
161 | deltaRect: {
162 | left: 0,
163 | top: 0
164 | }
165 | };
166 |
167 | resizableOptions.onstart(event);
168 | resizableOptions.onmove(event);
169 | resizableOptions.onend(event);
170 | $timeout.flush();
171 | expect(scope.onResizeEnd).to.have.been.calledWith(1, 4);
172 | expect(angular.element(event.target).css('transform')).to.eql('');
173 | expect(angular.element(event.target).css('width')).to.eql('30px');
174 | expect(angular.element(event.target).css('height')).to.eql('30px');
175 | });
176 |
177 | it('should unset interact when scope gets destroyed', function() {
178 | scope.$destroy();
179 | expect(interactInstance.unset).to.have.been.called;
180 | });
181 |
182 | it('should disable resizing on the event', function() {
183 | scope.resizable = false;
184 | scope.$apply();
185 | expect(interactInstance.resizable).to.have.been.calledWith({enabled: false});
186 | });
187 |
188 | });
189 |
--------------------------------------------------------------------------------
/test/unit/entry.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | require('angular');
3 | require('angular-mocks');
4 | require('../../src/entry');
5 |
6 | var testsContext = require.context('.', true, /\.spec/);
7 | testsContext.keys().forEach(testsContext);
8 |
--------------------------------------------------------------------------------
/test/unit/filters/calendarDate.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | beforeEach(angular.mock.module('mwl.calendar'));
6 |
7 | describe('calendarDateFilter', function() {
8 |
9 | var dateFilter, calendarHelper;
10 |
11 | beforeEach(inject(function($filter, _calendarHelper_) {
12 | dateFilter = $filter('calendarDate');
13 | calendarHelper = _calendarHelper_;
14 | }));
15 |
16 | it('should be marked as stateful', function() {
17 | expect(dateFilter.$stateful).to.be.true;
18 | });
19 |
20 | it('should should call calendarHelper.formatDate with the given date and format', function() {
21 | var spy = sinon.spy(calendarHelper, 'formatDate');
22 | var date = new Date();
23 | dateFilter(date, 'yyyy');
24 | expect(spy).to.have.been.calledWith(date, 'yyyy');
25 | });
26 |
27 | it('should should use the prdefined format from calendarConfig.dateFormats if getFromConfig is passed', function() {
28 | var spy = sinon.spy(calendarHelper, 'formatDate');
29 | var date = new Date();
30 | dateFilter(date, 'hour', true);
31 | expect(spy).to.have.been.calledWith(date, 'ha');
32 | });
33 |
34 | });
35 |
--------------------------------------------------------------------------------
/test/unit/filters/calendarTruncateEventTitle.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 | beforeEach(angular.mock.module('mwl.calendar'));
5 |
6 | describe('calendarTruncateEventTitleFilter', function() {
7 |
8 | var eventTitleFilter;
9 |
10 | beforeEach(inject(function(_calendarTruncateEventTitleFilter_) {
11 | eventTitleFilter = _calendarTruncateEventTitleFilter_;
12 | }));
13 |
14 | it('should return an empty string when passed a false value', function() {
15 | expect(eventTitleFilter(null)).to.equal('');
16 | });
17 |
18 | it('should return the original string', function() {
19 | expect(eventTitleFilter('test', 10, 100)).to.equal('test');
20 | });
21 |
22 | it('should return a truncated string for the first 5 characters', function() {
23 | expect(eventTitleFilter('A really long string', 5, 10)).to.equal('A rea...');
24 | });
25 |
26 | });
27 |
--------------------------------------------------------------------------------
/test/unit/filters/calendarTrustAsHtml.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 |
5 | beforeEach(angular.mock.module('mwl.calendar'));
6 |
7 | describe('calendarTrustAsHtml', function() {
8 |
9 | var calendarTrustAsHtml, $sce;
10 |
11 | beforeEach(inject(function($filter, _$sce_) {
12 | calendarTrustAsHtml = $filter('calendarTrustAsHtml');
13 | $sce = _$sce_;
14 | }));
15 |
16 | it('should mark the text as html', function() {
17 | sinon.spy($sce, 'trustAsHtml');
18 | var htmlString = 'text ';
19 | calendarTrustAsHtml(htmlString);
20 | expect($sce.trustAsHtml).to.have.been.calledWith(htmlString);
21 | });
22 |
23 | });
24 |
--------------------------------------------------------------------------------
/test/unit/services/calendarEventTitle.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 | beforeEach(angular.mock.module('mwl.calendar'));
5 |
6 | describe('calendarEventTitle', function() {
7 |
8 | var calendarEventTitle;
9 | beforeEach(inject(function(_calendarEventTitle_) {
10 | calendarEventTitle = _calendarEventTitle_;
11 | }));
12 |
13 | it('should get the year view title', function() {
14 | expect(calendarEventTitle.yearView({
15 | title: 'Event name',
16 | startsAt: new Date('October 20, 2015 02:00:00')
17 | })).to.equal('Event name (Oct 20, 2:00 AM)');
18 | });
19 |
20 | it('should get the month view title', function() {
21 | expect(calendarEventTitle.monthView({
22 | title: 'Event name',
23 | startsAt: new Date('October 20, 2015 02:00:00')
24 | })).to.equal('Event name (02:00)');
25 | });
26 |
27 | it('should get the month view tooltip', function() {
28 | expect(calendarEventTitle.monthViewTooltip({
29 | title: 'Event name',
30 | startsAt: new Date('October 20, 2015 02:00:00')
31 | })).to.equal('02:00 - Event name');
32 | });
33 |
34 | it('should get the week view title', function() {
35 | expect(calendarEventTitle.weekView({
36 | title: 'Event name',
37 | startsAt: new Date('October 20, 2015 02:00:00')
38 | })).to.equal('Event name');
39 | });
40 |
41 | it('should get the week view tooltip', function() {
42 | expect(calendarEventTitle.weekViewTooltip({
43 | title: 'Event name',
44 | startsAt: new Date('October 20, 2015 02:00:00')
45 | })).to.equal('Event name');
46 | });
47 |
48 | it('should get the day view title', function() {
49 | expect(calendarEventTitle.dayView({
50 | title: 'A really long event title that gets truncated',
51 | startsAt: new Date('October 20, 2015 02:00:00'),
52 | height: 10
53 | })).to.equal('A really long event ...');
54 | });
55 |
56 | it('should get the day view title for an all day event', function() {
57 | expect(calendarEventTitle.dayView({
58 | title: 'A really long event title thats not truncated',
59 | startsAt: new Date('October 20, 2015 02:00:00'),
60 | height: 10,
61 | allDay: true
62 | })).to.equal('A really long event title thats not truncated');
63 | });
64 |
65 | });
66 |
--------------------------------------------------------------------------------
/test/unit/services/calendarTitle.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 | beforeEach(angular.mock.module('mwl.calendar'));
5 |
6 | describe('calendarTitle', function() {
7 |
8 | var calendarTitle;
9 | var calendarDay = new Date(2015, 4, 1);
10 |
11 | beforeEach(inject(function(_calendarTitle_) {
12 | calendarTitle = _calendarTitle_;
13 | }));
14 |
15 | it('should give the correct day title', function() {
16 | expect(calendarTitle.day(calendarDay)).to.equal('Friday 1 May, 2015');
17 | });
18 |
19 | it('should give the correct week title', function() {
20 | expect(calendarTitle.week(calendarDay)).to.equal('Week 18 of 2015');
21 | });
22 |
23 | it('should use the start of the week for the year number', function() {
24 | expect(calendarTitle.week(new Date('2016-01-01'))).to.equal('Week 53 of 2015');
25 | });
26 |
27 | it('should use the start of the iso week for the year number', function() {
28 | expect(calendarTitle.week(new Date('2018-01-01'))).to.equal('Week 1 of 2018');
29 | });
30 |
31 | it('should give the correct month title', function() {
32 | expect(calendarTitle.month(calendarDay)).to.equal('May 2015');
33 | });
34 |
35 | it('should give the correct year title', function() {
36 | expect(calendarTitle.year(calendarDay)).to.equal('2015');
37 | });
38 |
39 | });
40 |
--------------------------------------------------------------------------------
/test/unit/services/interact.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 | var interactLib = require('interactjs');
5 | beforeEach(angular.mock.module('mwl.calendar'));
6 |
7 | describe('interactjs', function() {
8 |
9 | describe('library exists', function() {
10 | it('should be the interactjs library', inject(function(interact) {
11 |
12 | expect(interact).to.eql(interactLib);
13 |
14 | }));
15 | });
16 |
17 | });
18 |
--------------------------------------------------------------------------------
/test/unit/services/moment.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var angular = require('angular');
4 | var momentLib = require('moment');
5 | beforeEach(angular.mock.module('mwl.calendar'));
6 |
7 | describe('moment', function() {
8 |
9 | it('should be the window moment object', inject(function(moment) {
10 |
11 | expect(moment).to.eql(momentLib);
12 |
13 | }));
14 |
15 | });
16 |
--------------------------------------------------------------------------------
/webpack.config.build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const webpack = require('webpack');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 |
6 | module.exports = env => {
7 |
8 | env = env || {};
9 |
10 | const MIN = process.argv.indexOf('-p') > -1;
11 | let cssFilename, jsFilename;
12 | jsFilename = cssFilename = 'angular-bootstrap-calendar';
13 | if (!env.excludeTemplates) {
14 | jsFilename += '-tpls';
15 | }
16 | if (MIN) {
17 | jsFilename += '.min';
18 | cssFilename += '.min';
19 | }
20 | jsFilename += '.js';
21 | cssFilename += '.css';
22 |
23 | function getBanner() {
24 | const pkg = require('./package.json');
25 | return `
26 | /**
27 | * ${pkg.name} - ${pkg.description}
28 | * @version v${pkg.version}
29 | * @link ${pkg.homepage}
30 | * @license ${pkg.license}
31 | */
32 | `.trim();
33 | }
34 |
35 | const config = {
36 | entry: __dirname + '/src/entry.js',
37 | output: {
38 | path: __dirname + '/dist/js',
39 | filename: jsFilename,
40 | libraryTarget: 'umd',
41 | library: 'angularBootstrapCalendarModuleName'
42 | },
43 | externals: {
44 | angular: 'angular',
45 | moment: 'moment',
46 | 'interactjs': {
47 | root: 'interact',
48 | commonjs: 'interactjs',
49 | commonjs2: 'interactjs',
50 | amd: 'interact'
51 | }
52 | },
53 | devtool: MIN ? 'source-map' : false,
54 | module: {
55 | rules: [{
56 | test: /.*\.js$/,
57 | loader: 'eslint-loader',
58 | exclude: /node_modules/,
59 | enforce: 'pre'
60 | }, {
61 | test: /\.html$/,
62 | loader: 'htmlhint-loader',
63 | exclude: /node_modules/,
64 | enforce: 'pre'
65 | }, {
66 | test: /.*\.js$/,
67 | loader: 'ng-annotate-loader',
68 | exclude: /node_modules/
69 | }, {
70 | test: /\.html$/,
71 | loader: 'html-loader',
72 | exclude: /node_modules/
73 | }, {
74 | test: /\.less/,
75 | use: ExtractTextPlugin.extract({
76 | fallback: 'style-loader',
77 | use: 'css-loader?sourceMap!less-loader?sourceMap'
78 | }),
79 | exclude: /node_modules/
80 | }]
81 | },
82 | plugins: [
83 | new webpack.NoEmitOnErrorsPlugin(),
84 | new webpack.BannerPlugin({
85 | banner: getBanner(),
86 | raw: true,
87 | entryOnly: true
88 | }),
89 | new ExtractTextPlugin('../css/' + cssFilename),
90 | new webpack.DefinePlugin({
91 | EXCLUDE_TEMPLATES: !!env.excludeTemplates
92 | })
93 | ]
94 | };
95 |
96 | if (env.excludeTemplates) {
97 | config.plugins.push(new webpack.IgnorePlugin(/templates\/(.+)\.html$/));
98 | }
99 |
100 | return config;
101 |
102 | };
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const webpack = require('webpack');
4 | const WebpackNotifierPlugin = require('webpack-notifier');
5 |
6 | module.exports = {
7 | entry: __dirname + '/src/entry.js',
8 | devtool: 'eval',
9 | output: {
10 | filename: 'angular-bootstrap-calendar.js'
11 | },
12 | module: {
13 | rules: [{
14 | test: /.*\.js$/,
15 | loader: 'eslint-loader',
16 | exclude: /node_modules/,
17 | enforce: 'pre'
18 | }, {
19 | test: /\.html$/,
20 | loader: 'htmlhint-loader',
21 | exclude: /node_modules/,
22 | enforce: 'pre'
23 | }, {
24 | test: /\.less$/,
25 | loader: 'style-loader!css-loader!less-loader',
26 | exclude: /node_modules/
27 | }, {
28 | test: /\.html$/,
29 | loader: 'html-loader',
30 | exclude: /node_modules/
31 | }]
32 | },
33 | devServer: {
34 | port: 8000,
35 | inline: true,
36 | hot: true
37 | },
38 | plugins: [
39 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
40 | new webpack.HotModuleReplacementPlugin(),
41 | new WebpackNotifierPlugin(),
42 | new webpack.DefinePlugin({
43 | EXCLUDE_TEMPLATES: false
44 | })
45 | ]
46 | };
47 |
--------------------------------------------------------------------------------