├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── app
├── app.js
├── eventCalendar-primary.scss
├── index.html
├── pages
│ └── home
│ │ ├── home.controller.js
│ │ └── home.html
└── style.css
├── bower.json
├── dist
├── angular-material-event-calendar.css
├── angular-material-event-calendar.js
├── angular-material-event-calendar.min.css
└── angular-material-event-calendar.min.js
├── gulp
├── config.js
├── cssBuild.js
├── indexBuild.js
└── jsBuild.js
├── gulpfile.js
├── index.js
├── karma.conf.js
├── package.json
└── src
├── eventCalendar-colors.scss
├── eventCalendar-theme.scss
├── eventCalendar.js
├── eventCalendar.scss
├── icons
├── ic_close_black_24px.svg
└── ic_keyboard_arrow_right_black_24px.svg
└── js
├── eventCalendar.directive.js
├── eventCalendarBuilder.service.js
├── eventCalendarMonth.directive.js
├── eventCalendarNext.directive.js
├── eventCalendarPrev.directive.js
├── eventCalendarTitle.directive.js
├── eventCalendarToday.directive.js
└── utij.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | bower_components
40 |
41 | **/.DS_Store
42 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 | ## 0.0.6 (2016-09-24)
3 |
4 | * Add `auto-height` attribute
5 |
6 |
7 |
8 | ## 0.0.6 (2016-09-07)
9 |
10 | * Add Today button directive
11 | * Add `md-create-event-click` attribute that will trigger on month cell click. This passes a `$date` paramater
12 | * Add `md-show-create-link` attribute to display a create link on cell hover
13 | * Add `md-create-disabled` to disable and hide create
14 | * Fix event display on last day of week
15 |
16 |
17 |
18 |
19 | ## 0.0.5 (2016-09-05)
20 |
21 | * Add previos and next month days to calendar view. These contain events
22 | * Update theme to default to white for header background
23 | * Add `md-primary` styling for header background and selected events. You can add the `md-primary` class to the `me-event-calendar` directive.
24 | * Fix event sorting in month view to fit more events in a day
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Ben Rubin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # angular-material-event-calendar
2 | A calendar module that is based on material design concepts.
3 | The calendar module was built to run as a standalone component, and alongside of ngMaterial. If you use this component with ngMaterial then it will use the themes you have setup and use the $$dateLocal settings to display and format the dates.
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Quick Links:
12 | * [Installation](#installaton)
13 | * [Building](#building)
14 | * [Run Tests](#tests)
15 | * [Usage](#usage)
16 | * [Colors](#colors)
17 | * [Documentation](#documentation)
18 | * [FAQ](#faq)
19 |
20 |
21 |
22 | ## Installation
23 |
24 | #### Bower
25 |
26 | Change to your project's root directory.
27 |
28 | ```bash
29 | # To install latest
30 | bower install angular-material-event-calendar
31 |
32 | # To install latest and update bower.json
33 | bower install angular-material-event-calendar --save
34 | ```
35 |
36 |
37 | #### Npm
38 |
39 | Change to your project's root directory.
40 |
41 | ```bash
42 | # To install latest
43 | npm install angular-material-event-calendar
44 |
45 | # To install latest and update package.json
46 | npm install angular-material-event-calendar --save
47 | ```
48 |
49 |
50 | #### setup
51 |
52 | install modules
53 |
54 | ```bash
55 | # install npm modules
56 | npm install
57 |
58 | # install bower components
59 | bower install
60 | ```
61 |
62 | Include the `material.components.eventCalendar` module as a dependency in your application.
63 |
64 | ```javascript
65 | // with ngMaterial
66 | angular.module('myApp', ['ngMaterial', 'material.components.eventCalendar']);
67 |
68 | // without ngMaterial
69 | angular.module('myApp', ['material.components.eventCalendar']);
70 | ```
71 |
72 |
73 |
74 |
75 | ## Building
76 |
77 | You can easily build using gulp.
78 |
79 | The built files will be created in the `dist` folder
80 |
81 | Run the **gulp** tasks:
82 |
83 | ```bash
84 | # To run locally. This will kick of the watch process
85 | # navigate to `localhost:8080`
86 | gulp
87 |
88 | # To build the js and css files to the `/build` directory
89 | gulp build
90 | ```
91 |
92 |
93 | ## Run Tests
94 |
95 | Test using Karma
96 | Run the **gulp** tasks:
97 |
98 | ```bash
99 | gulp test
100 | ```
101 |
102 |
103 |
104 |
105 | ## Usage
106 |
107 | **Example**
108 |
109 | ```html
110 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | ```
128 |
129 |
130 |
131 | ## Colors
132 |
133 | **With Angular Material**
134 |
135 | If you want to have the header and selected elements use the primary color for their backgrounds the just add the `md-primary` class
136 | ```html
137 |
140 |
141 | ```
142 |
143 |
144 | **Without Angular Material**
145 |
146 | If you want to change the header and selected event background colors you add this [scss](https://github.com/B-3PO/angular-material-event-calendar/blob/master/app/eventCalendar-primary.scss) file after the `angular-material-event-calendar.css` file
147 |
148 | Primary Color scss: [Click Here](https://github.com/B-3PO/angular-material-event-calendar/blob/master/app/eventCalendar-primary.scss)
149 |
150 |
151 | ## Documentation
152 |
153 | To add eventCalendar to you angular-material project, include the `material.components.eventCalendar` module as a dependency in your application.
154 |
155 | ```javascript
156 | angular.module('myApp', ['ngMaterial', 'material.components.eventCalendar']);
157 | ```
158 |
159 |
160 | * [Event Object](#eventobject)
161 | * [mdEventCalendar](#mdEventCalendar)
162 | * [mdEventCalendarHeader](#mdEventCalendarHeader)
163 | * [mdEventCalendarNext](#mdEventCalendarNext)
164 | * [mdEventCalendarPrev](#mdEventCalendarPrev)
165 | * [mdEventCalendarTitle](#mdEventCalendarTitle)
166 | * [mdEventCalendarToday](#mdEventCalendarToday)
167 |
168 |
169 |
170 |
171 | ### Event Object
172 |
173 | ## Event Object
174 |
175 | ```javascript
176 | {
177 | title: 'Event Title',
178 | start: new Date(),
179 | end: new Date(),
180 | allDay: false
181 | }
182 | ```
183 |
184 | #### Attributes
185 |
186 | | Param | Type | Details |
187 | | :--: | :--: | :--: |
188 | | title | string | Event Tile
|
189 | | start | date/iso | Start date
|
190 | | end | date/iso= | Optional end date
|
191 | | allDay | boolean | If set to true, no time will be displayed on event
|
192 |
193 |
194 |
195 | ### Directives
196 |
197 |
198 | ## mdEventCalendar
199 |
200 | ```html
201 |
211 | ...
212 |
213 | ```
214 |
215 | #### Attributes
216 |
217 | | Param | Type | Details |
218 | | :--: | :--: | :--: |
219 | | ng-model | model= | Optional model to hold selected event object
|
220 | | md-events | array | Array of events
|
221 | | md-label | string=title | Property name for title display
|
222 | | md-event-click | function | Function to be called on event click. You can pass in $selectedEvent
to get the event object you clicked on
|
223 | | md-create-event-click | function | Function to be called when empty area of day is clicked. You can pass in $date
to get the days date you clicked on
|
224 | | md-show-create-link | boolean | Show `Create` in the top right corner when cell is hovered over
|
225 | | md-create-disabled | boolean | Hides create link and disabled create click event
|
226 | | auto-height | number | Calendar will fill to the bottom of the window. You can pass it a number(pixels) as an offset
|
227 |
228 |
229 |
230 |
231 | ## mdEventCalendarHeader
232 |
233 | The header is a container for the previous, next, and title directives. You can also add other elements to this.
234 | ```html
235 |
236 | ...
237 |
238 | ```
239 |
240 | #### Classes
241 |
242 | | Param | Type | Details |
243 | | :--: | :--: | :--: |
244 | | md-center | css | Center content inside of header
|
245 |
246 |
247 | ## mdEventCalendarNext
248 |
249 | This is the next arrow that will advance the current view by month/week/day. You can add this the header in any order
250 | ```html
251 |
252 |
253 | ```
254 |
255 |
256 | ## mdEventCalendarPrev
257 |
258 | This is the prev arrow that will change the current view by month/week/day. You can add this the header in any order
259 | ```html
260 |
261 |
262 | ```
263 |
264 |
265 | ## mdEventCalendarTitle
266 |
267 | This title will show the appropriate title for the calendar view
268 | ```html
269 |
270 |
271 | ```
272 |
273 | ## mdEventCalendarToday
274 |
275 | A button that can be clicked to take the month to the current month. This button is disabled if you are already on the current month
276 | ```html
277 |
278 |
279 | ```
280 |
281 |
282 |
283 |
284 |
285 | # FAQ
286 |
287 | #### Do i need to use ngMaterial?
288 | No, but you will not get the lovely theme colors.
289 |
290 |
291 | #### Where is my week/day views?
292 | On their way, this component is under active development.
293 |
294 | #### Will this support mobile?
295 | Mobile is in the roadmap and will be released in future versions.
296 |
--------------------------------------------------------------------------------
/app/app.js:
--------------------------------------------------------------------------------
1 | angular.module('eventCalendarApp', [
2 | 'ngRoute',
3 | 'ngAnimate',
4 | 'ngMaterial',
5 | 'material.components.eventCalendar'
6 | ])
7 | .config(configApp);
8 |
9 |
10 | configApp.$inject = ['$routeProvider'];
11 | function configApp($routeProvider) {
12 | $routeProvider
13 | .when('/', {
14 | templateUrl: 'pages/home/home.html',
15 | controller: 'HomeController',
16 | controllerAs: 'vm'
17 | })
18 | .otherwise('/');
19 | }
20 |
--------------------------------------------------------------------------------
/app/eventCalendar-primary.scss:
--------------------------------------------------------------------------------
1 | $primary: #1458b1;
2 | $primaryContrastFont: #FFF;
3 |
4 |
5 | md-event-calendar:not(._md) {
6 | md-event-calendar-header {
7 | color: $primaryContrastFont;
8 | background: $primary;
9 |
10 | md-event-calendar-next, md-event-calendar-prev {
11 | .md-arrow svg {
12 | fill: $primaryContrastFont;
13 | }
14 | }
15 | }
16 |
17 |
18 | md-event-calendar-month {
19 | .md-event-calendar-month-row-header {
20 | color: $primaryContrastFont;
21 | background: $primary;
22 | }
23 |
24 | .md-event-calendar-month-row {
25 | .md-event-calendar-month-cell {
26 | &.today {
27 | box-shadow: inset 0px 0px 0px 1px $primary;
28 |
29 | .md-event-calendar-month-cell-content {
30 | .md-event-calendar-cell-data-label {
31 | color: $primary;
32 | }
33 | }
34 |
35 | .md-event-calendar-month-cell-divider {
36 | border-color: $primary;
37 | }
38 | }
39 | }
40 | }
41 |
42 |
43 | .md-event-calendar-cell-event {
44 | &.md-selected {
45 | color: $primaryContrastFont;
46 | background: $primary;
47 |
48 | &.md-continue-left, &.md-end-left {
49 | &:after {
50 | border-right-color: $primary;
51 | }
52 | }
53 |
54 | &.md-continue-right, &.md-start-right {
55 | &:after {
56 | border-left-color: $primary;
57 | }
58 | }
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Angular Material Test
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/pages/home/home.controller.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('eventCalendarApp')
3 | .controller('HomeController', HomeController);
4 |
5 |
6 | function HomeController($scope, $timeout) {
7 |
8 | $scope.events = [
9 | {
10 | start: getDate(-6, 10),
11 | end: getDate(-6, 11),
12 | title: 'Event 1'
13 | },
14 | {
15 | start: getDate(0, 10),
16 | end: getDate(1, 11),
17 | title: 'Event 1'
18 | },
19 | {
20 | start: getDate(1, 11),
21 | end: getDate(2, 12),
22 | title: 'Event 2'
23 | },
24 | {
25 | start: getDate(2, 12),
26 | end: getDate(3, 13),
27 | title: 'Event 3'
28 | },
29 | {
30 | start: getDate(4, 12),
31 | end: getDate(5, 13),
32 | title: 'Event 4'
33 | },
34 | {
35 | start: getDate(5, 12),
36 | end: getDate(6, 13),
37 | title: 'Event 5'
38 | },
39 | {
40 | start: getDate(6, 12),
41 | end: getDate(6, 13),
42 | title: 'Event 6'
43 | },
44 | {
45 | start: getDate(6, 12),
46 | allDay: true,
47 | title: 'Event 7'
48 | },
49 |
50 |
51 |
52 |
53 | {
54 | start: getDate(8, 12),
55 | end: getDate(8, 13),
56 | title: 'Event 5'
57 | },
58 | {
59 | start: getDate(8, 12),
60 | end: getDate(8, 13),
61 | title: 'Event 6'
62 | },
63 | {
64 | start: getDate(8, 12),
65 | allDay: true,
66 | title: 'Event 7'
67 | }
68 | ];
69 | $scope.selected = $scope.events[0];
70 |
71 | $scope.eventClicked = function (item) {
72 | console.log(item);
73 | };
74 |
75 | $scope.createClicked = function (date) {
76 | console.log(date);
77 | };
78 |
79 | function getDate(offsetDays, hour) {
80 | offsetDays = offsetDays || 0;
81 | var offset = offsetDays * 24 * 60 * 60 * 1000;
82 | var date = new Date(new Date().getTime() + offset);
83 | if (hour) { date.setHours(hour); }
84 | return date;
85 | }
86 |
87 |
88 | $scope.dis = false;
89 | // $timeout(function () {
90 | // $scope.events.push({
91 | // date: new Date(new Date().getTime() + 48 * 60 * 60 * 1000),
92 | // label: 'Event Three'
93 | // });
94 | // }, 1000)
95 | }
96 |
--------------------------------------------------------------------------------
/app/pages/home/home.html:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | overflow: hidden;
3 | max-width: 100%;
4 | max-height: 100%;
5 | }
6 |
7 | .view-container {
8 | padding-left: 12px;
9 | padding-right: 12px;
10 | }
11 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-material-event-calendar",
3 | "version": "0.1.4",
4 | "description": "Angular material event calander component",
5 | "main": [
6 | "dist/angular-material-event-calendar.js",
7 | "dist/angular-material-event-calendar.css"
8 | ],
9 | "repository": {
10 | "type": "git",
11 | "url": "git@github.com:B-3PO/angular-material-event-calendar.git"
12 | },
13 | "authors": [
14 | "Ben Rubin"
15 | ],
16 | "license": "MIT",
17 | "keywords": [
18 | "material",
19 | "material-design",
20 | "design",
21 | "angular",
22 | "component",
23 | "calendar",
24 | "event",
25 | "md"
26 | ],
27 | "ignore": [
28 | "**/.*",
29 | "node_modules",
30 | "bower_components",
31 | "app",
32 | "src",
33 | "gulp"
34 | ],
35 | "dependencies": {
36 | "angular": "^1.5.7",
37 | "angular-animate": "^1.5.7",
38 | "angular-messages": "^1.5.7",
39 | "angular-aria": "^1.5.7",
40 | "angular-material": "^1.1.0"
41 | },
42 | "devDependencies": {
43 | "angular-mocks": "^1.5.7",
44 | "angular-route": "^1.5.7"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/dist/angular-material-event-calendar.css:
--------------------------------------------------------------------------------
1 | md-event-calendar:not(._md) md-event-calendar-header {
2 | color: #666;
3 | background: #FFF;
4 | border-color: #DDD; }
5 | md-event-calendar:not(._md) md-event-calendar-header md-event-calendar-next .md-arrow svg, md-event-calendar:not(._md) md-event-calendar-header md-event-calendar-prev .md-arrow svg {
6 | fill: #666; }
7 |
8 | md-event-calendar:not(._md) .md-button:not([disabled]) {
9 | color: #333; }
10 | md-event-calendar:not(._md) .md-button:not([disabled]):hover {
11 | background: rgba(158, 158, 158, 0.2); }
12 |
13 | md-event-calendar:not(._md) .md-button[disabled] {
14 | color: #CCC; }
15 |
16 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row-header {
17 | color: #999;
18 | background: #FFF;
19 | border-color: #DDD; }
20 |
21 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row {
22 | background: #FFF;
23 | border-color: #DDD; }
24 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell-divider {
25 | border-color: #DDD; }
26 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell {
27 | border-color: #DDD; }
28 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-create-link {
29 | color: #4189b8; }
30 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-data-label {
31 | color: #999; }
32 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-event-show-more-link {
33 | color: #4189b8; }
34 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.different-month {
35 | background: #F5F5F5; }
36 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.today {
37 | box-shadow: inset 0px 0px 0px 1px #AAA; }
38 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.today .md-event-calendar-month-cell-content .md-event-calendar-cell-data-label {
39 | color: #666; }
40 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.today .md-event-calendar-month-cell-divider {
41 | border-color: #AAA; }
42 | md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell:last-child {
43 | border-color: #DDD; }
44 |
45 | md-event-calendar:not(._md) .md-event-calendar-cell-event {
46 | background: #DDD;
47 | color: #666; }
48 | md-event-calendar:not(._md) .md-event-calendar-cell-event.md-selected {
49 | color: #EEE;
50 | background: #888; }
51 | md-event-calendar:not(._md) .md-event-calendar-cell-event.md-continue-left:after, md-event-calendar:not(._md) .md-event-calendar-cell-event.md-end-left:after {
52 | border-right-color: #DDD; }
53 | md-event-calendar:not(._md) .md-event-calendar-cell-event.md-continue-right:after, md-event-calendar:not(._md) .md-event-calendar-cell-event.md-start-right:after {
54 | border-left-color: #DDD; }
55 | md-event-calendar:not(._md) .md-event-calendar-cell-event.md-selected.md-continue-left:after, md-event-calendar:not(._md) .md-event-calendar-cell-event.md-selected.md-end-left:after {
56 | border-right-color: #888; }
57 | md-event-calendar:not(._md) .md-event-calendar-cell-event.md-selected.md-continue-right:after, md-event-calendar:not(._md) .md-event-calendar-cell-event.md-selected.md-start-right:after {
58 | border-left-color: #888; }
59 |
60 | md-event-calendar:not(._md) .md-event-calendar-show-more-container .md-event-calendar-show-more-date-label {
61 | color: #999; }
62 |
63 | md-event-calendar:not(._md) .md-event-calendar-show-more-container .md-event-calendar-show-more-close svg {
64 | fill: #999; }
65 |
66 | md-event-calendar {
67 | display: block; }
68 | md-event-calendar md-event-calendar-header {
69 | -webkit-box-orient: horizontal;
70 | -webkit-box-direction: normal;
71 | -ms-flex-direction: row;
72 | flex-direction: row;
73 | display: -webkit-box;
74 | display: -ms-flexbox;
75 | display: flex;
76 | line-height: 64px;
77 | -webkit-box-align: center;
78 | -ms-flex-align: center;
79 | align-items: center;
80 | border-style: solid;
81 | border-width: 1px 1px 0 1px; }
82 | md-event-calendar md-event-calendar-header.md-center {
83 | -webkit-box-pack: center;
84 | -ms-flex-pack: center;
85 | justify-content: center; }
86 | md-event-calendar md-event-calendar-header md-event-calendar-title {
87 | display: block;
88 | min-width: 170px;
89 | text-align: center;
90 | font-size: 20px; }
91 | md-event-calendar md-event-calendar-header md-event-calendar-next, md-event-calendar md-event-calendar-header md-event-calendar-prev {
92 | display: block; }
93 | md-event-calendar md-event-calendar-header md-event-calendar-next .md-arrow, md-event-calendar md-event-calendar-header md-event-calendar-prev .md-arrow {
94 | cursor: pointer;
95 | height: 24px;
96 | width: 24px; }
97 | md-event-calendar md-event-calendar-header md-event-calendar-next .md-arrow.md-left-arrow, md-event-calendar md-event-calendar-header md-event-calendar-prev .md-arrow.md-left-arrow {
98 | -webkit-transform: rotate(180deg);
99 | transform: rotate(180deg); }
100 |
101 | md-event-calendar .md-event-calendar-month-cell-content .md-event-calendar-create-link {
102 | opacity: 0; }
103 |
104 | md-event-calendar .md-event-calendar-month-cell-content:hover .md-event-calendar-create-link {
105 | opacity: 1; }
106 |
107 | md-event-calendar md-event-calendar-month.md-event-hover .md-event-calendar-month-cell-content .md-event-calendar-create-link {
108 | opacity: 0; }
109 |
110 | md-event-calendar md-event-calendar-month.fitted {
111 | display: -webkit-box;
112 | display: -ms-flexbox;
113 | display: flex;
114 | -webkit-box-orient: vertical;
115 | -webkit-box-direction: normal;
116 | -ms-flex-direction: column;
117 | flex-direction: column; }
118 |
119 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row-header {
120 | display: -webkit-box;
121 | display: -ms-flexbox;
122 | display: flex;
123 | -webkit-box-orient: horizontal;
124 | -webkit-box-direction: normal;
125 | -ms-flex-direction: row;
126 | flex-direction: row;
127 | min-height: 36px;
128 | height: 36px;
129 | -webkit-box-align: end;
130 | -ms-flex-align: end;
131 | align-items: flex-end;
132 | font-size: 12px;
133 | font-weight: 500;
134 | padding-bottom: 12px;
135 | border-style: solid;
136 | border-width: 0 1px 1px 1px; }
137 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row-header .md-event-calendar-month-cell-header {
138 | -webkit-box-flex: 1;
139 | -ms-flex: 1;
140 | flex: 1;
141 | padding-left: 6px; }
142 |
143 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row {
144 | -webkit-box-flex: 1;
145 | -ms-flex: 1;
146 | flex: 1;
147 | display: -webkit-box;
148 | display: -ms-flexbox;
149 | display: flex;
150 | -webkit-box-orient: horizontal;
151 | -webkit-box-direction: normal;
152 | -ms-flex-direction: row;
153 | flex-direction: row;
154 | border-style: solid;
155 | border-width: 0 0 1px 0; }
156 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell {
157 | position: relative;
158 | -webkit-box-flex: 1;
159 | -ms-flex: 1;
160 | flex: 1; }
161 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-spacer {
162 | margin-top: 100%; }
163 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-divider {
164 | position: absolute;
165 | top: 0;
166 | bottom: 0;
167 | left: 0;
168 | border-style: solid;
169 | border-width: 0 1px 0 0; }
170 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content {
171 | position: absolute;
172 | top: 0;
173 | bottom: 0;
174 | left: 0;
175 | right: 0; }
176 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-create-link {
177 | -ms-flex-item-align: center;
178 | align-self: center;
179 | text-transform: uppercase;
180 | font-size: 14px;
181 | font-weight: 500;
182 | padding-right: 12px;
183 | cursor: pointer;
184 | -webkit-transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
185 | transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); }
186 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-data-label {
187 | font-size: 13px;
188 | padding: 8px;
189 | -webkit-box-flex: 1;
190 | -ms-flex: 1;
191 | flex: 1; }
192 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-event-spacer {
193 | margin: 4px 0 4px 0;
194 | height: 23px; }
195 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-event-show-more-link {
196 | font-size: 13px;
197 | padding: 4px;
198 | padding-left: 8px;
199 | cursor: pointer; }
200 | md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell:last-child {
201 | border-style: solid;
202 | border-width: 0 1px 0 0; }
203 |
204 | md-event-calendar .md-event-calendar-cell-event {
205 | font-size: 12px;
206 | min-height: 15px;
207 | padding: 4px;
208 | cursor: pointer; }
209 | md-event-calendar .md-event-calendar-cell-event.md-single {
210 | margin: 4px;
211 | border-radius: 2px;
212 | text-overflow: ellipsis;
213 | white-space: nowrap;
214 | overflow: hidden; }
215 | md-event-calendar .md-event-calendar-cell-event.md-start {
216 | margin: 4px 0 4px 4px;
217 | border-radius: 2px 0 0 2px;
218 | white-space: nowrap;
219 | z-index: 1;
220 | position: relative; }
221 | md-event-calendar .md-event-calendar-cell-event.md-start-right {
222 | margin: 4px 13px 4px 4px;
223 | border-radius: 2px 0 0 2px; }
224 | md-event-calendar .md-event-calendar-cell-event.md-end {
225 | margin: 4px 4px 4px 0;
226 | border-radius: 0 2px 2px 0; }
227 | md-event-calendar .md-event-calendar-cell-event.md-end-left {
228 | margin: 4px 4px 4px 13px;
229 | border-radius: 0 2px 2px 0; }
230 | md-event-calendar .md-event-calendar-cell-event.md-continue, md-event-calendar .md-event-calendar-cell-event.md-continue-both {
231 | margin: 4px 0 4px 0;
232 | border-radius: 0; }
233 | md-event-calendar .md-event-calendar-cell-event.md-continue-right {
234 | margin: 4px 13px 4px 0;
235 | border-radius: 0;
236 | white-space: nowrap; }
237 | md-event-calendar .md-event-calendar-cell-event.md-continue-left {
238 | margin: 4px 0 4px 13px;
239 | border-radius: 0;
240 | white-space: nowrap; }
241 | md-event-calendar .md-event-calendar-cell-event.md-continue-right:after, md-event-calendar .md-event-calendar-cell-event.md-start-right:after {
242 | content: '';
243 | position: absolute;
244 | height: 0;
245 | width: 0;
246 | right: 0;
247 | margin-top: -4px;
248 | border-top: 12px solid transparent;
249 | border-bottom: 11.5px solid transparent;
250 | border-left: 13px solid #EEE; }
251 | md-event-calendar .md-event-calendar-cell-event.md-continue-left:after, md-event-calendar .md-event-calendar-cell-event.md-end-left:after {
252 | content: '';
253 | position: absolute;
254 | height: 0;
255 | width: 0;
256 | left: 0;
257 | margin-top: -4px;
258 | border-top: 12px solid transparent;
259 | border-bottom: 11.5px solid transparent;
260 | border-right: 13px solid #EEE; }
261 | md-event-calendar .md-event-calendar-cell-event .md-event-calendar-cell-event-time {
262 | font-weight: 500;
263 | padding-right: 6px;
264 | pointer-events: none; }
265 | md-event-calendar .md-event-calendar-cell-event span {
266 | pointer-events: none; }
267 |
268 | md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-single, md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-end, md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-start-right {
269 | padding-left: 16px; }
270 |
271 | md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-single, md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-end, md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-start-right {
272 | margin-left: 0; }
273 |
274 | md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-single, md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-end, md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-end-left {
275 | margin-right: 0; }
276 |
277 | md-event-calendar .md-event-calendar-show-more-container {
278 | opacity: 0;
279 | position: absolute;
280 | top: 0;
281 | left: 1px;
282 | width: 180px;
283 | background: #FFF;
284 | padding: 12px;
285 | padding-top: 7px;
286 | border-radius: 2px;
287 | z-index: 9;
288 | box-shadow: 0 7px 8px -4px rgba(0, 0, 0, 0.2), 0 13px 19px 2px rgba(0, 0, 0, 0.14), 0 5px 24px 4px rgba(0, 0, 0, 0.12); }
289 | md-event-calendar .md-event-calendar-show-more-container .md-event-calendar-show-more-content {
290 | position: relative; }
291 | md-event-calendar .md-event-calendar-show-more-container.show {
292 | opacity: 1; }
293 | md-event-calendar .md-event-calendar-show-more-container.show:not(.no-transition) {
294 | -webkit-transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
295 | transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); }
296 | md-event-calendar .md-event-calendar-show-more-container .md-event-calendar-show-more-close {
297 | display: -webkit-box;
298 | display: -ms-flexbox;
299 | display: flex;
300 | position: absolute;
301 | top: 6px;
302 | right: 7px;
303 | width: 16px;
304 | height: 16px;
305 | cursor: pointer; }
306 | md-event-calendar .md-event-calendar-show-more-container .md-event-calendar-show-more-close svg {
307 | pointer-events: none; }
308 | md-event-calendar .md-event-calendar-show-more-container .md-event-calendar-show-more-date-label {
309 | font-size: 13px;
310 | padding: 6px;
311 | margin-left: -11px;
312 | margin-top: -5px; }
313 |
314 | md-event-calendar.md-create-disabled md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-create-link {
315 | display: none; }
316 |
--------------------------------------------------------------------------------
/dist/angular-material-event-calendar.js:
--------------------------------------------------------------------------------
1 | (function(){"use strict";/**
2 | * @ngdoc module
3 | * @name material.components.eventCalendar
4 | *
5 | * @description
6 | * Calendar Component
7 | */
8 | addEventCalendarTheme.$inject = ["$injector", "$provide", "EVENT_CALENDAR_THEME"];
9 | angular
10 | .module('material.components.eventCalendar', [])
11 | .config(addEventCalendarTheme);
12 |
13 |
14 | /*@ngInject*/
15 | function addEventCalendarTheme($injector, $provide, EVENT_CALENDAR_THEME) {
16 | var $mdThemingProvider;
17 |
18 | // if using angular material, then register the event theme css
19 | if ($injector.has('$mdThemingProvider')) {
20 | $mdThemingProvider = $injector.get('$mdThemingProvider');
21 | $mdThemingProvider.registerStyles(EVENT_CALENDAR_THEME);
22 | } else {
23 | $provide.decorator('$$rAF', ["$delegate", rAFDecorator]);
24 | }
25 | }
26 |
27 |
28 | // polly fill rAF throttle if not using angular material
29 | function rAFDecorator($delegate) {
30 | $delegate.throttle = function(cb) {
31 | var queuedArgs, alreadyQueued, queueCb, context;
32 | return function debounced() {
33 | queuedArgs = arguments;
34 | context = this;
35 | queueCb = cb;
36 | if (!alreadyQueued) {
37 | alreadyQueued = true;
38 | $delegate(function() {
39 | queueCb.apply(context, Array.prototype.slice.call(queuedArgs));
40 | alreadyQueued = false;
41 | });
42 | }
43 | };
44 | };
45 | return $delegate;
46 | }
47 | }());
48 | (function(){"use strict";angular.module("material.components.eventCalendar").run(["$templateCache", function($templateCache) {$templateCache.put("icons/ic_close_black_24px.svg","\n \n \n ");
49 | $templateCache.put("icons/ic_keyboard_arrow_right_black_24px.svg","\n \n \n \n");}]);}());
50 | (function(){"use strict";angular.module("material.components.eventCalendar")
51 |
52 | .constant("EVENT_CALENDAR_THEME", "md-event-calendar._md md-event-calendar-header {\n color: '{{foreground-1}}';\n background: '{{background-hue-1}}';\n border-color: '{{foreground-4}}'; }\n md-event-calendar._md md-event-calendar-header md-event-calendar-next .md-arrow svg, md-event-calendar._md md-event-calendar-header md-event-calendar-prev .md-arrow svg {\n fill: '{{foreground-2}}'; }\n\nmd-event-calendar._md md-event-calendar-month .md-event-calendar-month-row-header {\n color: '{{foreground-3}}';\n background: '{{background-hue-1}}';\n border-color: '{{foreground-4}}'; }\n\nmd-event-calendar._md md-event-calendar-month .md-event-calendar-month-row {\n background: '{{background-hue-1}}';\n border-color: '{{foreground-4}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell-divider {\n border-color: '{{foreground-4}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell {\n border-color: '{{foreground-4}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-create-link {\n color: '{{primary-default}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-data-label {\n color: '{{foreground-3}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-event-show-more-link {\n color: '{{primary-default}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.different-month {\n background: '{{background-hue-2}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.today {\n box-shadow: inset 0px 0px 0px 1px '{{primary-default}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.today .md-event-calendar-month-cell-content .md-event-calendar-cell-data-label {\n color: '{{primary-default}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell:last-child {\n border-color: '{{foreground-4}}'; }\n\nmd-event-calendar._md .md-event-calendar-cell-event {\n background: '{{foreground-4}}';\n color: '{{background-900}}'; }\n md-event-calendar._md .md-event-calendar-cell-event.md-selected {\n color: #EEE;\n background: '{{primary-default}}'; }\n md-event-calendar._md .md-event-calendar-cell-event.md-continue-left:after, md-event-calendar._md .md-event-calendar-cell-event.md-end-left:after {\n border-right-color: '{{foreground-4}}'; }\n md-event-calendar._md .md-event-calendar-cell-event.md-continue-right:after, md-event-calendar._md .md-event-calendar-cell-event.md-start-right:after {\n border-left-color: '{{foreground-4}}'; }\n md-event-calendar._md .md-event-calendar-cell-event.md-selected.md-continue-left:after, md-event-calendar._md .md-event-calendar-cell-event.md-selected.md-end-left:after {\n border-right-color: '{{primary-default}}'; }\n md-event-calendar._md .md-event-calendar-cell-event.md-selected.md-continue-right:after, md-event-calendar._md .md-event-calendar-cell-event.md-selected.md-start-right:after {\n border-left-color: '{{primary-default}}'; }\n\nmd-event-calendar._md .md-event-calendar-show-more-container .md-event-calendar-show-more-date-label {\n color: '{{foreground-3}}'; }\n\nmd-event-calendar._md .md-event-calendar-show-more-container .md-event-calendar-show-more-close svg {\n fill: '{{foreground-1}}'; }\n\nmd-event-calendar._md.md-primary md-event-calendar-header {\n color: '{{background-100}}';\n background: '{{primary-default}}'; }\n md-event-calendar._md.md-primary md-event-calendar-header md-event-calendar-next .md-arrow svg, md-event-calendar._md.md-primary md-event-calendar-header md-event-calendar-prev .md-arrow svg {\n fill: '{{background-100}}'; }\n\nmd-event-calendar._md.md-primary md-event-calendar-month .md-event-calendar-month-row-header {\n color: '{{background-100}}';\n background: '{{primary-default}}'; }\n")
53 |
54 | ;}());
55 | (function(){"use strict";
56 | eventCalendarDirective.$inject = ["$injector", "$parse"];angular
57 | .module('material.components.eventCalendar')
58 | .directive('mdEventCalendar', eventCalendarDirective);
59 |
60 |
61 | /**
62 | * @ngdoc directive
63 | * @name mdEventCalendar
64 | * @module material.components.eventCalendar
65 | *
66 | * @restrict E
67 | **/
68 | function eventCalendarDirective($injector, $parse) {
69 | controller.$inject = ["$$mdEventCalendarUtil", "$element", "$attrs"];
70 | var $mdTheming = $injector.has('$mdTheming') ? $injector.get('$mdTheming') : undefined;
71 | var directive = {
72 | restrict: 'E',
73 | require: ['mdEventCalendar', '?ngModel'],
74 | scope: {
75 | events: '=mdEvents'
76 | },
77 | compile: compile,
78 | controller: controller,
79 | controllerAs: 'mdEventCalendar',
80 | bindToController: true
81 | };
82 | return directive;
83 |
84 |
85 | function compile(tElement, tAttr) {
86 | var eventClickFunc = tAttr.mdEventClick ? $parse(tAttr.mdEventClick, null, true) : undefined;
87 | var createEventClickFunc = tAttr.mdCreateEventClick ? $parse(tAttr.mdCreateEventClick, null, true) : undefined;
88 | var mdCreateDisabled = tAttr.mdCreateDisabled ? $parse(tAttr.mdCreateDisabled) : undefined;
89 | tElement.append(' ');
90 |
91 | return function postLink(scope, element, attrs, ctrls) {
92 | var createDisabled = false;
93 | var mdEventCalendarCtrl = ctrls[0];
94 | var ngModelCtrl = ctrls[1];
95 | if ($mdTheming) {
96 | element.addClass('_md');
97 | $mdTheming(element);
98 | }
99 |
100 | mdEventCalendarCtrl.isCreateDisabled = isCreateDisabled;
101 | mdEventCalendarCtrl.callEventClick = callEventClick;
102 | mdEventCalendarCtrl.createEventClick = createEventClick;
103 |
104 | if (ngModelCtrl) {
105 | ngModelCtrl.$render = render;
106 | mdEventCalendarCtrl.ngModelCtrl = ngModelCtrl;
107 | }
108 |
109 |
110 | function render() {
111 | var viewValue = ngModelCtrl.$viewValue || ngModelCtrl.$modelValue || [];
112 | mdEventCalendarCtrl.selectedEvents = [].concat(viewValue);
113 | }
114 |
115 |
116 | function callEventClick(e, eventItem) {
117 | if (!attrs.mdEventClick) { return; }
118 | eventClickFunc(scope.$parent, {$event: e, $selectedEvent: eventItem});
119 | }
120 |
121 | function createEventClick(e, date) {
122 | if (!attrs.mdCreateEventClick) { return; }
123 | createEventClickFunc(scope.$parent, {$event: e, $date: date});
124 | }
125 |
126 | function isCreateDisabled() {
127 | return createDisabled;
128 | }
129 |
130 | // watch for create being disabled
131 | if (mdCreateDisabled) {
132 | scope.$watch(function () { return mdCreateDisabled(scope.$parent); }, function (value) {
133 | createDisabled = value;
134 | element.toggleClass('md-create-disabled', value);
135 | });
136 |
137 | // if no string was given check to see if the attr exists
138 | } else if (tAttr.mdCreateDisabled !== undefined) {
139 | createDisabled = true;
140 | element.addClass('md-create-disabled');
141 | }
142 | };
143 | }
144 |
145 |
146 | /*@ngInject*/
147 | function controller($$mdEventCalendarUtil, $element, $attrs) {
148 | /*jshint validthis:true*/
149 | var vm = this;
150 |
151 | vm.$element = $element;
152 | vm.labelProperty = $attrs.mdLabel || 'title';
153 | vm.selectedEvents = [];
154 | vm.today = $$mdEventCalendarUtil.createDateAtMidnight();
155 | vm.date = $$mdEventCalendarUtil.createDateAtMidnight();
156 | vm.isToday = $$mdEventCalendarUtil.isSameDay(vm.date, new Date());
157 | vm.monthDisplay = $$mdEventCalendarUtil.months[vm.date.getMonth()];
158 | vm.yearDisplay = vm.date.getFullYear();
159 | vm.isTodayDisabled = true;
160 | vm.showCreateLink = $attrs.mdShowCreateLink !== undefined && $attrs.mdShowCreateLink !== 'false';
161 | vm.nextMonth = nextMonth;
162 | vm.previousMonth = previousMonth;
163 | vm.selectEvent = selectEvent;
164 | vm.setToday = setToday;
165 | vm.autoHeight = $attrs.autoHeight !== undefined;
166 | vm.fitted = $attrs.fitted !== undefined;
167 | vm.offset = vm.autoHeight === false || $attrs.autoHeight === '' || isNaN($attrs.autoHeight.replace('px', '')) ? 0 : parseInt($attrs.autoHeight.replace('px', ''));
168 |
169 |
170 | function nextMonth() {
171 | vm.date = $$mdEventCalendarUtil.getDateInNextMonth(vm.date);
172 | vm.monthDisplay = $$mdEventCalendarUtil.months[vm.date.getMonth()];
173 | vm.yearDisplay = vm.date.getFullYear();
174 | vm.isTodayDisabled = vm.date.getMonth() === (new Date()).getMonth();
175 | }
176 |
177 |
178 | function previousMonth() {
179 | vm.date = $$mdEventCalendarUtil.getDateInPreviousMonth(vm.date);
180 | vm.monthDisplay = $$mdEventCalendarUtil.months[vm.date.getMonth()];
181 | vm.yearDisplay = vm.date.getFullYear();
182 | vm.isTodayDisabled = vm.date.getMonth() === (new Date()).getMonth();
183 | }
184 |
185 | function setToday() {
186 | vm.date = new Date();
187 | vm.monthDisplay = $$mdEventCalendarUtil.months[vm.date.getMonth()];
188 | vm.yearDisplay = vm.date.getFullYear();
189 | vm.isTodayDisabled = true;
190 | }
191 |
192 |
193 | function selectEvent(e, id) {
194 | // TODO create hashkeys for all events and store in reference object
195 | var value = vm.events.filter(function (item) {
196 | return item.$$mdEventId === id;
197 | });
198 |
199 | if (vm.ngModelCtrl) {
200 | vm.ngModelCtrl.$setViewValue(value[0]);
201 | vm.ngModelCtrl.$render();
202 | }
203 | vm.callEventClick(e, value[0]);
204 |
205 | return true;
206 | }
207 | }
208 | }
209 | }());
210 | (function(){"use strict";
211 | mdEventCalendarBuilderService.$inject = ["$$mdEventCalendarUtil", "$templateCache"];angular
212 | .module('material.components.eventCalendar')
213 | .factory('$$mdEventCalendarBuilder', mdEventCalendarBuilderService);
214 |
215 |
216 | var nextId = 0;
217 |
218 | /**
219 | * @ngdoc service
220 | * @name $$mdEventCalendarBuilder
221 | * @module material.components.eventCalendar
222 | **/
223 | /*@ngInject*/
224 | function mdEventCalendarBuilderService($$mdEventCalendarUtil, $templateCache) {
225 | var service = {
226 | month: month,
227 | showMore: showMore
228 | };
229 | return service;
230 |
231 |
232 |
233 | function showMore(opts) {
234 | var date = opts.date;
235 | var selected = opts.selected || [];
236 | var events = opts.events ? filterEventsOnDay(date, opts.events) : [];
237 | var labelProperty = opts.labelProperty;
238 | var showMoreBody = document.createDocumentFragment();
239 | var container = document.createElement('div');
240 | container.classList.add('md-event-calendar-show-more-container');
241 | var content = document.createElement('div');
242 | content.classList.add('md-event-calendar-show-more-content');
243 | var dateLabel = document.createElement('div');
244 | dateLabel.classList.add('md-event-calendar-show-more-date-label');
245 | dateLabel.textContent = $$mdEventCalendarUtil.dates[date.getDate()];
246 | var closeButton = document.createElement('div');
247 | closeButton.classList.add('md-event-calendar-show-more-close');
248 | closeButton.innerHTML = $templateCache.get('icons/ic_close_black_24px.svg');
249 | closeButton.setAttribute('md-show-more-close', 'true');
250 | container.appendChild(dateLabel);
251 | container.appendChild(closeButton);
252 | container.appendChild(content);
253 | showMoreBody.appendChild(container);
254 |
255 | events.forEach(function (item) {
256 | var eventElement;
257 | var isStartThisDay = $$mdEventCalendarUtil.isSameDay(date, item.start);
258 | var isEndThisDay = $$mdEventCalendarUtil.isValidDate(item.end) ? $$mdEventCalendarUtil.isSameDay(date, item.end) : true;
259 | var eventOptions = {
260 | labelProperty: labelProperty,
261 | selected: selected
262 | };
263 |
264 | if (isStartThisDay && isEndThisDay) {
265 | eventElement = createEventElement({className: 'single', hasLabel: true}, item, eventOptions);
266 | } else if (isStartThisDay) {
267 | eventElement = createEventElement({className: 'start-right', hasLabel: true}, item, eventOptions);
268 | } else if (isEndThisDay) {
269 | eventElement = createEventElement({className: 'end-left', hasLabel: true}, item, eventOptions);
270 | } else {
271 | eventElement = createEventElement({className: 'continue', hasLabel: true}, item, eventOptions);
272 | }
273 |
274 | content.appendChild(eventElement);
275 | });
276 |
277 |
278 | var bounds = opts.cell.getBoundingClientRect();
279 | var cellTop = opts.cell.offsetTop;
280 | var cellLeft = opts.cell.offsetLeft;
281 | container.style.top = cellTop+'px';
282 | container.style.left = cellLeft+'px';
283 |
284 | return showMoreBody;
285 | }
286 |
287 |
288 | function filterEventsOnDay(date, events) {
289 | return !events || !events.length ? [] : events.filter(function (item) {
290 | return $$mdEventCalendarUtil.isDateWithinRange(date, item.start, item.end || item.start);
291 | }).sort(function(a, b) {
292 | a = new Date(a.start);
293 | b = new Date(b.start);
294 | return a > b ? 1 : a < b ? -1 : 0;
295 | });
296 | }
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 | function month(options) {
310 | var calendarStartDate;
311 | var lastCalendarDayNum;
312 | var d = 0;
313 | var rowNumber = 1;
314 | var firstCalendarDay = true;
315 | var lastCalendarDay = false;
316 | var today = $$mdEventCalendarUtil.createDateAtMidnight();
317 | var date = $$mdEventCalendarUtil.isValidDate(options.date) ? options.date : new Date();
318 | var firstDayOfMonth = $$mdEventCalendarUtil.getFirstDateOfMonth(date);
319 | var firstDayOfTheWeek = (firstDayOfMonth.getDay() + 7) % 7;
320 | var numberOfDaysInMonth = $$mdEventCalendarUtil.getNumberOfDaysInMonth(date);
321 | var events = filterCurrentCalendar(date, options.events);
322 | events.forEach(cleanEvent);
323 | var selected = options.selected || [];
324 | var monthElement = createMonthElement();
325 | var row = createRowElement();
326 | monthElement.appendChild(row);
327 | var cellSize = options.cellHeight - 48;
328 | var maxEvents = Math.floor(cellSize / 24);
329 |
330 |
331 | // days from last month
332 | if (firstDayOfTheWeek > 0) {
333 | calendarStartDate = $$mdEventCalendarUtil.getFirstDateOfMonth(date);
334 | calendarStartDate.setDate(calendarStartDate.getDate() - firstDayOfTheWeek);
335 | while (d < firstDayOfTheWeek) {
336 | row.appendChild(createCellElement(getCellOptions(calendarStartDate, d, true)));
337 | firstCalendarDay = false;
338 | d += 1;
339 | calendarStartDate.setDate(calendarStartDate.getDate() + 1);
340 | }
341 | }
342 |
343 |
344 |
345 | // Add a cell for each day of the month, keeping track of the day of the week so that
346 | // we know when to start a new row.
347 | var dayOfWeek = firstDayOfTheWeek;
348 | var iterationDate = firstDayOfMonth;
349 | d = 1;
350 | while (d <= numberOfDaysInMonth) {
351 | // If we've reached the end of the week, start a new row.
352 | if (dayOfWeek === 7) {
353 | dayOfWeek = 0;
354 | row = createRowElement();
355 | firstCalendarDay = false;
356 | monthElement.appendChild(row);
357 | }
358 |
359 | if (dayOfWeek === 6 && d === numberOfDaysInMonth) {
360 | lastCalendarDay = true;
361 | }
362 |
363 | iterationDate.setDate(d);
364 | row.appendChild(createCellElement(getCellOptions(iterationDate, dayOfWeek)));
365 | firstCalendarDay = false;
366 | dayOfWeek += 1;
367 | d += 1;
368 | }
369 |
370 |
371 | lastCalendarDayNum = d;
372 | // fill in the rest of the row with next month
373 | while (row.childNodes.length < 7) {
374 | if (dayOfWeek === 6) {
375 | lastCalendarDay = true;
376 | }
377 | iterationDate.setDate((d - lastCalendarDayNum) + 1);
378 | row.appendChild(createCellElement(getCellOptions(iterationDate, dayOfWeek, true)));
379 | dayOfWeek += 1;
380 | d += 1;
381 | }
382 |
383 |
384 | return monthElement;
385 |
386 |
387 | function getCellOptions(cellDate, dayOfWeek, differentMonth) {
388 | return {
389 | date: cellDate, // date for day on calendar
390 | today: today, // todays date at midnight
391 | dayOfWeek: dayOfWeek, // 0-6 (sun-sat)
392 | differentMonth: differentMonth || false, // previous or next month overflow days
393 | events: events, // events arr
394 | isFirstDay: firstCalendarDay, // is first day of current month view. not the first day of month(unless that is sunday)
395 | isLastDay: lastCalendarDay, // last day of calenday. not last day of month (unless sat)
396 | maxEvents: maxEvents, // max events that can be displayed in a day cell. based on cell size
397 | selected: selected, // array of selected events. from ngModel
398 | labelProperty: options.labelProperty, // name of the label property. default: title
399 | showCreateLink: options.showCreateLink // show create link on hover of day cell
400 | };
401 | }
402 | }
403 |
404 |
405 |
406 | function createCellElement(options) {
407 | var cell = document.createElement('div');
408 | cell.classList.add('md-event-calendar-month-cell');
409 | cell.setAttribute('md-date', options.date);
410 | if (options.differentMonth === true) { cell.classList.add('different-month'); }
411 | if ($$mdEventCalendarUtil.isSameDay(options.date, options.today)) { cell.classList.add('today'); }
412 |
413 | var cellSpacer = document.createElement('div');
414 | cellSpacer.classList.add('md-event-calendar-month-cell-spacer');
415 | cell.appendChild(cellSpacer);
416 |
417 | var divider = document.createElement('div');
418 | divider.classList.add('md-event-calendar-month-cell-divider');
419 | cell.appendChild(divider);
420 |
421 | var cellContent = document.createElement('div');
422 | cellContent.setAttribute('md-create-event', '');
423 | cellContent.classList.add('md-event-calendar-month-cell-content');
424 | cell.appendChild(cellContent);
425 |
426 | var cellHeader = document.createElement('div');
427 | cellHeader.setAttribute('md-create-event', '');
428 | cellHeader.classList.add('layout-row');
429 | cellContent.appendChild(cellHeader);
430 |
431 | var dateLabel = document.createElement('div');
432 | dateLabel.setAttribute('md-create-event', '');
433 | dateLabel.classList.add('md-event-calendar-cell-data-label');
434 | dateLabel.textContent = $$mdEventCalendarUtil.dates[options.date.getDate()];
435 | cellHeader.appendChild(dateLabel);
436 |
437 | if (options.showCreateLink === true) {
438 | var createLink = document.createElement('div');
439 | createLink.setAttribute('md-create-event', '');
440 | createLink.classList.add('md-event-calendar-create-link');
441 | createLink.textContent = 'Create';
442 | cellHeader.appendChild(createLink);
443 | }
444 |
445 | createEventElements(cellContent, options);
446 |
447 | return cell;
448 | }
449 |
450 |
451 |
452 | function createEventElements(cellContent, options) {
453 | var i;
454 | var place = 0;
455 | var hasEvents = false;
456 | var matchingEvents = getEventsInRange(options.date, options.events);
457 | matchingEvents = setEventPlaces(matchingEvents, options.dayOfWeek);
458 | matchingEvents.every(function (eventItem, pos) {
459 | var type = getEventDisplayType(eventItem, options);
460 | var placeDiff = eventItem.$$place - place;
461 | hasEvents = true;
462 | place = eventItem.$$place + 1;
463 | i = 0;
464 | // add spacer items for overflow events from last day
465 | while (i < placeDiff) {
466 | if (place >= options.maxEvents) {
467 | cellContent.appendChild(createShowMore(matchingEvents.length - pos, options.date));
468 | return false;
469 | }
470 | cellContent.appendChild(createEventSpacerElement());
471 | i += 1;
472 | }
473 |
474 | if (place >= options.maxEvents) {
475 | cellContent.appendChild(createShowMore(matchingEvents.length - pos, options.date));
476 | return false;
477 | }
478 | cellContent.appendChild(createEventElement(type, eventItem, options));
479 | return true;
480 | });
481 |
482 | if (hasEvents === true) {
483 | cellContent.classList.add('md-has-events');
484 | }
485 | }
486 |
487 |
488 | function createShowMore(num, date) {
489 | var showMoreElement = document.createElement('div');
490 | showMoreElement.classList.add('md-event-calendar-cell-event-show-more-link');
491 | showMoreElement.textContent = num+' more';
492 | showMoreElement.setAttribute('md-show-more', date.toISOString());
493 | return showMoreElement;
494 | }
495 |
496 |
497 | function createEventSpacerElement() {
498 | var spacer = document.createElement('div');
499 | spacer.classList.add('md-event-calendar-cell-event-spacer');
500 | return spacer;
501 | }
502 |
503 | function createEventElement(type, eventItem, options) {
504 | var hash = getHashValue(eventItem);
505 | var eventElement = document.createElement('div');
506 | eventElement.setAttribute('md-event-id', hash);
507 | eventElement.classList.add('md-event-calendar-cell-event');
508 | eventElement.classList.add('md-'+type.className);
509 | if (eventItem.customClass) { eventElement.classList.add(eventItem.customClass); }
510 |
511 | if (type.hasLabel === true) {
512 | // do not show time for allDay events
513 | if (type.allDay !== true) {
514 | var dateLabelTime = document.createElement('span');
515 | dateLabelTime.classList.add('md-event-calendar-cell-event-time');
516 | dateLabelTime.textContent = $$mdEventCalendarUtil.formatEventTime(eventItem.start);
517 | eventElement.appendChild(dateLabelTime);
518 | }
519 |
520 | var dateLabelText = document.createElement('span');
521 | dateLabelText.textContent = eventItem[options.labelProperty];
522 | eventElement.appendChild(dateLabelText);
523 | }
524 |
525 | options.selected.every(function (sel) {
526 | if (sel.$$mdEventId !== undefined && sel.$$mdEventId === eventItem.$$mdEventId) {
527 | eventElement.classList.add('md-selected');
528 | return false;
529 | }
530 | return true;
531 | });
532 |
533 | return eventElement;
534 | }
535 |
536 |
537 |
538 | function getHashValue(value) {
539 | if (angular.isObject(value)) {
540 | return 'object_' + (value.$$mdEventId || (value.$$mdEventId = ++nextId));
541 | }
542 | return 'id_' + (++nextId);
543 | }
544 |
545 |
546 | function getEventDisplayType(item, options) {
547 | var className;
548 | var hasLabel;
549 |
550 | var isStartThisDay = $$mdEventCalendarUtil.isSameDay(options.date, item.start);
551 | var isEndThisDay = $$mdEventCalendarUtil.isValidDate(item.end) ? $$mdEventCalendarUtil.isSameDay(options.date, item.end) : true;
552 |
553 | // single day event
554 | if (isStartThisDay && (options.allDay || isEndThisDay)) {
555 | className = 'single';
556 | hasLabel = true;
557 |
558 | // starts today on last day of week
559 | } else if (isStartThisDay && options.dayOfWeek === 6) {
560 | className = 'start-right';
561 | hasLabel = true;
562 |
563 | // starts today
564 | } else if (isStartThisDay) {
565 | className = 'start';
566 | hasLabel = true;
567 |
568 | // ends on sunday
569 | } else if (isEndThisDay && options.dayOfWeek === 0) {
570 | className = 'end-left';
571 | hasLabel = true;
572 |
573 | // last day of event
574 | } else if (isEndThisDay) {
575 | className = 'end';
576 | hasLabel = options.isFirstDay; // add label if event is continuing from last month
577 |
578 | // continuation on sunday
579 | } else if (options.dayOfWeek === 0) {
580 | className = 'continue-left';
581 | hasLabel = true;
582 |
583 | // continue on sat
584 | } else if (options.dayOfWeek === 6) {
585 | className = 'continue-right';
586 | hasLabel = false;
587 |
588 | // continuation
589 | } else {
590 | className = 'continue';
591 | hasLabel = false;
592 | }
593 |
594 | return {
595 | className: className,
596 | hasLabel: hasLabel,
597 | allDay: item.allDay || false
598 | };
599 | }
600 |
601 | function getEventsInRange(date, events) {
602 | return events.filter(function (item) {
603 | return $$mdEventCalendarUtil.isDateWithinRange(date, item.start, item.end || item.start);
604 | });
605 | }
606 |
607 | function setEventPlaces(events, dayOfWeek) {
608 | var takenPlaces = [];
609 | var sorted = events.sort(function (a, b) {
610 | if (a.end > b.end) { return -1; }
611 | if (a.end < b.end) { return 1; }
612 | return 0;
613 | });
614 |
615 | // if not first day of week then get event palces. this is for dates that come from previous days
616 | // otherwise reset places
617 | sorted.forEach(function (item) {
618 | if (dayOfWeek === 0) { item.$$place = undefined; }
619 | else if (item.$$place !== undefined) { takenPlaces.push(item.$$place); }
620 | });
621 |
622 | // fill in places that have not been set
623 | sorted.forEach(function(item) {
624 | if (item.$$place === undefined) { item.$$place = getPlace(); }
625 | });
626 |
627 | // sort on places
628 | return sorted.sort(function(a, b) {
629 | if (a.$$place > b.$$place) { return 1; }
630 | if (a.$$place < b.$$place) { return -1; }
631 | return 0;
632 | });
633 |
634 |
635 | // find lowest place not taken
636 | function getPlace() {
637 | var place = 0;
638 | while (takenPlaces.indexOf(place) !== -1) {
639 | place++;
640 | }
641 | takenPlaces.push(place);
642 | return place;
643 | }
644 | }
645 |
646 |
647 | function createMonthElement() {
648 | var monthBody = document.createDocumentFragment();
649 | var headerRow = document.createElement('div');
650 | headerRow.classList.add('md-event-calendar-month-row-header');
651 | monthBody.appendChild(headerRow);
652 |
653 | // add header day labels
654 | $$mdEventCalendarUtil.days.forEach(function (name) {
655 | var dayHeader = document.createElement('div');
656 | dayHeader.classList.add('md-event-calendar-month-cell-header');
657 | dayHeader.textContent = name.slice(0,3).toLowerCase();
658 | headerRow.appendChild(dayHeader);
659 | });
660 |
661 | return monthBody;
662 | }
663 |
664 | function createRowElement() {
665 | var row = document.createElement('div');
666 | row.classList.add("md-event-calendar-month-row");
667 | return row;
668 | }
669 |
670 |
671 | function filterCurrentCalendar(date, events) {
672 | if (!events || !events.length) { return []; }
673 | // back fill 6 days for posibility of last month days showing up
674 | var start = $$mdEventCalendarUtil.getFirstDateOfMonth(date).getDate(-6);
675 | // front fill 6 days for posibility of next month days showing up
676 | var end = $$mdEventCalendarUtil.getFirstDateOfMonth(date).getDate(37);
677 |
678 | return events.filter(function (item) {
679 | if (!$$mdEventCalendarUtil.isValidDate(item.start)) { return false; }
680 | if ($$mdEventCalendarUtil.isDateWithinRange(item.start, start, end)) { return true; }
681 | if (!$$mdEventCalendarUtil.isValidDate(item.end)) { return false; }
682 | if ($$mdEventCalendarUtil.isDateWithinRange(item.end, start, end)) { return true; }
683 | return false;
684 | }).sort(function(a, b) {
685 | a = new Date(a.start);
686 | b = new Date(b.start);
687 | return a > b ? 1 : a < b ? -1 : 0;
688 | });
689 | }
690 |
691 |
692 | function cleanEvent(item) {
693 | item.$$hide = undefined;
694 | item.$$place = undefined;
695 | }
696 | }
697 | }());
698 | (function(){"use strict";
699 | eventCalendarMonthDirective.$inject = ["$$mdEventCalendarBuilder", "$window", "$$rAF", "$timeout"];angular
700 | .module('material.components.eventCalendar')
701 | .directive('mdEventCalendarMonth', eventCalendarMonthDirective);
702 |
703 |
704 | /**
705 | * @ngdoc directive
706 | * @name eventCalendarMonthDirective
707 | * @module material.components.eventCalendar
708 | *
709 | * @restrict E
710 | **/
711 | /*@ngInject*/
712 | function eventCalendarMonthDirective($$mdEventCalendarBuilder, $window, $$rAF, $timeout) {
713 | var directive = {
714 | restrict: 'E',
715 | require: '^mdEventCalendar',
716 | link: link
717 | };
718 | return directive;
719 |
720 |
721 | function link(scope, element, attrs, ctrl) {
722 | var showMoreData;
723 |
724 | var mdEventCalendarCtrl = ctrl;
725 | var rebuildThrottle = $$rAF.throttle(function () {
726 | scope.$evalAsync(function () {
727 | setAutoHeight();
728 | element.toggleClass('fitted', mdEventCalendarCtrl.fitted);
729 | buildView();
730 | });
731 | });
732 |
733 | scope.$watch(function () { return mdEventCalendarCtrl.date; }, buildView);
734 | scope.$watch(function () { return mdEventCalendarCtrl.events; }, function (newValue, oldValue) {
735 | if (newValue === oldValue) { return; }
736 | buildView();
737 | }, true);
738 | scope.$watch(function () { return mdEventCalendarCtrl.selectedEvents; }, function (newValue, oldValue) {
739 | if (newValue === oldValue) { return; }
740 | buildView();
741 | }, true);
742 |
743 | angular.element($window).on('resize', rebuildThrottle);
744 | scope.$on('$destroy', function () {
745 | angular.element($window).off('resize', rebuildThrottle);
746 | });
747 |
748 | $$rAF(function () {
749 | setAutoHeight();
750 | element.toggleClass('fitted', mdEventCalendarCtrl.fitted);
751 | });
752 |
753 | function setAutoHeight() {
754 | if (!mdEventCalendarCtrl.autoHeight) { return; }
755 | mdEventCalendarCtrl.fitted = true;
756 | var top = element[0].getBoundingClientRect().top;
757 | var height = $window.innerHeight - top - mdEventCalendarCtrl.offset;
758 | element.css('height', height+'px');
759 | }
760 |
761 | hideCreateLinkOnEventItemHover();
762 |
763 | // When user mouses over an existing event, add a class of md-event-hover to
764 | // the month element, so that the create link is hidden from view.
765 | function hideCreateLinkOnEventItemHover() {
766 | element.on('mouseenter', function () {
767 | element.on('mousemove', checkForEventItemRAF);
768 | });
769 |
770 | element.on('mouseleave', function () {
771 | element.off('mousemove', checkForEventItemRAF);
772 | element.removeClass('md-event-hover');
773 | });
774 |
775 | var lastHoverItem;
776 | var checkForEventItemRAF = $$rAF.throttle(checkForEventItem);
777 | function checkForEventItem(e) {
778 | if (mdEventCalendarCtrl.isCreateDisabled() === true) { return; }
779 | if (lastHoverItem === e.target) { return; }
780 | lastHoverItem = e.target;
781 |
782 | var targetIsEvent = !!e.target.getAttribute('md-event-id');
783 |
784 | element.toggleClass('md-event-hover', targetIsEvent);
785 | }
786 | }
787 |
788 | element.on('click', function (e) {
789 | if (mdEventCalendarCtrl.isCreateDisabled() === true) { return; }
790 |
791 | var eventId = e.target.getAttribute('md-event-id');
792 | var showMore = e.target.getAttribute('md-show-more');
793 | var showMoreClose = e.target.getAttribute('md-show-more-close');
794 | var createEvent = e.target.getAttribute('md-create-event') !== null;
795 |
796 | if (eventId) {
797 | var eventItem = getIdFromHash(eventId);
798 | scope.$apply(function () {
799 | mdEventCalendarCtrl.selectEvent(e, getIdFromHash(eventId));
800 | });
801 | return;
802 | }
803 |
804 | removeShowMore(true);
805 | if (showMore) {
806 | addShowMore(new Date(showMore));
807 | }
808 |
809 | if (createEvent) {
810 | var cellDate = getDateFromCreate(e.target);
811 | if (cellDate !== undefined) {
812 | scope.$apply(function () {
813 | mdEventCalendarCtrl.createEventClick(e, cellDate);
814 | });
815 | }
816 | }
817 | });
818 |
819 | function getDateFromCreate(el) {
820 | var dateString = el.getAttribute('md-date');
821 | while (dateString === null && el.nodeName !== 'MD-EVENT-CALENDAR-MONTH') {
822 | el = el.parentNode;
823 | dateString = el.getAttribute('md-date');
824 | }
825 |
826 | return dateString === null ? undefined : new Date(dateString);
827 | }
828 |
829 |
830 | function buildView() {
831 | var cellHeight;
832 | if (mdEventCalendarCtrl.fitted) {
833 | cellHeight = element[0].offsetHeight / 5;
834 | } else {
835 | cellHeight = mdEventCalendarCtrl.$element[0].offsetWidth / 7;
836 | }
837 |
838 | var monthElement = $$mdEventCalendarBuilder.month({
839 | date: mdEventCalendarCtrl.date,
840 | events: mdEventCalendarCtrl.events,
841 | selected: mdEventCalendarCtrl.selectedEvents,
842 | labelProperty: mdEventCalendarCtrl.labelProperty,
843 | showCreateLink: mdEventCalendarCtrl.showCreateLink,
844 | cellHeight: cellHeight
845 | });
846 | element.empty();
847 | element.append(monthElement);
848 |
849 | buildShowMore();
850 | }
851 |
852 |
853 | function addShowMore(date) {
854 | showMoreData = {
855 | date: date
856 | };
857 | buildShowMore(true);
858 | }
859 |
860 | function buildShowMore(animate) {
861 | if (showMoreData === undefined) { return; }
862 | if (showMoreData.element) {
863 | angular.element(showMoreData.element).remove();
864 | showMoreData.element = undefined;
865 | }
866 |
867 | var cell = getCellFromDate(showMoreData.date);
868 | var showMoreFragment = $$mdEventCalendarBuilder.showMore({
869 | date: showMoreData.date,
870 | selected: mdEventCalendarCtrl.selectedEvents,
871 | events: mdEventCalendarCtrl.events,
872 | labelProperty: mdEventCalendarCtrl.labelProperty,
873 | cell: cell
874 | });
875 |
876 | element.append(showMoreFragment);
877 | showMoreData.element = element[0].lastChild;
878 | positonShowMore();
879 |
880 | if (animate) {
881 | // add class after element added so animation
882 | $timeout(function () {
883 | angular.element(showMoreData.element).addClass('show');
884 | }, 0);
885 | } else {
886 | angular.element(showMoreData.element).addClass('no-transition');
887 | angular.element(showMoreData.element).addClass('show');
888 | }
889 | }
890 |
891 | function positonShowMore() {
892 | var showMoreBounds = showMoreData.element.getBoundingClientRect();
893 | var minTop = $window.innerHeight - showMoreBounds.height;
894 | var minLeft = $window.innerWidth - showMoreBounds.width;
895 | var leftDiff = showMoreBounds.left - minLeft;
896 |
897 | if (showMoreBounds.top > minTop) {
898 | showMoreData.element.style.top = minTop+'px';
899 | }
900 |
901 | if (leftDiff > 0) {
902 | showMoreData.element.style.left = minLeft+'px';
903 | // offset date
904 |
905 | leftDiff -= 10;
906 | if (leftDiff > 0) {
907 | showMoreData.element.querySelector('.md-event-calendar-show-more-date-label').style.marginLeft = leftDiff+'px';
908 | }
909 | }
910 | }
911 |
912 | function getCellFromDate(date) {
913 | return element[0].querySelector('[md-date="'+date+'"]');
914 | }
915 |
916 | function removeShowMore(animate) {
917 | if (!showMoreData) { return; }
918 |
919 | var el = showMoreData.element;
920 | showMoreData = undefined;
921 |
922 | if (animate) {
923 | angular.element(el).removeClass('no-transition');
924 | $timeout(function () {
925 | angular.element(el).removeClass('show');
926 | }, 0);
927 | $timeout(function () {
928 | el.remove();
929 | el = undefined;
930 | }, 400);
931 | } else {
932 | el.remove();
933 | el = undefined;
934 | }
935 | }
936 |
937 |
938 | function getClosestCell(el) {
939 | var target = angular.element(el).parent();
940 | while (target.hasClass('md-event-calendar-month-cell') === false) {
941 | target = target.parent();
942 | }
943 | return target;
944 | }
945 |
946 | function getIdFromHash(id) {
947 | return parseInt(id.replace('object_', ''));
948 | }
949 | }
950 | }
951 | }());
952 | (function(){"use strict";angular
953 | .module('material.components.eventCalendar')
954 | .directive('mdEventCalendarNext', mdEventCalendarNextDirective);
955 |
956 | /**
957 | * @ngdoc directive
958 | * @name mdEventCalendarNextDirective
959 | * @module material.components.eventCalendar
960 | *
961 | * @restrict E
962 | **/
963 | function mdEventCalendarNextDirective() {
964 | var directive = {
965 | restrict: 'E',
966 | require: '^mdEventCalendar',
967 | template: ''+
968 | '
'+
969 | ' '
970 | };
971 | return directive;
972 | }
973 | }());
974 | (function(){"use strict";angular
975 | .module('material.components.eventCalendar')
976 | .directive('mdEventCalendarPrev', mdEventCalendarPrevDirective);
977 |
978 | /**
979 | * @ngdoc directive
980 | * @name mdEventCalendarPrevDirective
981 | * @module material.components.eventCalendar
982 | *
983 | * @restrict E
984 | **/
985 | function mdEventCalendarPrevDirective() {
986 | var directive = {
987 | restrict: 'E',
988 | require: '^mdEventCalendar',
989 | template: ''+
990 | '
'+
991 | ' '
992 | };
993 | return directive;
994 | }
995 | }());
996 | (function(){"use strict";angular
997 | .module('material.components.eventCalendar')
998 | .directive('mdEventCalendarTitle', mdEventCalendarTitleDirective);
999 |
1000 | /**
1001 | * @ngdoc directive
1002 | * @name mdEventCalendarTitleDirective
1003 | * @module material.components.eventCalendar
1004 | *
1005 | * @restrict E
1006 | **/
1007 | function mdEventCalendarTitleDirective() {
1008 | var directive = {
1009 | restrict: 'E',
1010 | require: '^mdEventCalendar',
1011 | template: '',
1012 | link: link
1013 | };
1014 | return directive;
1015 |
1016 | function link(scope, element, attrs, ctrl) {
1017 | scope.mdEventCalendar = ctrl;
1018 | }
1019 | }
1020 | }());
1021 | (function(){"use strict";angular
1022 | .module('material.components.eventCalendar')
1023 | .directive('mdEventCalendarToday', mdEventCalendarTodayDirective);
1024 |
1025 | /**
1026 | * @ngdoc directive
1027 | * @name mdEventCalendarTodayDirective
1028 | * @module material.components.eventCalendar
1029 | *
1030 | * @restrict E
1031 | **/
1032 | function mdEventCalendarTodayDirective() {
1033 | var directive = {
1034 | restrict: 'E',
1035 | require: '^mdEventCalendar',
1036 | template: 'Today '
1037 | };
1038 | return directive;
1039 | }
1040 | }());
1041 | (function(){"use strict";
1042 | utilService.$inject = ["$injector", "$locale", "$filter"];angular
1043 | .module('material.components.eventCalendar')
1044 | .factory('$$mdEventCalendarUtil', utilService);
1045 |
1046 |
1047 | function utilService($injector, $locale, $filter) {
1048 | var $mdDateLocale = $injector.has('$mdDateLocale') ? $injector.get('$mdDateLocale') : undefined;
1049 | var dateFilter = $filter('date');
1050 | var months = $mdDateLocale ? $mdDateLocale.months : $locale.DATETIME_FORMATS.MONTH;
1051 | var shortMonths = $mdDateLocale ? $mdDateLocale.shortMonths : $locale.DATETIME_FORMATS.SHORTMONTH;
1052 | var days = $mdDateLocale ? $mdDateLocale.days : $locale.DATETIME_FORMATS.DAY;
1053 | var shortDays = $mdDateLocale ? $mdDateLocale.shortDays : $locale.DATETIME_FORMATS.SHORTDAY.map(function(day) {
1054 | return day.substring(0, 1);
1055 | });
1056 | // The default dates are simply the numbers 1 through 31.
1057 | var defaultDates = Array(32);
1058 | for (var i = 1; i <= 31; i++) {
1059 | defaultDates[i] = i;
1060 | }
1061 |
1062 |
1063 | var service = {
1064 | months: months,
1065 | shortMonths: shortMonths,
1066 | days: days,
1067 | dates: $mdDateLocale ? $mdDateLocale.dates : defaultDates,
1068 | shortDays: shortDays,
1069 | isValidDate: isValidDate,
1070 | createDateAtMidnight: createDateAtMidnight,
1071 | getDateInNextMonth: getDateInNextMonth,
1072 | getDateInPreviousMonth: getDateInPreviousMonth,
1073 | getFirstDateOfMonth: getFirstDateOfMonth,
1074 | getNumberOfDaysInMonth: getNumberOfDaysInMonth,
1075 | weekNumberFormatter: $mdDateLocale ? $mdDateLocale.weekNumberFormatter : weekNumberFormatter,
1076 | isSameMonthAndYear: isSameMonthAndYear,
1077 | isSameDay: isSameDay,
1078 | isDateWithinRange: isDateWithinRange,
1079 | formatEventTime: formatEventTime
1080 | };
1081 | return service;
1082 |
1083 |
1084 |
1085 |
1086 | function formatEventTime(date) {
1087 | return dateFilter(date, 'h:mm a');
1088 | }
1089 |
1090 |
1091 |
1092 | /**
1093 | * Checks if a date is within a min and max range, ignoring the time component.
1094 | * If minDate or maxDate are not dates, they are ignored.
1095 | * @param {Date} date
1096 | * @param {Date} minDate
1097 | * @param {Date} maxDate
1098 | */
1099 | function isDateWithinRange(date, minDate, maxDate) {
1100 | var dateAtMidnight = createDateAtMidnight(date);
1101 | var minDateAtMidnight = isValidDate(minDate) ? createDateAtMidnight(minDate) : null;
1102 | var maxDateAtMidnight = isValidDate(maxDate) ? createDateAtMidnight(maxDate) : null;
1103 | return (!minDateAtMidnight || minDateAtMidnight <= dateAtMidnight) &&
1104 | (!maxDateAtMidnight || maxDateAtMidnight >= dateAtMidnight);
1105 | }
1106 |
1107 |
1108 |
1109 | /**
1110 | * Default week number formatter.
1111 | * @param number
1112 | * @returns {string}
1113 | */
1114 | function weekNumberFormatter(number) {
1115 | return 'Week ' + number;
1116 | }
1117 |
1118 |
1119 | /**
1120 | * Sets a date's time to midnight.
1121 | * @param {Date} date
1122 | */
1123 | function setDateTimeToMidnight(date) {
1124 | if (isValidDate(date)) {
1125 | date.setHours(0, 0, 0, 0);
1126 | }
1127 | }
1128 |
1129 | /**
1130 | * Checks whether a date is valid.
1131 | * @param {Date} date
1132 | * @return {boolean} Whether the date is a valid Date.
1133 | */
1134 | function isValidDate(date) {
1135 | return date && date.getTime && !isNaN(date.getTime());
1136 | }
1137 |
1138 |
1139 | /**
1140 | * Creates a date with the time set to midnight.
1141 | * Drop-in replacement for two forms of the Date constructor:
1142 | * 1. No argument for Date representing now.
1143 | * 2. Single-argument value representing number of seconds since Unix Epoch
1144 | * or a Date object.
1145 | * @param {number|Date=} opt_value
1146 | * @return {Date} New date with time set to midnight.
1147 | */
1148 | function createDateAtMidnight(opt_value) {
1149 | var date;
1150 | if (opt_value === undefined) {
1151 | date = new Date();
1152 | } else {
1153 | date = new Date(opt_value);
1154 | }
1155 | setDateTimeToMidnight(date);
1156 | return date;
1157 | }
1158 |
1159 |
1160 |
1161 | /**
1162 | * Get an arbitrary date in the month after the given date's month.
1163 | * @param date
1164 | * @returns {Date}
1165 | */
1166 | function getDateInNextMonth(date) {
1167 | return new Date(date.getFullYear(), date.getMonth() + 1, 1);
1168 | }
1169 |
1170 |
1171 |
1172 | /**
1173 | * Get an arbitrary date in the month before the given date's month.
1174 | * @param date
1175 | * @returns {Date}
1176 | */
1177 | function getDateInPreviousMonth(date) {
1178 | return new Date(date.getFullYear(), date.getMonth() - 1, 1);
1179 | }
1180 |
1181 |
1182 |
1183 |
1184 | /**
1185 | * Gets the first day of the month for the given date's month.
1186 | * @param {Date} date
1187 | * @returns {Date}
1188 | */
1189 | function getFirstDateOfMonth(date) {
1190 | return new Date(date.getFullYear(), date.getMonth(), 1);
1191 | }
1192 |
1193 |
1194 | /**
1195 | * Gets the number of days in the month for the given date's month.
1196 | * @param date
1197 | * @returns {number}
1198 | */
1199 | function getNumberOfDaysInMonth(date) {
1200 | return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
1201 | }
1202 |
1203 |
1204 | /**
1205 | * Gets whether two dates have the same month and year.
1206 | * @param {Date} d1
1207 | * @param {Date} d2
1208 | * @returns {boolean}
1209 | */
1210 | function isSameMonthAndYear(d1, d2) {
1211 | return d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth();
1212 | }
1213 |
1214 |
1215 | /**
1216 | * Gets whether two dates are the same day (not not necesarily the same time).
1217 | * @param {Date} d1
1218 | * @param {Date} d2
1219 | * @returns {boolean}
1220 | */
1221 | function isSameDay(d1, d2) {
1222 | return d1.getDate() == d2.getDate() && isSameMonthAndYear(d1, d2);
1223 | }
1224 | }
1225 | }());
--------------------------------------------------------------------------------
/dist/angular-material-event-calendar.min.css:
--------------------------------------------------------------------------------
1 | md-event-calendar:not(._md) md-event-calendar-header{color:#666;background:#fff;border-color:#ddd}md-event-calendar:not(._md) md-event-calendar-header md-event-calendar-next .md-arrow svg,md-event-calendar:not(._md) md-event-calendar-header md-event-calendar-prev .md-arrow svg{fill:#666}md-event-calendar:not(._md) .md-button:not([disabled]){color:#333}md-event-calendar:not(._md) .md-button:not([disabled]):hover{background:hsla(0,0%,62%,.2)}md-event-calendar:not(._md) .md-button[disabled]{color:#ccc}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row-header{color:#999;background:#fff;border-color:#ddd}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row{background:#fff;border-color:#ddd}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell,md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell-divider{border-color:#ddd}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-create-link{color:#4189b8}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-data-label{color:#999}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-event-show-more-link{color:#4189b8}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.different-month{background:#f5f5f5}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.today{box-shadow:inset 0 0 0 1px #aaa}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.today .md-event-calendar-month-cell-content .md-event-calendar-cell-data-label{color:#666}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.today .md-event-calendar-month-cell-divider{border-color:#aaa}md-event-calendar:not(._md) md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell:last-child{border-color:#ddd}md-event-calendar:not(._md) .md-event-calendar-cell-event{background:#ddd;color:#666}md-event-calendar:not(._md) .md-event-calendar-cell-event.md-selected{color:#eee;background:#888}md-event-calendar:not(._md) .md-event-calendar-cell-event.md-continue-left:after,md-event-calendar:not(._md) .md-event-calendar-cell-event.md-end-left:after{border-right-color:#ddd}md-event-calendar:not(._md) .md-event-calendar-cell-event.md-continue-right:after,md-event-calendar:not(._md) .md-event-calendar-cell-event.md-start-right:after{border-left-color:#ddd}md-event-calendar:not(._md) .md-event-calendar-cell-event.md-selected.md-continue-left:after,md-event-calendar:not(._md) .md-event-calendar-cell-event.md-selected.md-end-left:after{border-right-color:#888}md-event-calendar:not(._md) .md-event-calendar-cell-event.md-selected.md-continue-right:after,md-event-calendar:not(._md) .md-event-calendar-cell-event.md-selected.md-start-right:after{border-left-color:#888}md-event-calendar:not(._md) .md-event-calendar-show-more-container .md-event-calendar-show-more-date-label{color:#999}md-event-calendar:not(._md) .md-event-calendar-show-more-container .md-event-calendar-show-more-close svg{fill:#999}md-event-calendar{display:block}md-event-calendar md-event-calendar-header{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;display:-webkit-box;display:-ms-flexbox;display:flex;line-height:64px;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border-style:solid;border-width:1px 1px 0}md-event-calendar md-event-calendar-header.md-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}md-event-calendar md-event-calendar-header md-event-calendar-title{display:block;min-width:170px;text-align:center;font-size:20px}md-event-calendar md-event-calendar-header md-event-calendar-next,md-event-calendar md-event-calendar-header md-event-calendar-prev{display:block}md-event-calendar md-event-calendar-header md-event-calendar-next .md-arrow,md-event-calendar md-event-calendar-header md-event-calendar-prev .md-arrow{cursor:pointer;height:24px;width:24px}md-event-calendar md-event-calendar-header md-event-calendar-next .md-arrow.md-left-arrow,md-event-calendar md-event-calendar-header md-event-calendar-prev .md-arrow.md-left-arrow{-webkit-transform:rotate(180deg);transform:rotate(180deg)}md-event-calendar .md-event-calendar-month-cell-content .md-event-calendar-create-link{opacity:0}md-event-calendar .md-event-calendar-month-cell-content:hover .md-event-calendar-create-link{opacity:1}md-event-calendar md-event-calendar-month.md-event-hover .md-event-calendar-month-cell-content .md-event-calendar-create-link{opacity:0}md-event-calendar md-event-calendar-month.fitted{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}md-event-calendar md-event-calendar-month .md-event-calendar-month-row-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;min-height:36px;height:36px;-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end;font-size:12px;font-weight:500;padding-bottom:12px;border-style:solid;border-width:0 1px 1px}md-event-calendar md-event-calendar-month .md-event-calendar-month-row-header .md-event-calendar-month-cell-header{-webkit-box-flex:1;-ms-flex:1;flex:1;padding-left:6px}md-event-calendar md-event-calendar-month .md-event-calendar-month-row{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;border-style:solid;border-width:0 0 1px}md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell{position:relative;-webkit-box-flex:1;-ms-flex:1;flex:1}md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-spacer{margin-top:100%}md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-divider{position:absolute;top:0;bottom:0;left:0;border-style:solid;border-width:0 1px 0 0}md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content{position:absolute;top:0;bottom:0;left:0;right:0}md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-create-link{-ms-flex-item-align:center;align-self:center;text-transform:uppercase;font-size:14px;font-weight:500;padding-right:12px;cursor:pointer;-webkit-transition:opacity .4s cubic-bezier(.25,.8,.25,1);transition:opacity .4s cubic-bezier(.25,.8,.25,1)}md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-data-label{font-size:13px;padding:8px;-webkit-box-flex:1;-ms-flex:1;flex:1}md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-event-spacer{margin:4px 0;height:23px}md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-event-show-more-link{font-size:13px;padding:4px;padding-left:8px;cursor:pointer}md-event-calendar md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell:last-child{border-style:solid;border-width:0 1px 0 0}md-event-calendar .md-event-calendar-cell-event{font-size:12px;min-height:15px;padding:4px;cursor:pointer}md-event-calendar .md-event-calendar-cell-event.md-single{margin:4px;border-radius:2px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}md-event-calendar .md-event-calendar-cell-event.md-start{margin:4px 0 4px 4px;border-radius:2px 0 0 2px;white-space:nowrap;z-index:1;position:relative}md-event-calendar .md-event-calendar-cell-event.md-start-right{margin:4px 13px 4px 4px;border-radius:2px 0 0 2px}md-event-calendar .md-event-calendar-cell-event.md-end{margin:4px 4px 4px 0;border-radius:0 2px 2px 0}md-event-calendar .md-event-calendar-cell-event.md-end-left{margin:4px 4px 4px 13px;border-radius:0 2px 2px 0}md-event-calendar .md-event-calendar-cell-event.md-continue,md-event-calendar .md-event-calendar-cell-event.md-continue-both{margin:4px 0;border-radius:0}md-event-calendar .md-event-calendar-cell-event.md-continue-right{margin:4px 13px 4px 0;border-radius:0;white-space:nowrap}md-event-calendar .md-event-calendar-cell-event.md-continue-left{margin:4px 0 4px 13px;border-radius:0;white-space:nowrap}md-event-calendar .md-event-calendar-cell-event.md-continue-right:after,md-event-calendar .md-event-calendar-cell-event.md-start-right:after{content:'';position:absolute;height:0;width:0;right:0;margin-top:-4px;border-top:12px solid transparent;border-bottom:11.5px solid transparent;border-left:13px solid #eee}md-event-calendar .md-event-calendar-cell-event.md-continue-left:after,md-event-calendar .md-event-calendar-cell-event.md-end-left:after{content:'';position:absolute;height:0;width:0;left:0;margin-top:-4px;border-top:12px solid transparent;border-bottom:11.5px solid transparent;border-right:13px solid #eee}md-event-calendar .md-event-calendar-cell-event .md-event-calendar-cell-event-time{font-weight:500;padding-right:6px;pointer-events:none}md-event-calendar .md-event-calendar-cell-event span{pointer-events:none}md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-end,md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-single,md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-start-right{padding-left:16px;margin-left:0}md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-end,md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-end-left,md-event-calendar .md-event-calendar-show-more-content .md-event-calendar-cell-event.md-single{margin-right:0}md-event-calendar .md-event-calendar-show-more-container{opacity:0;position:absolute;top:0;left:1px;width:180px;background:#fff;padding:12px;padding-top:7px;border-radius:2px;z-index:2;box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 13px 19px 2px rgba(0,0,0,.14),0 5px 24px 4px rgba(0,0,0,.12)}md-event-calendar .md-event-calendar-show-more-container .md-event-calendar-show-more-content{position:relative}md-event-calendar .md-event-calendar-show-more-container.show{opacity:1}md-event-calendar .md-event-calendar-show-more-container.show:not(.no-transition){-webkit-transition:opacity .4s cubic-bezier(.25,.8,.25,1);transition:opacity .4s cubic-bezier(.25,.8,.25,1)}md-event-calendar .md-event-calendar-show-more-container .md-event-calendar-show-more-close{display:-webkit-box;display:-ms-flexbox;display:flex;position:absolute;top:6px;right:7px;width:16px;height:16px;cursor:pointer}md-event-calendar .md-event-calendar-show-more-container .md-event-calendar-show-more-close svg{pointer-events:none}md-event-calendar .md-event-calendar-show-more-container .md-event-calendar-show-more-date-label{font-size:13px;padding:6px;margin-left:-11px;margin-top:-5px}md-event-calendar.md-create-disabled md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-create-link{display:none}
--------------------------------------------------------------------------------
/dist/angular-material-event-calendar.min.js:
--------------------------------------------------------------------------------
1 | !function(){"use strict";function e(e,n,a){var r;e.has("$mdThemingProvider")?(r=e.get("$mdThemingProvider"),r.registerStyles(a)):n.decorator("$$rAF",["$delegate",t])}function t(e){return e.throttle=function(t){var n,a,r,d;return function(){n=arguments,d=this,r=t,a||(a=!0,e(function(){r.apply(d,Array.prototype.slice.call(n)),a=!1}))}},e}e.$inject=["$injector","$provide","EVENT_CALENDAR_THEME"],angular.module("material.components.eventCalendar",[]).config(e)}(),function(){"use strict";angular.module("material.components.eventCalendar").run(["$templateCache",function(e){e.put("icons/ic_close_black_24px.svg",'\n \n \n '),e.put("icons/ic_keyboard_arrow_right_black_24px.svg",'\n \n \n \n')}])}(),function(){"use strict";angular.module("material.components.eventCalendar").constant("EVENT_CALENDAR_THEME","md-event-calendar._md md-event-calendar-header {\n color: '{{foreground-1}}';\n background: '{{background-hue-1}}';\n border-color: '{{foreground-4}}'; }\n md-event-calendar._md md-event-calendar-header md-event-calendar-next .md-arrow svg, md-event-calendar._md md-event-calendar-header md-event-calendar-prev .md-arrow svg {\n fill: '{{foreground-2}}'; }\n\nmd-event-calendar._md md-event-calendar-month .md-event-calendar-month-row-header {\n color: '{{foreground-3}}';\n background: '{{background-hue-1}}';\n border-color: '{{foreground-4}}'; }\n\nmd-event-calendar._md md-event-calendar-month .md-event-calendar-month-row {\n background: '{{background-hue-1}}';\n border-color: '{{foreground-4}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell-divider {\n border-color: '{{foreground-4}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell {\n border-color: '{{foreground-4}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-create-link {\n color: '{{primary-default}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-data-label {\n color: '{{foreground-3}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell .md-event-calendar-month-cell-content .md-event-calendar-cell-event-show-more-link {\n color: '{{primary-default}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.different-month {\n background: '{{background-hue-2}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.today {\n box-shadow: inset 0px 0px 0px 1px '{{primary-default}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell.today .md-event-calendar-month-cell-content .md-event-calendar-cell-data-label {\n color: '{{primary-default}}'; }\n md-event-calendar._md md-event-calendar-month .md-event-calendar-month-row .md-event-calendar-month-cell:last-child {\n border-color: '{{foreground-4}}'; }\n\nmd-event-calendar._md .md-event-calendar-cell-event {\n background: '{{foreground-4}}';\n color: '{{background-900}}'; }\n md-event-calendar._md .md-event-calendar-cell-event.md-selected {\n color: #EEE;\n background: '{{primary-default}}'; }\n md-event-calendar._md .md-event-calendar-cell-event.md-continue-left:after, md-event-calendar._md .md-event-calendar-cell-event.md-end-left:after {\n border-right-color: '{{foreground-4}}'; }\n md-event-calendar._md .md-event-calendar-cell-event.md-continue-right:after, md-event-calendar._md .md-event-calendar-cell-event.md-start-right:after {\n border-left-color: '{{foreground-4}}'; }\n md-event-calendar._md .md-event-calendar-cell-event.md-selected.md-continue-left:after, md-event-calendar._md .md-event-calendar-cell-event.md-selected.md-end-left:after {\n border-right-color: '{{primary-default}}'; }\n md-event-calendar._md .md-event-calendar-cell-event.md-selected.md-continue-right:after, md-event-calendar._md .md-event-calendar-cell-event.md-selected.md-start-right:after {\n border-left-color: '{{primary-default}}'; }\n\nmd-event-calendar._md .md-event-calendar-show-more-container .md-event-calendar-show-more-date-label {\n color: '{{foreground-3}}'; }\n\nmd-event-calendar._md .md-event-calendar-show-more-container .md-event-calendar-show-more-close svg {\n fill: '{{foreground-1}}'; }\n\nmd-event-calendar._md.md-primary md-event-calendar-header {\n color: '{{background-100}}';\n background: '{{primary-default}}'; }\n md-event-calendar._md.md-primary md-event-calendar-header md-event-calendar-next .md-arrow svg, md-event-calendar._md.md-primary md-event-calendar-header md-event-calendar-prev .md-arrow svg {\n fill: '{{background-100}}'; }\n\nmd-event-calendar._md.md-primary md-event-calendar-month .md-event-calendar-month-row-header {\n color: '{{background-100}}';\n background: '{{primary-default}}'; }\n")}(),function(){"use strict";function e(e,t){function n(e,n){var a=n.mdEventClick?t(n.mdEventClick,null,!0):void 0,d=n.mdCreateEventClick?t(n.mdCreateEventClick,null,!0):void 0,l=n.mdCreateDisabled?t(n.mdCreateDisabled):void 0;return e.append(" "),function(e,t,o,c){function i(){var e=f.$viewValue||f.$modelValue||[];h.selectedEvents=[].concat(e)}function m(t,n){o.mdEventClick&&a(e.$parent,{$event:t,$selectedEvent:n})}function s(t,n){o.mdCreateEventClick&&d(e.$parent,{$event:t,$date:n})}function v(){return u}var u=!1,h=c[0],f=c[1];r&&(t.addClass("_md"),r(t)),h.isCreateDisabled=v,h.callEventClick=m,h.createEventClick=s,f&&(f.$render=i,h.ngModelCtrl=f),l?e.$watch(function(){return l(e.$parent)},function(e){u=e,t.toggleClass("md-create-disabled",e)}):void 0!==n.mdCreateDisabled&&(u=!0,t.addClass("md-create-disabled"))}}function a(e,t,n){function a(){o.date=e.getDateInNextMonth(o.date),o.monthDisplay=e.months[o.date.getMonth()],o.yearDisplay=o.date.getFullYear(),o.isTodayDisabled=o.date.getMonth()===(new Date).getMonth()}function r(){o.date=e.getDateInPreviousMonth(o.date),o.monthDisplay=e.months[o.date.getMonth()],o.yearDisplay=o.date.getFullYear(),o.isTodayDisabled=o.date.getMonth()===(new Date).getMonth()}function d(){o.date=new Date,o.monthDisplay=e.months[o.date.getMonth()],o.yearDisplay=o.date.getFullYear(),o.isTodayDisabled=!0}function l(e,t){var n=o.events.filter(function(e){return e.$$mdEventId===t});return o.ngModelCtrl&&(o.ngModelCtrl.$setViewValue(n[0]),o.ngModelCtrl.$render()),o.callEventClick(e,n[0]),!0}var o=this;o.$element=t,o.labelProperty=n.mdLabel||"title",o.selectedEvents=[],o.today=e.createDateAtMidnight(),o.date=e.createDateAtMidnight(),o.isToday=e.isSameDay(o.date,new Date),o.monthDisplay=e.months[o.date.getMonth()],o.yearDisplay=o.date.getFullYear(),o.isTodayDisabled=!0,o.showCreateLink=void 0!==n.mdShowCreateLink&&"false"!==n.mdShowCreateLink,o.nextMonth=a,o.previousMonth=r,o.selectEvent=l,o.setToday=d,o.autoHeight=void 0!==n.autoHeight,o.fitted=void 0!==n.fitted,o.offset=o.autoHeight===!1||""===n.autoHeight||isNaN(n.autoHeight.replace("px",""))?0:parseInt(n.autoHeight.replace("px",""))}a.$inject=["$$mdEventCalendarUtil","$element","$attrs"];var r=e.has("$mdTheming")?e.get("$mdTheming"):void 0,d={restrict:"E",require:["mdEventCalendar","?ngModel"],scope:{events:"=mdEvents"},compile:n,controller:a,controllerAs:"mdEventCalendar",bindToController:!0};return d}e.$inject=["$injector","$parse"],angular.module("material.components.eventCalendar").directive("mdEventCalendar",e)}(),function(){"use strict";function e(e,n){function a(t){var a=t.date,d=t.selected||[],l=t.events?r(a,t.events):[],o=t.labelProperty,c=document.createDocumentFragment(),i=document.createElement("div");i.classList.add("md-event-calendar-show-more-container");var s=document.createElement("div");s.classList.add("md-event-calendar-show-more-content");var v=document.createElement("div");v.classList.add("md-event-calendar-show-more-date-label"),v.textContent=e.dates[a.getDate()];var u=document.createElement("div");u.classList.add("md-event-calendar-show-more-close"),u.innerHTML=n.get("icons/ic_close_black_24px.svg"),u.setAttribute("md-show-more-close","true"),i.appendChild(v),i.appendChild(u),i.appendChild(s),c.appendChild(i),l.forEach(function(t){var n,r=e.isSameDay(a,t.start),l=!e.isValidDate(t.end)||e.isSameDay(a,t.end),c={labelProperty:o,selected:d};n=r&&l?m({className:"single",hasLabel:!0},t,c):r?m({className:"start-right",hasLabel:!0},t,c):l?m({className:"end-left",hasLabel:!0},t,c):m({className:"continue",hasLabel:!0},t,c),s.appendChild(n)});var h=(t.cell.getBoundingClientRect(),t.cell.offsetTop),f=t.cell.offsetLeft;return i.style.top=h+"px",i.style.left=f+"px",c}function r(t,n){return n&&n.length?n.filter(function(n){return e.isDateWithinRange(t,n.start,n.end||n.start)}).sort(function(e,t){return e=new Date(e.start),t=new Date(t.start),e>t?1:e0)for(a=e.getFirstDateOfMonth(m),a.setDate(a.getDate()-v);d=t.maxEvents)return e.appendChild(c(d.length-o,t.date)),!1;e.appendChild(i()),n+=1}return a>=t.maxEvents?(e.appendChild(c(d.length-o,t.date)),!1):(e.appendChild(m(s,l,t)),!0)}),r===!0&&e.classList.add("md-has-events")}function c(e,t){var n=document.createElement("div");return n.classList.add("md-event-calendar-cell-event-show-more-link"),n.textContent=e+" more",n.setAttribute("md-show-more",t.toISOString()),n}function i(){var e=document.createElement("div");return e.classList.add("md-event-calendar-cell-event-spacer"),e}function m(t,n,a){var r=s(n),d=document.createElement("div");if(d.setAttribute("md-event-id",r),d.classList.add("md-event-calendar-cell-event"),d.classList.add("md-"+t.className),n.customClass&&d.classList.add(n.customClass),t.hasLabel===!0){if(t.allDay!==!0){var l=document.createElement("span");l.classList.add("md-event-calendar-cell-event-time"),l.textContent=e.formatEventTime(n.start),d.appendChild(l)}var o=document.createElement("span");o.textContent=n[a.labelProperty],d.appendChild(o)}return a.selected.every(function(e){return void 0===e.$$mdEventId||e.$$mdEventId!==n.$$mdEventId||(d.classList.add("md-selected"),!1)}),d}function s(e){return angular.isObject(e)?"object_"+(e.$$mdEventId||(e.$$mdEventId=++t)):"id_"+ ++t}function v(t,n){var a,r,d=e.isSameDay(n.date,t.start),l=!e.isValidDate(t.end)||e.isSameDay(n.date,t.end);return d&&(n.allDay||l)?(a="single",r=!0):d&&6===n.dayOfWeek?(a="start-right",r=!0):d?(a="start",r=!0):l&&0===n.dayOfWeek?(a="end-left",r=!0):l?(a="end",r=n.isFirstDay):0===n.dayOfWeek?(a="continue-left",r=!0):6===n.dayOfWeek?(a="continue-right",r=!1):(a="continue",r=!1),{className:a,hasLabel:r,allDay:t.allDay||!1}}function u(t,n){return n.filter(function(n){return e.isDateWithinRange(t,n.start,n.end||n.start)})}function h(e,t){function n(){for(var e=0;a.indexOf(e)!==-1;)e++;return a.push(e),e}var a=[],r=e.sort(function(e,t){return e.end>t.end?-1:e.endt.$$place?1:e.$$placet?1:en&&(C.element.style.top=n+"px"),r>0&&(C.element.style.left=a+"px",r-=10,r>0&&(C.element.querySelector(".md-event-calendar-show-more-date-label").style.marginLeft=r+"px"))}function f(e){return d[0].querySelector('[md-date="'+e+'"]')}function g(e){if(C){var t=C.element;C=void 0,e?(angular.element(t).removeClass("no-transition"),a(function(){angular.element(t).removeClass("show")},0),a(function(){t.remove(),t=void 0},400)):(t.remove(),t=void 0)}}function p(e){return parseInt(e.replace("object_",""))}var C,E=o,b=n.throttle(function(){r.$evalAsync(function(){c(),d.toggleClass("fitted",E.fitted),s()})});r.$watch(function(){return E.date},s),r.$watch(function(){return E.events},function(e,t){e!==t&&s()},!0),r.$watch(function(){return E.selectedEvents},function(e,t){e!==t&&s()},!0),angular.element(t).on("resize",b),r.$on("$destroy",function(){angular.element(t).off("resize",b)}),n(function(){c(),d.toggleClass("fitted",E.fitted)}),i(),d.on("click",function(e){if(E.isCreateDisabled()!==!0){var t=e.target.getAttribute("md-event-id"),n=e.target.getAttribute("md-show-more"),a=(e.target.getAttribute("md-show-more-close"),null!==e.target.getAttribute("md-create-event"));if(t){p(t);return void r.$apply(function(){E.selectEvent(e,p(t))})}if(g(!0),n&&v(new Date(n)),a){var d=m(e.target);void 0!==d&&r.$apply(function(){E.createEventClick(e,d)})}}})}var d={restrict:"E",require:"^mdEventCalendar",link:r};return d}e.$inject=["$$mdEventCalendarBuilder","$window","$$rAF","$timeout"],angular.module("material.components.eventCalendar").directive("mdEventCalendarMonth",e)}(),function(){"use strict";function e(){var e={restrict:"E",require:"^mdEventCalendar",template:'
'};return e}angular.module("material.components.eventCalendar").directive("mdEventCalendarNext",e)}(),function(){"use strict";function e(){var e={restrict:"E",require:"^mdEventCalendar",template:'
'};return e}angular.module("material.components.eventCalendar").directive("mdEventCalendarPrev",e)}(),function(){"use strict";function e(){function e(e,t,n,a){e.mdEventCalendar=a}var t={restrict:"E",require:"^mdEventCalendar",template:'',link:e};return t}angular.module("material.components.eventCalendar").directive("mdEventCalendarTitle",e)}(),function(){"use strict";function e(){var e={restrict:"E",require:"^mdEventCalendar",template:'Today '};return e}angular.module("material.components.eventCalendar").directive("mdEventCalendarToday",e)}(),function(){"use strict";function e(e,t,n){function a(e){return g(e,"h:mm a")}function r(e,t,n){var a=c(e),r=o(t)?c(t):null,d=o(n)?c(n):null;return(!r||r<=a)&&(!d||d>=a)}function d(e){return"Week "+e}function l(e){o(e)&&e.setHours(0,0,0,0)}function o(e){return e&&e.getTime&&!isNaN(e.getTime())}function c(e){var t;return t=void 0===e?new Date:new Date(e),l(t),t}function i(e){return new Date(e.getFullYear(),e.getMonth()+1,1)}function m(e){return new Date(e.getFullYear(),e.getMonth()-1,1)}function s(e){return new Date(e.getFullYear(),e.getMonth(),1)}function v(e){return new Date(e.getFullYear(),e.getMonth()+1,0).getDate()}function u(e,t){return e.getFullYear()===t.getFullYear()&&e.getMonth()===t.getMonth()}function h(e,t){return e.getDate()==t.getDate()&&u(e,t)}for(var f=e.has("$mdDateLocale")?e.get("$mdDateLocale"):void 0,g=n("date"),p=f?f.months:t.DATETIME_FORMATS.MONTH,C=f?f.shortMonths:t.DATETIME_FORMATS.SHORTMONTH,E=f?f.days:t.DATETIME_FORMATS.DAY,b=f?f.shortDays:t.DATETIME_FORMATS.SHORTDAY.map(function(e){return e.substring(0,1)}),y=Array(32),D=1;D<=31;D++)y[D]=D;var $={months:p,shortMonths:C,days:E,dates:f?f.dates:y,shortDays:b,isValidDate:o,createDateAtMidnight:c,getDateInNextMonth:i,getDateInPreviousMonth:m,getFirstDateOfMonth:s,getNumberOfDaysInMonth:v,weekNumberFormatter:f?f.weekNumberFormatter:d,isSameMonthAndYear:u,isSameDay:h,isDateWithinRange:r,formatEventTime:a};return $}e.$inject=["$injector","$locale","$filter"],angular.module("material.components.eventCalendar").factory("$$mdEventCalendarUtil",e)}();
--------------------------------------------------------------------------------
/gulp/config.js:
--------------------------------------------------------------------------------
1 | exports.paths = {
2 | src: 'src/',
3 | app: 'app/',
4 | dest: 'public/',
5 | build: 'dist/',
6 | scripts: ['src/mdCalendar.js', 'src/*.js', 'src/**/*.js', '!src/*spec.js', '!src/**/*spec.js'],
7 | appScripts: ['app/app.js', 'app/*.js', 'app/**/*.js'],
8 | css: ['src/*.scss', 'src/*.css', '!src/*spec.css', '!src/*-theme.scss'],
9 | appCss: ['app/style.css', 'app/**/*.css'],
10 | injectCss: ['public/*.css', 'public/**/*.css'],
11 | partials: ['app/**/*.html'],
12 | icons: ['src/**/*.svg'],
13 | bower: './bower.json'
14 | };
15 |
--------------------------------------------------------------------------------
/gulp/cssBuild.js:
--------------------------------------------------------------------------------
1 | var paths = require('./config').paths;
2 |
3 | var gulp = require('gulp');
4 | var gutil = require('gulp-util');
5 | var autoprefixer = require('gulp-autoprefixer');
6 | var gulpFilter = require('gulp-filter');
7 | var concat = require('gulp-concat');
8 | var cssnano = require('gulp-cssnano');
9 | var sass = require('gulp-sass');
10 | var rename = require('gulp-rename');
11 |
12 | exports.getDev = function (srcs) {
13 | srcs = srcs || paths.css.concat(paths.appCss);
14 |
15 | return function dev() {
16 | return gulp.src(srcs)
17 | .pipe(sass())
18 | .pipe(autoprefixer())
19 | .pipe(gulp.dest(paths.dest))
20 | .on('end', function(){
21 | gutil.log(gutil.colors.green('✔ CSS dev'), 'Finished');
22 | });
23 | };
24 | };
25 |
26 |
27 | exports.release = function () {
28 | return gulp.src(paths.css)
29 | .pipe(sass())
30 | .pipe(concat('angular-material-event-calendar.css'))
31 | .pipe(autoprefixer())
32 | .pipe(gulp.dest(paths.build))
33 | .pipe(cssnano())
34 | .pipe(rename('angular-material-event-calendar.min.css'))
35 | .pipe(gulp.dest(paths.build))
36 | .on('end', function(){
37 | gutil.log(gutil.colors.green('✔ CSS Build'), 'Finished');
38 | });
39 | };
40 |
--------------------------------------------------------------------------------
/gulp/indexBuild.js:
--------------------------------------------------------------------------------
1 | var paths = require('./config').paths;
2 |
3 | var gulp = require('gulp');
4 | var inject = require('gulp-inject');
5 | var mainBowerFiles = require('gulp-main-bower-files');
6 |
7 |
8 | exports.inject = function () {
9 | var scripts = gulp.src(paths.scripts, {read: false});
10 | var appScripts = gulp.src(paths.appScripts, {read: false});
11 | var appCss = gulp.src(paths.appCss, {read: false});
12 | var bower = gulp.src(paths.bower).pipe(mainBowerFiles({includeDev: true}));
13 | var css = gulp.src(paths.injectCss, {read: false});
14 | var themeConstant = gulp.src(['public/js/_theme.js', 'public/templates.js'], {read: false});
15 |
16 | return gulp.src(paths.app + 'index.html')
17 | .pipe(inject(css, {
18 | name: 'css',
19 | relative: true,
20 | ignorePath: '../public'
21 | }))
22 | .pipe(inject(scripts, {
23 | name: 'scripts',
24 | relative: true,
25 | ignorePath: '../src'
26 | }))
27 | .pipe(inject(themeConstant, {
28 | name: 'theme',
29 | relative: true,
30 | ignorePath: '../public'
31 | }))
32 | .pipe(inject(appScripts, {
33 | name: 'appscripts',
34 | relative: true,
35 | ignorePath: '../'
36 | }))
37 | .pipe(inject(bower, {
38 | name: 'bower',
39 | relative: true,
40 | ignorePath: '../bower_components/'
41 | }))
42 | .pipe(gulp.dest(paths.dest));
43 | };
44 |
--------------------------------------------------------------------------------
/gulp/jsBuild.js:
--------------------------------------------------------------------------------
1 | var paths = require('./config').paths;
2 |
3 | var gulp = require('gulp');
4 | var jshint = require('gulp-jshint');
5 | var wrap = require("gulp-wrap");
6 | var concat = require('gulp-concat');
7 | var uglify = require('gulp-uglify');
8 | var stripDebug = require('gulp-strip-debug');
9 | var rename = require("gulp-rename");
10 | var filter = require('gulp-filter');
11 | var gutil = require('gulp-util');
12 | var ngAnnotate = require('gulp-ng-annotate');
13 |
14 |
15 |
16 | exports.getDevSrc = function (srcs) {
17 | srcs = srcs || paths.scripts;
18 |
19 | return function dev() {
20 | return gulp.src(srcs, {base: paths.src})
21 | .pipe(wrap('(function(){"use strict";<%= contents %>}());'))
22 | .pipe(jshint())
23 | .pipe(jshint.reporter('default'))
24 | .pipe(gulp.dest(paths.dest))
25 | .on('end', function() {
26 | gutil.log(gutil.colors.green('✔ JS Dev'), 'Finished');
27 | });
28 | };
29 | }
30 |
31 |
32 | exports.getDevApp = function (srcs) {
33 | srcs = srcs || paths.appScripts;
34 |
35 | return function dev() {
36 | return gulp.src(srcs, {base: paths.app})
37 | .pipe(wrap('(function(){"use strict";<%= contents %>}());'))
38 | .pipe(jshint())
39 | .pipe(jshint.reporter('default'))
40 | .pipe(gulp.dest(paths.dest))
41 | .on('end', function() {
42 | gutil.log(gutil.colors.green('✔ JS Dev'), 'Finished');
43 | });
44 | };
45 | }
46 |
47 |
48 | exports.release = function () {
49 | return gulp.src(paths.scripts)
50 | .pipe(wrap('(function(){"use strict";<%= contents %>}());'))
51 | .pipe(jshint())
52 | .pipe(jshint.reporter('default'))
53 | .pipe(concat('angular-material-event-calendar.js'))
54 | .pipe(stripDebug())
55 | .pipe(ngAnnotate())
56 | .pipe(gulp.dest(paths.build))
57 | .pipe(uglify())
58 | .pipe(rename('angular-material-event-calendar.min.js'))
59 | .pipe(gulp.dest(paths.build))
60 | .on('end', function() {
61 | gutil.log(gutil.colors.green('✔ JS build'), 'Finished');
62 | });
63 | }
64 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var paths = require('./gulp/config').paths;
2 |
3 | var gulp = require('gulp');
4 | var serve = require('gulp-serve');
5 | var gulpSequence = require('gulp-sequence');
6 | var del = require('del');
7 | var bump = require('gulp-bump');
8 | var ngConstant = require('gulp-ng-constant');
9 | var sass = require('gulp-sass');
10 | var through2 = require('through2');
11 | var rename = require('gulp-rename');
12 | var wrap = require('gulp-wrap');
13 | var templateCache = require('gulp-angular-templatecache');
14 | var KarmaServer = require('karma').Server;
15 |
16 |
17 | var jsBuild = require('./gulp/jsBuild');
18 | var cssBuild = require('./gulp/cssBuild');
19 | var indexBuild = require('./gulp/indexBuild');
20 |
21 |
22 |
23 | gulp.task('jsSrcBuild', jsBuild.getDevSrc());
24 | gulp.task('jsAppBuild', jsBuild.getDevApp());
25 | gulp.task('jsReleaseBuild', jsBuild.release);
26 | gulp.task('cssBuild', cssBuild.getDev());
27 | gulp.task('cssReleaseBuild', cssBuild.release);
28 | gulp.task('indexBuild', indexBuild.inject);
29 |
30 |
31 |
32 | // -- main tasks. use these to watch and build and release
33 |
34 | gulp.task('default', gulpSequence('buildLocal', ['serve', 'watch']));
35 | gulp.task('buildLocal', gulpSequence(
36 | 'clean',
37 | [
38 | 'jsSrcBuild',
39 | 'jsAppBuild',
40 | 'cssBuild',
41 | 'copyPartials',
42 | 'copyIcons',
43 | 'themeToConstant',
44 | 'buildIconCacheDev'
45 | ],
46 | 'indexBuild'
47 | ));
48 |
49 | gulp.task('build', gulpSequence('buildIconCache', 'themeToConstantBuild', ['jsReleaseBuild', 'cssReleaseBuild'], 'cleanIconCache', 'cleanThemeConstant'));
50 |
51 |
52 |
53 | gulp.task('themeToConstant', function () {
54 | return gulp.src(paths.src+'eventCalendar-theme.scss')
55 | .pipe(sass())
56 | .pipe(through2.obj(function (file, enc, cb) {
57 | var config = {
58 | name: 'material.components.eventCalendar',
59 | deps: false,
60 | constants: {
61 | EVENT_CALENDAR_THEME: file.contents.toString()
62 | }
63 | };
64 | file.contents = new Buffer(JSON.stringify(config), 'utf-8');
65 | this.push(file);
66 | cb();
67 | }))
68 | .pipe(ngConstant())
69 | .pipe(wrap('(function(){"use strict";<%= contents %>}());'))
70 | .pipe(rename('_theme.js'))
71 | .pipe(gulp.dest(paths.dest+'/js'))
72 | });
73 | gulp.task('themeToConstantBuild', function () {
74 | return gulp.src(paths.src+'eventCalendar-theme.scss')
75 | .pipe(sass())
76 | .pipe(through2.obj(function (file, enc, cb) {
77 | var config = {
78 | name: 'material.components.eventCalendar',
79 | deps: false,
80 | constants: {
81 | EVENT_CALENDAR_THEME: file.contents.toString()
82 | }
83 | };
84 | file.contents = new Buffer(JSON.stringify(config), 'utf-8');
85 | this.push(file);
86 | cb();
87 | }))
88 | .pipe(ngConstant())
89 | .pipe(rename('_theme.js'))
90 | .pipe(gulp.dest(paths.src+'/js'))
91 | });
92 | gulp.task('cleanThemeConstant', function () {
93 | return del(paths.src+'/js/_theme.js');
94 | });
95 |
96 |
97 |
98 | gulp.task('clean', function () {
99 | return del(paths.dest);
100 | });
101 |
102 |
103 | gulp.task('copyPartials', function () {
104 | return gulp.src(paths.partials, {base: paths.app})
105 | .pipe(gulp.dest(paths.dest));
106 | });
107 |
108 | gulp.task('copyIcons', function () {
109 | return gulp.src(paths.icons, {base: paths.src})
110 | .pipe(gulp.dest(paths.dest));
111 | });
112 |
113 | gulp.task('buildIconCacheDev', function () {
114 | return gulp.src(paths.icons)
115 | .pipe(templateCache({module: 'material.components.eventCalendar'}))
116 | .pipe(gulp.dest(paths.dest));
117 | });
118 | gulp.task('buildIconCache', function () {
119 | return gulp.src(paths.icons)
120 | .pipe(templateCache({module: 'material.components.eventCalendar'}))
121 | .pipe(gulp.dest(paths.src));
122 | });
123 |
124 | gulp.task('cleanIconCache', function () {
125 | return del('src/templates.js');
126 | });
127 |
128 | gulp.task('serve', serve({
129 | root: ['public', 'bower_components'],
130 | port: 8080
131 | }));
132 |
133 |
134 |
135 | gulp.task('test-karma', function (done) {
136 | new KarmaServer({
137 | configFile: __dirname + '/karma.conf.js',
138 | singleRun: true
139 | }, function (errorCode) {
140 | if (errorCode !== 0) {
141 | console.log('Karma exited with error code ' + errorCode);
142 | done();
143 | return process.exit(errorCode);
144 | }
145 | done();
146 | }).start();
147 | });
148 |
149 | gulp.task('test', gulpSequence('build', 'test-karma'));
150 |
151 |
152 |
153 |
154 | gulp.task('watch', function () {
155 | gulp.watch(paths.scripts, function (event) {
156 | jsBuild.getDevSrc(event.path)()
157 | .on('end', function () {
158 | if (event.type !== 'changed') { indexBuild.inject(); }
159 | });
160 | });
161 |
162 | gulp.watch(paths.appScripts, function (event) {
163 | jsBuild.getDevApp(event.path)()
164 | .on('end', function () {
165 | if (event.type !== 'changed') { indexBuild.inject(); }
166 | });
167 | });
168 |
169 |
170 | gulp.watch(paths.css.concat(paths.appCss), function (event) {
171 | cssBuild.getDev(event.path)()
172 | .on('end', function () {
173 | if (event.type !== 'changed') { indexBuild.inject(); }
174 | });
175 | });
176 |
177 | gulp.watch('src/eventCalendar-theme.scss', ['themeToConstant']);
178 |
179 |
180 | gulp.watch(paths.partials, function (event) {
181 | return gulp.src(event.path, {base: paths.app})
182 | .pipe(gulp.dest(paths.dest));
183 | });
184 | });
185 |
186 |
187 |
188 |
189 |
190 |
191 | gulp.task('major', function(){
192 | gulp.src(['./bower.json', './package.json'])
193 | .pipe(bump({type:'major'}))
194 | .pipe(gulp.dest('./'));
195 | });
196 |
197 | gulp.task('minor', function(){
198 | gulp.src(['./bower.json', './package.json'])
199 | .pipe(bump({type:'minor'}))
200 | .pipe(gulp.dest('./'));
201 | });
202 |
203 | gulp.task('patch', function(){
204 | gulp.src(['./bower.json', './package.json'])
205 | .pipe(bump({type:'patch'}))
206 | .pipe(gulp.dest('./'));
207 | });
208 |
209 | gulp.task('prerelease', function(){
210 | gulp.src(['./bower.json', './package.json'])
211 | .pipe(bump({type:'prerelease'}))
212 | .pipe(gulp.dest('./'));
213 | });
214 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./dist/angular-material-event-calendar');
2 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Wed May 11 2016 11:02:59 GMT-0500 (CDT)
3 | module.exports = function(config) {
4 | config.set({
5 |
6 | // base path that will be used to resolve all patterns (eg. files, exclude)
7 | basePath: '',
8 |
9 |
10 | // frameworks to use
11 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
12 | frameworks: ['jasmine'],
13 |
14 |
15 | // list of files / patterns to load in the browser
16 | files: [
17 | 'dist/md-calendar.css',
18 | 'bower_components/angular-material/angular-material.css',
19 |
20 | 'bower_components/angular/angular.js',
21 | 'bower_components/angular-animate/angular-animate.js',
22 | 'bower_components/angular-material/angular-material.js',
23 | 'bower_components/angular-mocks/angular-mocks.js',
24 | 'src/mdCalendar.js',
25 | 'src/**/*.js'
26 | ],
27 |
28 |
29 | // test results reporter to use
30 | // possible values: 'dots', 'progress'
31 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
32 | reporters: [ 'spec'],
33 |
34 |
35 | // web server port
36 | port: 9876,
37 |
38 |
39 | // enable / disable colors in the output (reporters and logs)
40 | colors: true,
41 |
42 |
43 | // level of logging
44 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
45 | logLevel: config.LOG_INFO,
46 |
47 |
48 | // enable / disable watching file and executing tests whenever any file changes
49 | autoWatch: true,
50 |
51 |
52 | // start these browsers
53 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
54 | // browsers: ['Chrome'],
55 | browsers: ['PhantomJS'],
56 |
57 |
58 | // Continuous Integration mode
59 | // if true, Karma captures browsers, runs the tests and exits
60 | singleRun: false,
61 |
62 | // Concurrency level
63 | // how many browser should be started simultaneous
64 | concurrency: Infinity
65 | })
66 | }
67 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-material-event-calendar",
3 | "version": "0.1.4",
4 | "author": "Ben Rubin",
5 | "description": "Angular material event calander component",
6 | "keywords": "material, material-design, design, angular, component, calendar, event, md",
7 | "license": "MIT",
8 | "repository": {
9 | "type": "git",
10 | "url": "git@github.com/B-3PO/angular-material-event-calendar.git"
11 | },
12 | "main": "index.js",
13 | "devDependencies": {
14 | "bump": "^0.2.5",
15 | "del": "^2.2.0",
16 | "gulp": "^3.9.1",
17 | "gulp-angular-templatecache": "^1.9.1",
18 | "gulp-autoprefixer": "^3.1.0",
19 | "gulp-bump": "^1.0.0",
20 | "gulp-concat": "^2.6.0",
21 | "gulp-cssnano": "^2.1.0",
22 | "gulp-filter": "^4.0.0",
23 | "gulp-flatten": "^0.3.0",
24 | "gulp-if": "^2.0.1",
25 | "gulp-inject": "^4.0.0",
26 | "gulp-jasmine": "^2.4.0",
27 | "gulp-jshint": "^2.0.0",
28 | "gulp-main-bower-files": "^1.5.1",
29 | "gulp-ng-annotate": "^2.0.0",
30 | "gulp-ng-constant": "^1.1.0",
31 | "gulp-preprocess": "^2.0.0",
32 | "gulp-rename": "^1.2.2",
33 | "gulp-sass": "^2.3.2",
34 | "gulp-sequence": "^0.4.5",
35 | "gulp-serve": "^1.2.0",
36 | "gulp-strip-debug": "^1.1.0",
37 | "gulp-uglify": "^1.5.3",
38 | "gulp-util": "^3.0.7",
39 | "gulp-wrap": "^0.11.0",
40 | "jshint": "^2.8.0",
41 | "karma": "^1.1.0",
42 | "karma-jasmine": "^1.0.2",
43 | "karma-phantomjs-launcher": "^1.0.1",
44 | "karma-spec-reporter": "0.0.26",
45 | "through2": "^2.0.1"
46 | },
47 | "scripts": {
48 | "local": "gulp",
49 | "build": "gulp build"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/eventCalendar-colors.scss:
--------------------------------------------------------------------------------
1 | // Borders
2 | $border: #DDD;
3 | $todayBorder: #AAA;
4 |
5 | // Backgrounds
6 | $headerBackground: #FFF;
7 | $eventBackground: #DDD;
8 | $eventBackgroundSelected: #EEE;
9 | $rowBackground: #FFF;
10 | $previousMonthDayBackground: #F5F5F5;
11 | $buttonHover: rgba(158,158,158,0.2);
12 |
13 | // Fonts
14 | $eventFont: #666;
15 | $headerFont: #666;
16 | $monthDayfont: #999;
17 | $monthDayHeaderFont: #999;
18 | $showMoreLink: #4189b8;
19 | $eventSelectedFont: #888;
20 | $button: #333;
21 | $buttonDisabled: #CCC;
22 |
23 |
24 |
25 |
26 | md-event-calendar:not(._md) {
27 | md-event-calendar-header {
28 | color: $headerFont;
29 | background: $headerBackground;
30 | border-color: $border;
31 |
32 | md-event-calendar-next, md-event-calendar-prev {
33 | .md-arrow svg {
34 | fill: $headerFont;
35 | }
36 | }
37 | }
38 |
39 | .md-button:not([disabled]) {
40 | color: $button;
41 | &:hover {
42 | background: $buttonHover;
43 | }
44 | }
45 |
46 | .md-button[disabled] {
47 | color: $buttonDisabled;
48 | }
49 |
50 |
51 | md-event-calendar-month {
52 | .md-event-calendar-month-row-header {
53 | color: $monthDayHeaderFont;
54 | background: $headerBackground;
55 | border-color: $border;
56 | }
57 |
58 | .md-event-calendar-month-row {
59 | background: $rowBackground;
60 | border-color: $border;
61 |
62 | .md-event-calendar-month-cell-divider {
63 | border-color: $border;
64 | }
65 |
66 | .md-event-calendar-month-cell {
67 | border-color: $border;
68 |
69 | .md-event-calendar-month-cell-content {
70 | .md-event-calendar-create-link {
71 | color: $showMoreLink;
72 | }
73 |
74 | .md-event-calendar-cell-data-label {
75 | color: $monthDayfont;
76 | }
77 |
78 | .md-event-calendar-cell-event-show-more-link {
79 | color: $showMoreLink;
80 | }
81 | }
82 |
83 |
84 |
85 | &.different-month {
86 | background: $previousMonthDayBackground;
87 | }
88 |
89 | &.today {
90 | box-shadow: inset 0px 0px 0px 1px $todayBorder;
91 |
92 | .md-event-calendar-month-cell-content {
93 | .md-event-calendar-cell-data-label {
94 | color: $headerFont;
95 | }
96 | }
97 |
98 | .md-event-calendar-month-cell-divider {
99 | border-color: $todayBorder;
100 | }
101 | }
102 |
103 | &:last-child {
104 | border-color: $border;
105 | }
106 | }
107 | }
108 | }
109 |
110 | .md-event-calendar-cell-event {
111 | background: $eventBackground;
112 | color: $eventFont;
113 |
114 | &.md-selected {
115 | color: $eventBackgroundSelected;
116 | background: $eventSelectedFont;
117 | }
118 |
119 | &.md-continue-left, &.md-end-left {
120 | &:after {
121 | border-right-color: $eventBackground;
122 | }
123 | }
124 |
125 | &.md-continue-right, &.md-start-right {
126 | &:after {
127 | border-left-color: $eventBackground;
128 | }
129 | }
130 |
131 |
132 | &.md-selected {
133 | &.md-continue-left, &.md-end-left {
134 | &:after {
135 | border-right-color: $eventSelectedFont;
136 | }
137 | }
138 |
139 | &.md-continue-right, &.md-start-right {
140 | &:after {
141 | border-left-color: $eventSelectedFont;
142 | }
143 | }
144 | }
145 | }
146 |
147 | .md-event-calendar-show-more-container {
148 | .md-event-calendar-show-more-date-label {
149 | color: $monthDayfont;
150 | }
151 |
152 | .md-event-calendar-show-more-close svg {
153 | fill: $monthDayfont;
154 | }
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/eventCalendar-theme.scss:
--------------------------------------------------------------------------------
1 | md-event-calendar._md {
2 | md-event-calendar-header {
3 | color: '{{foreground-1}}';
4 | background: '{{background-hue-1}}';
5 | border-color: '{{foreground-4}}';
6 |
7 | md-event-calendar-next, md-event-calendar-prev {
8 | .md-arrow svg {
9 | fill: '{{foreground-2}}';
10 | }
11 | }
12 | }
13 |
14 |
15 | md-event-calendar-month {
16 | .md-event-calendar-month-row-header {
17 | color: '{{foreground-3}}';
18 | background: '{{background-hue-1}}';
19 | border-color: '{{foreground-4}}';
20 | }
21 |
22 | .md-event-calendar-month-row {
23 | background: '{{background-hue-1}}';
24 | border-color: '{{foreground-4}}';
25 |
26 |
27 | .md-event-calendar-month-cell-divider {
28 | border-color: '{{foreground-4}}';
29 | }
30 |
31 | .md-event-calendar-month-cell {
32 | border-color: '{{foreground-4}}';
33 |
34 | .md-event-calendar-month-cell-content {
35 | .md-event-calendar-create-link {
36 | color: '{{primary-default}}';
37 | }
38 |
39 | .md-event-calendar-cell-data-label {
40 | color: '{{foreground-3}}';
41 | }
42 |
43 | .md-event-calendar-cell-event-show-more-link {
44 | color: '{{primary-default}}';
45 | }
46 | }
47 |
48 |
49 |
50 | &.different-month {
51 | background: '{{background-hue-2}}';
52 | }
53 |
54 | &.today {
55 | box-shadow: inset 0px 0px 0px 1px '{{primary-default}}';
56 |
57 | .md-event-calendar-month-cell-content {
58 | .md-event-calendar-cell-data-label {
59 | color: '{{primary-default}}';
60 | }
61 | }
62 | }
63 |
64 | &:last-child {
65 | border-color: '{{foreground-4}}';
66 | }
67 | }
68 | }
69 | }
70 |
71 | .md-event-calendar-cell-event {
72 | background: '{{foreground-4}}';
73 | color: '{{background-900}}';
74 |
75 | &.md-selected {
76 | color: #EEE;
77 | background: '{{primary-default}}';
78 | }
79 |
80 | &.md-continue-left, &.md-end-left {
81 | &:after {
82 | border-right-color:'{{foreground-4}}';
83 | }
84 | }
85 |
86 | &.md-continue-right, &.md-start-right {
87 | &:after {
88 | border-left-color: '{{foreground-4}}';
89 | }
90 | }
91 |
92 |
93 | &.md-selected {
94 | &.md-continue-left, &.md-end-left {
95 | &:after {
96 | border-right-color: '{{primary-default}}';
97 | }
98 | }
99 |
100 | &.md-continue-right, &.md-start-right {
101 | &:after {
102 | border-left-color: '{{primary-default}}';
103 | }
104 | }
105 | }
106 | }
107 |
108 |
109 | .md-event-calendar-show-more-container {
110 | .md-event-calendar-show-more-date-label {
111 | color: '{{foreground-3}}';
112 | }
113 | .md-event-calendar-show-more-close svg {
114 | fill: '{{foreground-1}}';
115 | }
116 | }
117 | }
118 |
119 |
120 |
121 | md-event-calendar._md.md-primary {
122 | md-event-calendar-header {
123 | color: '{{background-100}}';
124 | background: '{{primary-default}}';
125 |
126 | md-event-calendar-next, md-event-calendar-prev {
127 | .md-arrow svg {
128 | fill: '{{background-100}}';
129 | }
130 | }
131 | }
132 |
133 | md-event-calendar-month {
134 | .md-event-calendar-month-row-header {
135 | color: '{{background-100}}';
136 | background: '{{primary-default}}';
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/eventCalendar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @ngdoc module
3 | * @name material.components.eventCalendar
4 | *
5 | * @description
6 | * Calendar Component
7 | */
8 | angular
9 | .module('material.components.eventCalendar', [])
10 | .config(addEventCalendarTheme);
11 |
12 |
13 | /*@ngInject*/
14 | function addEventCalendarTheme($injector, $provide, EVENT_CALENDAR_THEME) {
15 | var $mdThemingProvider;
16 |
17 | // if using angular material, then register the event theme css
18 | if ($injector.has('$mdThemingProvider')) {
19 | $mdThemingProvider = $injector.get('$mdThemingProvider');
20 | $mdThemingProvider.registerStyles(EVENT_CALENDAR_THEME);
21 | } else {
22 | $provide.decorator('$$rAF', ["$delegate", rAFDecorator]);
23 | }
24 | }
25 |
26 |
27 | // polly fill rAF throttle if not using angular material
28 | function rAFDecorator($delegate) {
29 | $delegate.throttle = function(cb) {
30 | var queuedArgs, alreadyQueued, queueCb, context;
31 | return function debounced() {
32 | queuedArgs = arguments;
33 | context = this;
34 | queueCb = cb;
35 | if (!alreadyQueued) {
36 | alreadyQueued = true;
37 | $delegate(function() {
38 | queueCb.apply(context, Array.prototype.slice.call(queuedArgs));
39 | alreadyQueued = false;
40 | });
41 | }
42 | };
43 | };
44 | return $delegate;
45 | }
46 |
--------------------------------------------------------------------------------
/src/eventCalendar.scss:
--------------------------------------------------------------------------------
1 | md-event-calendar {
2 | display: block;
3 |
4 | md-event-calendar-header {
5 | flex-direction: row;
6 | display: flex;
7 | line-height: 64px;
8 | align-items: center;
9 | border-style: solid;
10 | border-width: 1px 1px 0 1px;
11 |
12 | &.md-center {
13 | justify-content: center;
14 | }
15 |
16 |
17 | md-event-calendar-title {
18 | display: block;
19 | min-width: 170px;
20 | text-align: center;
21 | font-size: 20px;
22 | }
23 |
24 | md-event-calendar-next, md-event-calendar-prev {
25 | display: block;
26 |
27 | .md-arrow {
28 | cursor: pointer;
29 | height: 24px;
30 | width: 24px;
31 | &.md-left-arrow {
32 | transform: rotate(180deg);
33 | }
34 | }
35 | }
36 | }
37 | }
38 |
39 |
40 |
41 |
42 |
43 | md-event-calendar {
44 | // hide create link until hover over the day view. Hide it again if hovering
45 | // over an existing event. This specific structure is necessary in order to
46 | // preserve the correct precedence.
47 |
48 | .md-event-calendar-month-cell-content {
49 | .md-event-calendar-create-link {
50 | opacity: 0;
51 | }
52 | }
53 |
54 | .md-event-calendar-month-cell-content:hover {
55 | .md-event-calendar-create-link {
56 | opacity: 1;
57 | }
58 | }
59 |
60 | md-event-calendar-month {
61 | &.md-event-hover {
62 | .md-event-calendar-month-cell-content .md-event-calendar-create-link {
63 | opacity: 0;
64 | }
65 | }
66 |
67 | &.fitted {
68 | display: flex;
69 | flex-direction: column;
70 | }
71 |
72 | .md-event-calendar-month-row-header {
73 | display: flex;
74 | flex-direction: row;
75 | min-height: 36px;
76 | height: 36px;
77 | align-items: flex-end;
78 | font-size: 12px;
79 | font-weight: 500;
80 | padding-bottom: 12px;
81 | border-style: solid;
82 | border-width: 0 1px 1px 1px;
83 |
84 | .md-event-calendar-month-cell-header {
85 | flex: 1;
86 | padding-left: 6px;
87 | }
88 | }
89 |
90 |
91 |
92 | .md-event-calendar-month-row {
93 | flex: 1;
94 | display: flex;
95 | flex-direction: row;
96 | border-style: solid;
97 | border-width: 0 0 1px 0;
98 |
99 | .md-event-calendar-month-cell {
100 | position: relative;
101 | flex: 1;
102 |
103 | .md-event-calendar-month-cell-spacer {
104 | margin-top: 100%
105 | }
106 |
107 | .md-event-calendar-month-cell-divider {
108 | position: absolute;
109 | top: 0;
110 | bottom: 0;
111 | left: 0;
112 | border-style: solid;
113 | border-width: 0 1px 0 0;
114 | }
115 |
116 | .md-event-calendar-month-cell-content {
117 | position: absolute;
118 | top: 0;
119 | bottom: 0;
120 | left: 0;
121 | right: 0;
122 |
123 | .md-event-calendar-create-link {
124 | align-self: center;
125 | text-transform: uppercase;
126 | font-size: 14px;
127 | font-weight: 500;
128 | padding-right: 12px;
129 | cursor: pointer;
130 | transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
131 | }
132 |
133 | .md-event-calendar-cell-data-label {
134 | font-size: 13px;
135 | padding: 8px;
136 | flex: 1;
137 | }
138 |
139 | .md-event-calendar-cell-event-spacer {
140 | margin: 4px 0 4px 0;
141 | height: 23px;
142 | }
143 |
144 | .md-event-calendar-cell-event-show-more-link {
145 | font-size: 13px;
146 | padding: 4px;
147 | padding-left: 8px;
148 | cursor: pointer;
149 | }
150 | }
151 |
152 |
153 | &:last-child {
154 | border-style: solid;
155 | border-width: 0 1px 0 0;
156 | }
157 | }
158 | }
159 | }
160 |
161 |
162 | .md-event-calendar-cell-event {
163 | font-size: 12px;
164 | min-height: 15px;
165 | padding: 4px;
166 | cursor: pointer;
167 |
168 | &.md-single {
169 | margin: 4px;
170 | border-radius: 2px;
171 | text-overflow: ellipsis;
172 | white-space: nowrap;
173 | overflow: hidden;
174 | }
175 |
176 | &.md-start {
177 | margin: 4px 0 4px 4px;
178 | border-radius: 2px 0 0 2px;
179 | white-space: nowrap;
180 | z-index: 1;
181 | position: relative;
182 | }
183 |
184 | &.md-start-right {
185 | margin: 4px 13px 4px 4px;
186 | border-radius: 2px 0 0 2px;
187 | }
188 |
189 | &.md-end {
190 | margin: 4px 4px 4px 0;
191 | border-radius: 0 2px 2px 0;
192 | }
193 |
194 | &.md-end-left {
195 | margin: 4px 4px 4px 13px;
196 | border-radius: 0 2px 2px 0;
197 | }
198 |
199 | &.md-continue, &.md-continue-both {
200 | margin: 4px 0 4px 0;
201 | border-radius: 0;
202 | }
203 |
204 | &.md-continue-right {
205 | margin: 4px 13px 4px 0;
206 | border-radius: 0;
207 | white-space: nowrap;
208 | }
209 |
210 | &.md-continue-left {
211 | margin: 4px 0 4px 13px;
212 | border-radius: 0;
213 | white-space: nowrap;
214 | }
215 |
216 | &.md-continue-right, &.md-start-right {
217 | &:after {
218 | content: '';
219 | position:absolute;
220 | height: 0;
221 | width: 0;
222 | right: 0;
223 | margin-top: -4px;
224 | border-top: 12px solid transparent;
225 | border-bottom: 11.5px solid transparent;
226 | border-left:13px solid #EEE;
227 | }
228 | }
229 |
230 | &.md-continue-left, &.md-end-left {
231 | &:after {
232 | content: '';
233 | position:absolute;
234 | height: 0;
235 | width: 0;
236 | left: 0;
237 | margin-top: -4px;
238 | border-top: 12px solid transparent;
239 | border-bottom: 11.5px solid transparent;
240 | border-right:13px solid #EEE;
241 | }
242 | }
243 |
244 | .md-event-calendar-cell-event-time {
245 | font-weight: 500;
246 | padding-right: 6px;
247 | pointer-events: none;
248 | }
249 |
250 | span {
251 | pointer-events: none;
252 | }
253 | }
254 |
255 | // show more popup
256 | .md-event-calendar-show-more-content .md-event-calendar-cell-event {
257 | &.md-single,
258 | &.md-end,
259 | &.md-start-right
260 | {
261 | padding-left: 16px;
262 | }
263 |
264 | &.md-single,
265 | &.md-end,
266 | &.md-start-right
267 | {
268 | margin-left: 0;
269 | }
270 |
271 | &.md-single,
272 | &.md-end,
273 | &.md-end-left
274 | {
275 | margin-right: 0;
276 | }
277 | }
278 |
279 |
280 |
281 | .md-event-calendar-show-more-container {
282 | opacity: 0;
283 | // transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
284 |
285 | position: absolute;
286 | top: 0;
287 | left: 1px;
288 | width: 180px;
289 | background: #FFF;
290 | padding: 12px;
291 | padding-top: 7px;
292 | border-radius: 2px;
293 | z-index: 9;
294 | box-shadow: 0 7px 8px -4px rgba(0,0,0,.2),
295 | 0 13px 19px 2px rgba(0,0,0,.14),
296 | 0 5px 24px 4px rgba(0,0,0,.12);
297 |
298 | .md-event-calendar-show-more-content {
299 | position: relative;
300 | }
301 |
302 | &.show {
303 | opacity: 1;
304 |
305 | &:not(.no-transition) {
306 | transition: opacity 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
307 | }
308 | }
309 |
310 | .md-event-calendar-show-more-close {
311 | display: flex;
312 | position: absolute;
313 | top: 6px;
314 | right: 7px;
315 | width: 16px;
316 | height: 16px;
317 | cursor: pointer;
318 |
319 | svg {
320 | pointer-events: none;
321 | }
322 | }
323 |
324 | .md-event-calendar-show-more-date-label {
325 | font-size: 13px;
326 | padding: 6px;
327 | margin-left: -11px;
328 | margin-top: -5px;
329 | }
330 | }
331 | }
332 |
333 |
334 |
335 | md-event-calendar.md-create-disabled {
336 | md-event-calendar-month {
337 | .md-event-calendar-month-row {
338 | .md-event-calendar-month-cell {
339 | .md-event-calendar-month-cell-content {
340 | .md-event-calendar-create-link {
341 | display: none;
342 | }
343 | }
344 | }
345 | }
346 | }
347 | }
348 |
--------------------------------------------------------------------------------
/src/icons/ic_close_black_24px.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/icons/ic_keyboard_arrow_right_black_24px.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/js/eventCalendar.directive.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('material.components.eventCalendar')
3 | .directive('mdEventCalendar', eventCalendarDirective);
4 |
5 |
6 | /**
7 | * @ngdoc directive
8 | * @name mdEventCalendar
9 | * @module material.components.eventCalendar
10 | *
11 | * @restrict E
12 | **/
13 | function eventCalendarDirective($injector, $parse) {
14 | var $mdTheming = $injector.has('$mdTheming') ? $injector.get('$mdTheming') : undefined;
15 | var directive = {
16 | restrict: 'E',
17 | require: ['mdEventCalendar', '?ngModel'],
18 | scope: {
19 | events: '=mdEvents'
20 | },
21 | compile: compile,
22 | controller: controller,
23 | controllerAs: 'mdEventCalendar',
24 | bindToController: true
25 | };
26 | return directive;
27 |
28 |
29 | function compile(tElement, tAttr) {
30 | var eventClickFunc = tAttr.mdEventClick ? $parse(tAttr.mdEventClick, null, true) : undefined;
31 | var createEventClickFunc = tAttr.mdCreateEventClick ? $parse(tAttr.mdCreateEventClick, null, true) : undefined;
32 | var mdCreateDisabled = tAttr.mdCreateDisabled ? $parse(tAttr.mdCreateDisabled) : undefined;
33 | tElement.append(' ');
34 |
35 | return function postLink(scope, element, attrs, ctrls) {
36 | var createDisabled = false;
37 | var mdEventCalendarCtrl = ctrls[0];
38 | var ngModelCtrl = ctrls[1];
39 | if ($mdTheming) {
40 | element.addClass('_md');
41 | $mdTheming(element);
42 | }
43 |
44 | mdEventCalendarCtrl.isCreateDisabled = isCreateDisabled;
45 | mdEventCalendarCtrl.callEventClick = callEventClick;
46 | mdEventCalendarCtrl.createEventClick = createEventClick;
47 |
48 | if (ngModelCtrl) {
49 | ngModelCtrl.$render = render;
50 | mdEventCalendarCtrl.ngModelCtrl = ngModelCtrl;
51 | }
52 |
53 |
54 | function render() {
55 | var viewValue = ngModelCtrl.$viewValue || ngModelCtrl.$modelValue || [];
56 | mdEventCalendarCtrl.selectedEvents = [].concat(viewValue);
57 | }
58 |
59 |
60 | function callEventClick(e, eventItem) {
61 | if (!attrs.mdEventClick) { return; }
62 | eventClickFunc(scope.$parent, {$event: e, $selectedEvent: eventItem});
63 | }
64 |
65 | function createEventClick(e, date) {
66 | if (!attrs.mdCreateEventClick) { return; }
67 | createEventClickFunc(scope.$parent, {$event: e, $date: date});
68 | }
69 |
70 | function isCreateDisabled() {
71 | return createDisabled;
72 | }
73 |
74 | // watch for create being disabled
75 | if (mdCreateDisabled) {
76 | scope.$watch(function () { return mdCreateDisabled(scope.$parent); }, function (value) {
77 | createDisabled = value;
78 | element.toggleClass('md-create-disabled', value);
79 | });
80 |
81 | // if no string was given check to see if the attr exists
82 | } else if (tAttr.mdCreateDisabled !== undefined) {
83 | createDisabled = true;
84 | element.addClass('md-create-disabled');
85 | }
86 | };
87 | }
88 |
89 |
90 | /*@ngInject*/
91 | function controller($$mdEventCalendarUtil, $element, $attrs) {
92 | /*jshint validthis:true*/
93 | var vm = this;
94 |
95 | vm.$element = $element;
96 | vm.labelProperty = $attrs.mdLabel || 'title';
97 | vm.selectedEvents = [];
98 | vm.today = $$mdEventCalendarUtil.createDateAtMidnight();
99 | vm.date = $$mdEventCalendarUtil.createDateAtMidnight();
100 | vm.isToday = $$mdEventCalendarUtil.isSameDay(vm.date, new Date());
101 | vm.monthDisplay = $$mdEventCalendarUtil.months[vm.date.getMonth()];
102 | vm.yearDisplay = vm.date.getFullYear();
103 | vm.isTodayDisabled = true;
104 | vm.showCreateLink = $attrs.mdShowCreateLink !== undefined && $attrs.mdShowCreateLink !== 'false';
105 | vm.nextMonth = nextMonth;
106 | vm.previousMonth = previousMonth;
107 | vm.selectEvent = selectEvent;
108 | vm.setToday = setToday;
109 | vm.autoHeight = $attrs.autoHeight !== undefined;
110 | vm.fitted = $attrs.fitted !== undefined;
111 | vm.offset = vm.autoHeight === false || $attrs.autoHeight === '' || isNaN($attrs.autoHeight.replace('px', '')) ? 0 : parseInt($attrs.autoHeight.replace('px', ''));
112 |
113 |
114 | function nextMonth() {
115 | vm.date = $$mdEventCalendarUtil.getDateInNextMonth(vm.date);
116 | vm.monthDisplay = $$mdEventCalendarUtil.months[vm.date.getMonth()];
117 | vm.yearDisplay = vm.date.getFullYear();
118 | vm.isTodayDisabled = vm.date.getMonth() === (new Date()).getMonth();
119 | }
120 |
121 |
122 | function previousMonth() {
123 | vm.date = $$mdEventCalendarUtil.getDateInPreviousMonth(vm.date);
124 | vm.monthDisplay = $$mdEventCalendarUtil.months[vm.date.getMonth()];
125 | vm.yearDisplay = vm.date.getFullYear();
126 | vm.isTodayDisabled = vm.date.getMonth() === (new Date()).getMonth();
127 | }
128 |
129 | function setToday() {
130 | vm.date = new Date();
131 | vm.monthDisplay = $$mdEventCalendarUtil.months[vm.date.getMonth()];
132 | vm.yearDisplay = vm.date.getFullYear();
133 | vm.isTodayDisabled = true;
134 | }
135 |
136 |
137 | function selectEvent(e, id) {
138 | // TODO create hashkeys for all events and store in reference object
139 | var value = vm.events.filter(function (item) {
140 | return item.$$mdEventId === id;
141 | });
142 |
143 | if (vm.ngModelCtrl) {
144 | vm.ngModelCtrl.$setViewValue(value[0]);
145 | vm.ngModelCtrl.$render();
146 | }
147 | vm.callEventClick(e, value[0]);
148 |
149 | return true;
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/js/eventCalendarBuilder.service.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('material.components.eventCalendar')
3 | .factory('$$mdEventCalendarBuilder', mdEventCalendarBuilderService);
4 |
5 |
6 | var nextId = 0;
7 |
8 | /**
9 | * @ngdoc service
10 | * @name $$mdEventCalendarBuilder
11 | * @module material.components.eventCalendar
12 | **/
13 | /*@ngInject*/
14 | function mdEventCalendarBuilderService($$mdEventCalendarUtil, $templateCache) {
15 | var service = {
16 | month: month,
17 | showMore: showMore
18 | };
19 | return service;
20 |
21 |
22 |
23 | function showMore(opts) {
24 | var date = opts.date;
25 | var selected = opts.selected || [];
26 | var events = opts.events ? filterEventsOnDay(date, opts.events) : [];
27 | var labelProperty = opts.labelProperty;
28 | var showMoreBody = document.createDocumentFragment();
29 | var container = document.createElement('div');
30 | container.classList.add('md-event-calendar-show-more-container');
31 | var content = document.createElement('div');
32 | content.classList.add('md-event-calendar-show-more-content');
33 | var dateLabel = document.createElement('div');
34 | dateLabel.classList.add('md-event-calendar-show-more-date-label');
35 | dateLabel.textContent = $$mdEventCalendarUtil.dates[date.getDate()];
36 | var closeButton = document.createElement('div');
37 | closeButton.classList.add('md-event-calendar-show-more-close');
38 | closeButton.innerHTML = $templateCache.get('icons/ic_close_black_24px.svg');
39 | closeButton.setAttribute('md-show-more-close', 'true');
40 | container.appendChild(dateLabel);
41 | container.appendChild(closeButton);
42 | container.appendChild(content);
43 | showMoreBody.appendChild(container);
44 |
45 | events.forEach(function (item) {
46 | var eventElement;
47 | var isStartThisDay = $$mdEventCalendarUtil.isSameDay(date, item.start);
48 | var isEndThisDay = $$mdEventCalendarUtil.isValidDate(item.end) ? $$mdEventCalendarUtil.isSameDay(date, item.end) : true;
49 | var eventOptions = {
50 | labelProperty: labelProperty,
51 | selected: selected
52 | };
53 |
54 | if (isStartThisDay && isEndThisDay) {
55 | eventElement = createEventElement({className: 'single', hasLabel: true}, item, eventOptions);
56 | } else if (isStartThisDay) {
57 | eventElement = createEventElement({className: 'start-right', hasLabel: true}, item, eventOptions);
58 | } else if (isEndThisDay) {
59 | eventElement = createEventElement({className: 'end-left', hasLabel: true}, item, eventOptions);
60 | } else {
61 | eventElement = createEventElement({className: 'continue', hasLabel: true}, item, eventOptions);
62 | }
63 |
64 | content.appendChild(eventElement);
65 | });
66 |
67 |
68 | var bounds = opts.cell.getBoundingClientRect();
69 | var cellTop = opts.cell.offsetTop;
70 | var cellLeft = opts.cell.offsetLeft;
71 | container.style.top = cellTop+'px';
72 | container.style.left = cellLeft+'px';
73 |
74 | return showMoreBody;
75 | }
76 |
77 |
78 | function filterEventsOnDay(date, events) {
79 | return !events || !events.length ? [] : events.filter(function (item) {
80 | return $$mdEventCalendarUtil.isDateWithinRange(date, item.start, item.end || item.start);
81 | }).sort(function(a, b) {
82 | a = new Date(a.start);
83 | b = new Date(b.start);
84 | return a > b ? 1 : a < b ? -1 : 0;
85 | });
86 | }
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | function month(options) {
100 | var calendarStartDate;
101 | var lastCalendarDayNum;
102 | var d = 0;
103 | var rowNumber = 1;
104 | var firstCalendarDay = true;
105 | var lastCalendarDay = false;
106 | var today = $$mdEventCalendarUtil.createDateAtMidnight();
107 | var date = $$mdEventCalendarUtil.isValidDate(options.date) ? options.date : new Date();
108 | var firstDayOfMonth = $$mdEventCalendarUtil.getFirstDateOfMonth(date);
109 | var firstDayOfTheWeek = (firstDayOfMonth.getDay() + 7) % 7;
110 | var numberOfDaysInMonth = $$mdEventCalendarUtil.getNumberOfDaysInMonth(date);
111 | var events = filterCurrentCalendar(date, options.events);
112 | events.forEach(cleanEvent);
113 | var selected = options.selected || [];
114 | var monthElement = createMonthElement();
115 | var row = createRowElement();
116 | monthElement.appendChild(row);
117 | var cellSize = options.cellHeight - 48;
118 | var maxEvents = Math.floor(cellSize / 24);
119 |
120 |
121 | // days from last month
122 | if (firstDayOfTheWeek > 0) {
123 | calendarStartDate = $$mdEventCalendarUtil.getFirstDateOfMonth(date);
124 | calendarStartDate.setDate(calendarStartDate.getDate() - firstDayOfTheWeek);
125 | while (d < firstDayOfTheWeek) {
126 | row.appendChild(createCellElement(getCellOptions(calendarStartDate, d, true)));
127 | firstCalendarDay = false;
128 | d += 1;
129 | calendarStartDate.setDate(calendarStartDate.getDate() + 1);
130 | }
131 | }
132 |
133 |
134 |
135 | // Add a cell for each day of the month, keeping track of the day of the week so that
136 | // we know when to start a new row.
137 | var dayOfWeek = firstDayOfTheWeek;
138 | var iterationDate = firstDayOfMonth;
139 | d = 1;
140 | while (d <= numberOfDaysInMonth) {
141 | // If we've reached the end of the week, start a new row.
142 | if (dayOfWeek === 7) {
143 | dayOfWeek = 0;
144 | row = createRowElement();
145 | firstCalendarDay = false;
146 | monthElement.appendChild(row);
147 | }
148 |
149 | if (dayOfWeek === 6 && d === numberOfDaysInMonth) {
150 | lastCalendarDay = true;
151 | }
152 |
153 | iterationDate.setDate(d);
154 | row.appendChild(createCellElement(getCellOptions(iterationDate, dayOfWeek)));
155 | firstCalendarDay = false;
156 | dayOfWeek += 1;
157 | d += 1;
158 | }
159 |
160 |
161 | lastCalendarDayNum = d;
162 | // fill in the rest of the row with next month
163 | while (row.childNodes.length < 7) {
164 | if (dayOfWeek === 6) {
165 | lastCalendarDay = true;
166 | }
167 | iterationDate.setDate((d - lastCalendarDayNum) + 1);
168 | row.appendChild(createCellElement(getCellOptions(iterationDate, dayOfWeek, true)));
169 | dayOfWeek += 1;
170 | d += 1;
171 | }
172 |
173 |
174 | return monthElement;
175 |
176 |
177 | function getCellOptions(cellDate, dayOfWeek, differentMonth) {
178 | return {
179 | date: cellDate, // date for day on calendar
180 | today: today, // todays date at midnight
181 | dayOfWeek: dayOfWeek, // 0-6 (sun-sat)
182 | differentMonth: differentMonth || false, // previous or next month overflow days
183 | events: events, // events arr
184 | isFirstDay: firstCalendarDay, // is first day of current month view. not the first day of month(unless that is sunday)
185 | isLastDay: lastCalendarDay, // last day of calenday. not last day of month (unless sat)
186 | maxEvents: maxEvents, // max events that can be displayed in a day cell. based on cell size
187 | selected: selected, // array of selected events. from ngModel
188 | labelProperty: options.labelProperty, // name of the label property. default: title
189 | showCreateLink: options.showCreateLink // show create link on hover of day cell
190 | };
191 | }
192 | }
193 |
194 |
195 |
196 | function createCellElement(options) {
197 | var cell = document.createElement('div');
198 | cell.classList.add('md-event-calendar-month-cell');
199 | cell.setAttribute('md-date', options.date);
200 | if (options.differentMonth === true) { cell.classList.add('different-month'); }
201 | if ($$mdEventCalendarUtil.isSameDay(options.date, options.today)) { cell.classList.add('today'); }
202 |
203 | var cellSpacer = document.createElement('div');
204 | cellSpacer.classList.add('md-event-calendar-month-cell-spacer');
205 | cell.appendChild(cellSpacer);
206 |
207 | var divider = document.createElement('div');
208 | divider.classList.add('md-event-calendar-month-cell-divider');
209 | cell.appendChild(divider);
210 |
211 | var cellContent = document.createElement('div');
212 | cellContent.setAttribute('md-create-event', '');
213 | cellContent.classList.add('md-event-calendar-month-cell-content');
214 | cell.appendChild(cellContent);
215 |
216 | var cellHeader = document.createElement('div');
217 | cellHeader.setAttribute('md-create-event', '');
218 | cellHeader.classList.add('layout-row');
219 | cellContent.appendChild(cellHeader);
220 |
221 | var dateLabel = document.createElement('div');
222 | dateLabel.setAttribute('md-create-event', '');
223 | dateLabel.classList.add('md-event-calendar-cell-data-label');
224 | dateLabel.textContent = $$mdEventCalendarUtil.dates[options.date.getDate()];
225 | cellHeader.appendChild(dateLabel);
226 |
227 | if (options.showCreateLink === true) {
228 | var createLink = document.createElement('div');
229 | createLink.setAttribute('md-create-event', '');
230 | createLink.classList.add('md-event-calendar-create-link');
231 | createLink.textContent = 'Create';
232 | cellHeader.appendChild(createLink);
233 | }
234 |
235 | createEventElements(cellContent, options);
236 |
237 | return cell;
238 | }
239 |
240 |
241 |
242 | function createEventElements(cellContent, options) {
243 | var i;
244 | var place = 0;
245 | var hasEvents = false;
246 | var matchingEvents = getEventsInRange(options.date, options.events);
247 | matchingEvents = setEventPlaces(matchingEvents, options.dayOfWeek);
248 | matchingEvents.every(function (eventItem, pos) {
249 | var type = getEventDisplayType(eventItem, options);
250 | var placeDiff = eventItem.$$place - place;
251 | hasEvents = true;
252 | place = eventItem.$$place + 1;
253 | i = 0;
254 | // add spacer items for overflow events from last day
255 | while (i < placeDiff) {
256 | if (place >= options.maxEvents) {
257 | cellContent.appendChild(createShowMore(matchingEvents.length - pos, options.date));
258 | return false;
259 | }
260 | cellContent.appendChild(createEventSpacerElement());
261 | i += 1;
262 | }
263 |
264 | if (place >= options.maxEvents) {
265 | cellContent.appendChild(createShowMore(matchingEvents.length - pos, options.date));
266 | return false;
267 | }
268 | cellContent.appendChild(createEventElement(type, eventItem, options));
269 | return true;
270 | });
271 |
272 | if (hasEvents === true) {
273 | cellContent.classList.add('md-has-events');
274 | }
275 | }
276 |
277 |
278 | function createShowMore(num, date) {
279 | var showMoreElement = document.createElement('div');
280 | showMoreElement.classList.add('md-event-calendar-cell-event-show-more-link');
281 | showMoreElement.textContent = num+' more';
282 | showMoreElement.setAttribute('md-show-more', date.toISOString());
283 | return showMoreElement;
284 | }
285 |
286 |
287 | function createEventSpacerElement() {
288 | var spacer = document.createElement('div');
289 | spacer.classList.add('md-event-calendar-cell-event-spacer');
290 | return spacer;
291 | }
292 |
293 | function createEventElement(type, eventItem, options) {
294 | var hash = getHashValue(eventItem);
295 | var eventElement = document.createElement('div');
296 | eventElement.setAttribute('md-event-id', hash);
297 | eventElement.classList.add('md-event-calendar-cell-event');
298 | eventElement.classList.add('md-'+type.className);
299 | if (eventItem.customClass) { eventElement.classList.add(eventItem.customClass); }
300 |
301 | if (type.hasLabel === true) {
302 | // do not show time for allDay events
303 | if (type.allDay !== true) {
304 | var dateLabelTime = document.createElement('span');
305 | dateLabelTime.classList.add('md-event-calendar-cell-event-time');
306 | dateLabelTime.textContent = $$mdEventCalendarUtil.formatEventTime(eventItem.start);
307 | eventElement.appendChild(dateLabelTime);
308 | }
309 |
310 | var dateLabelText = document.createElement('span');
311 | dateLabelText.textContent = eventItem[options.labelProperty];
312 | eventElement.appendChild(dateLabelText);
313 | }
314 |
315 | options.selected.every(function (sel) {
316 | if (sel.$$mdEventId !== undefined && sel.$$mdEventId === eventItem.$$mdEventId) {
317 | eventElement.classList.add('md-selected');
318 | return false;
319 | }
320 | return true;
321 | });
322 |
323 | return eventElement;
324 | }
325 |
326 |
327 |
328 | function getHashValue(value) {
329 | if (angular.isObject(value)) {
330 | return 'object_' + (value.$$mdEventId || (value.$$mdEventId = ++nextId));
331 | }
332 | return 'id_' + (++nextId);
333 | }
334 |
335 |
336 | function getEventDisplayType(item, options) {
337 | var className;
338 | var hasLabel;
339 |
340 | var isStartThisDay = $$mdEventCalendarUtil.isSameDay(options.date, item.start);
341 | var isEndThisDay = $$mdEventCalendarUtil.isValidDate(item.end) ? $$mdEventCalendarUtil.isSameDay(options.date, item.end) : true;
342 |
343 | // single day event
344 | if (isStartThisDay && (options.allDay || isEndThisDay)) {
345 | className = 'single';
346 | hasLabel = true;
347 |
348 | // starts today on last day of week
349 | } else if (isStartThisDay && options.dayOfWeek === 6) {
350 | className = 'start-right';
351 | hasLabel = true;
352 |
353 | // starts today
354 | } else if (isStartThisDay) {
355 | className = 'start';
356 | hasLabel = true;
357 |
358 | // ends on sunday
359 | } else if (isEndThisDay && options.dayOfWeek === 0) {
360 | className = 'end-left';
361 | hasLabel = true;
362 |
363 | // last day of event
364 | } else if (isEndThisDay) {
365 | className = 'end';
366 | hasLabel = options.isFirstDay; // add label if event is continuing from last month
367 |
368 | // continuation on sunday
369 | } else if (options.dayOfWeek === 0) {
370 | className = 'continue-left';
371 | hasLabel = true;
372 |
373 | // continue on sat
374 | } else if (options.dayOfWeek === 6) {
375 | className = 'continue-right';
376 | hasLabel = false;
377 |
378 | // continuation
379 | } else {
380 | className = 'continue';
381 | hasLabel = false;
382 | }
383 |
384 | return {
385 | className: className,
386 | hasLabel: hasLabel,
387 | allDay: item.allDay || false
388 | };
389 | }
390 |
391 | function getEventsInRange(date, events) {
392 | return events.filter(function (item) {
393 | return $$mdEventCalendarUtil.isDateWithinRange(date, item.start, item.end || item.start);
394 | });
395 | }
396 |
397 | function setEventPlaces(events, dayOfWeek) {
398 | var takenPlaces = [];
399 | var sorted = events.sort(function (a, b) {
400 | if (a.end > b.end) { return -1; }
401 | if (a.end < b.end) { return 1; }
402 | return 0;
403 | });
404 |
405 | // if not first day of week then get event palces. this is for dates that come from previous days
406 | // otherwise reset places
407 | sorted.forEach(function (item) {
408 | if (dayOfWeek === 0) { item.$$place = undefined; }
409 | else if (item.$$place !== undefined) { takenPlaces.push(item.$$place); }
410 | });
411 |
412 | // fill in places that have not been set
413 | sorted.forEach(function(item) {
414 | if (item.$$place === undefined) { item.$$place = getPlace(); }
415 | });
416 |
417 | // sort on places
418 | return sorted.sort(function(a, b) {
419 | if (a.$$place > b.$$place) { return 1; }
420 | if (a.$$place < b.$$place) { return -1; }
421 | return 0;
422 | });
423 |
424 |
425 | // find lowest place not taken
426 | function getPlace() {
427 | var place = 0;
428 | while (takenPlaces.indexOf(place) !== -1) {
429 | place++;
430 | }
431 | takenPlaces.push(place);
432 | return place;
433 | }
434 | }
435 |
436 |
437 | function createMonthElement() {
438 | var monthBody = document.createDocumentFragment();
439 | var headerRow = document.createElement('div');
440 | headerRow.classList.add('md-event-calendar-month-row-header');
441 | monthBody.appendChild(headerRow);
442 |
443 | // add header day labels
444 | $$mdEventCalendarUtil.days.forEach(function (name) {
445 | var dayHeader = document.createElement('div');
446 | dayHeader.classList.add('md-event-calendar-month-cell-header');
447 | dayHeader.textContent = name.slice(0,3).toLowerCase();
448 | headerRow.appendChild(dayHeader);
449 | });
450 |
451 | return monthBody;
452 | }
453 |
454 | function createRowElement() {
455 | var row = document.createElement('div');
456 | row.classList.add("md-event-calendar-month-row");
457 | return row;
458 | }
459 |
460 |
461 | function filterCurrentCalendar(date, events) {
462 | if (!events || !events.length) { return []; }
463 | // back fill 6 days for posibility of last month days showing up
464 | var start = $$mdEventCalendarUtil.getFirstDateOfMonth(date).getDate(-6);
465 | // front fill 6 days for posibility of next month days showing up
466 | var end = $$mdEventCalendarUtil.getFirstDateOfMonth(date).getDate(37);
467 |
468 | return events.filter(function (item) {
469 | if (!$$mdEventCalendarUtil.isValidDate(item.start)) { return false; }
470 | if ($$mdEventCalendarUtil.isDateWithinRange(item.start, start, end)) { return true; }
471 | if (!$$mdEventCalendarUtil.isValidDate(item.end)) { return false; }
472 | if ($$mdEventCalendarUtil.isDateWithinRange(item.end, start, end)) { return true; }
473 | return false;
474 | }).sort(function(a, b) {
475 | a = new Date(a.start);
476 | b = new Date(b.start);
477 | return a > b ? 1 : a < b ? -1 : 0;
478 | });
479 | }
480 |
481 |
482 | function cleanEvent(item) {
483 | item.$$hide = undefined;
484 | item.$$place = undefined;
485 | }
486 | }
487 |
--------------------------------------------------------------------------------
/src/js/eventCalendarMonth.directive.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('material.components.eventCalendar')
3 | .directive('mdEventCalendarMonth', eventCalendarMonthDirective);
4 |
5 |
6 | /**
7 | * @ngdoc directive
8 | * @name eventCalendarMonthDirective
9 | * @module material.components.eventCalendar
10 | *
11 | * @restrict E
12 | **/
13 | /*@ngInject*/
14 | function eventCalendarMonthDirective($$mdEventCalendarBuilder, $window, $$rAF, $timeout) {
15 | var directive = {
16 | restrict: 'E',
17 | require: '^mdEventCalendar',
18 | link: link
19 | };
20 | return directive;
21 |
22 |
23 | function link(scope, element, attrs, ctrl) {
24 | var showMoreData;
25 |
26 | var mdEventCalendarCtrl = ctrl;
27 | var rebuildThrottle = $$rAF.throttle(function () {
28 | scope.$evalAsync(function () {
29 | setAutoHeight();
30 | element.toggleClass('fitted', mdEventCalendarCtrl.fitted);
31 | buildView();
32 | });
33 | });
34 |
35 | scope.$watch(function () { return mdEventCalendarCtrl.date; }, buildView);
36 | scope.$watch(function () { return mdEventCalendarCtrl.events; }, function (newValue, oldValue) {
37 | if (newValue === oldValue) { return; }
38 | buildView();
39 | }, true);
40 | scope.$watch(function () { return mdEventCalendarCtrl.selectedEvents; }, function (newValue, oldValue) {
41 | if (newValue === oldValue) { return; }
42 | buildView();
43 | }, true);
44 |
45 | angular.element($window).on('resize', rebuildThrottle);
46 | scope.$on('$destroy', function () {
47 | angular.element($window).off('resize', rebuildThrottle);
48 | });
49 |
50 | $$rAF(function () {
51 | setAutoHeight();
52 | element.toggleClass('fitted', mdEventCalendarCtrl.fitted);
53 | });
54 |
55 | function setAutoHeight() {
56 | if (!mdEventCalendarCtrl.autoHeight) { return; }
57 | mdEventCalendarCtrl.fitted = true;
58 | var top = element[0].getBoundingClientRect().top;
59 | var height = $window.innerHeight - top - mdEventCalendarCtrl.offset;
60 | element.css('height', height+'px');
61 | }
62 |
63 | hideCreateLinkOnEventItemHover();
64 |
65 | // When user mouses over an existing event, add a class of md-event-hover to
66 | // the month element, so that the create link is hidden from view.
67 | function hideCreateLinkOnEventItemHover() {
68 | element.on('mouseenter', function () {
69 | element.on('mousemove', checkForEventItemRAF);
70 | });
71 |
72 | element.on('mouseleave', function () {
73 | element.off('mousemove', checkForEventItemRAF);
74 | element.removeClass('md-event-hover');
75 | });
76 |
77 | var lastHoverItem;
78 | var checkForEventItemRAF = $$rAF.throttle(checkForEventItem);
79 | function checkForEventItem(e) {
80 | if (mdEventCalendarCtrl.isCreateDisabled() === true) { return; }
81 | if (lastHoverItem === e.target) { return; }
82 | lastHoverItem = e.target;
83 |
84 | var targetIsEvent = !!e.target.getAttribute('md-event-id');
85 |
86 | element.toggleClass('md-event-hover', targetIsEvent);
87 | }
88 | }
89 |
90 | element.on('click', function (e) {
91 | if (mdEventCalendarCtrl.isCreateDisabled() === true) { return; }
92 |
93 | var eventId = e.target.getAttribute('md-event-id');
94 | var showMore = e.target.getAttribute('md-show-more');
95 | var showMoreClose = e.target.getAttribute('md-show-more-close');
96 | var createEvent = e.target.getAttribute('md-create-event') !== null;
97 |
98 | if (eventId) {
99 | var eventItem = getIdFromHash(eventId);
100 | scope.$apply(function () {
101 | mdEventCalendarCtrl.selectEvent(e, getIdFromHash(eventId));
102 | });
103 | return;
104 | }
105 |
106 | removeShowMore(true);
107 | if (showMore) {
108 | addShowMore(new Date(showMore));
109 | }
110 |
111 | if (createEvent) {
112 | var cellDate = getDateFromCreate(e.target);
113 | if (cellDate !== undefined) {
114 | scope.$apply(function () {
115 | mdEventCalendarCtrl.createEventClick(e, cellDate);
116 | });
117 | }
118 | }
119 | });
120 |
121 | function getDateFromCreate(el) {
122 | var dateString = el.getAttribute('md-date');
123 | while (dateString === null && el.nodeName !== 'MD-EVENT-CALENDAR-MONTH') {
124 | el = el.parentNode;
125 | dateString = el.getAttribute('md-date');
126 | }
127 |
128 | return dateString === null ? undefined : new Date(dateString);
129 | }
130 |
131 |
132 | function buildView() {
133 | var cellHeight;
134 | if (mdEventCalendarCtrl.fitted) {
135 | cellHeight = element[0].offsetHeight / 5;
136 | } else {
137 | cellHeight = mdEventCalendarCtrl.$element[0].offsetWidth / 7;
138 | }
139 |
140 | var monthElement = $$mdEventCalendarBuilder.month({
141 | date: mdEventCalendarCtrl.date,
142 | events: mdEventCalendarCtrl.events,
143 | selected: mdEventCalendarCtrl.selectedEvents,
144 | labelProperty: mdEventCalendarCtrl.labelProperty,
145 | showCreateLink: mdEventCalendarCtrl.showCreateLink,
146 | cellHeight: cellHeight
147 | });
148 | element.empty();
149 | element.append(monthElement);
150 |
151 | buildShowMore();
152 | }
153 |
154 |
155 | function addShowMore(date) {
156 | showMoreData = {
157 | date: date
158 | };
159 | buildShowMore(true);
160 | }
161 |
162 | function buildShowMore(animate) {
163 | if (showMoreData === undefined) { return; }
164 | if (showMoreData.element) {
165 | angular.element(showMoreData.element).remove();
166 | showMoreData.element = undefined;
167 | }
168 |
169 | var cell = getCellFromDate(showMoreData.date);
170 | var showMoreFragment = $$mdEventCalendarBuilder.showMore({
171 | date: showMoreData.date,
172 | selected: mdEventCalendarCtrl.selectedEvents,
173 | events: mdEventCalendarCtrl.events,
174 | labelProperty: mdEventCalendarCtrl.labelProperty,
175 | cell: cell
176 | });
177 |
178 | element.append(showMoreFragment);
179 | showMoreData.element = element[0].lastChild;
180 | positonShowMore();
181 |
182 | if (animate) {
183 | // add class after element added so animation
184 | $timeout(function () {
185 | angular.element(showMoreData.element).addClass('show');
186 | }, 0);
187 | } else {
188 | angular.element(showMoreData.element).addClass('no-transition');
189 | angular.element(showMoreData.element).addClass('show');
190 | }
191 | }
192 |
193 | function positonShowMore() {
194 | var showMoreBounds = showMoreData.element.getBoundingClientRect();
195 | var minTop = $window.innerHeight - showMoreBounds.height;
196 | var minLeft = $window.innerWidth - showMoreBounds.width;
197 | var leftDiff = showMoreBounds.left - minLeft;
198 |
199 | if (showMoreBounds.top > minTop) {
200 | showMoreData.element.style.top = minTop+'px';
201 | }
202 |
203 | if (leftDiff > 0) {
204 | showMoreData.element.style.left = minLeft+'px';
205 | // offset date
206 |
207 | leftDiff -= 10;
208 | if (leftDiff > 0) {
209 | showMoreData.element.querySelector('.md-event-calendar-show-more-date-label').style.marginLeft = leftDiff+'px';
210 | }
211 | }
212 | }
213 |
214 | function getCellFromDate(date) {
215 | return element[0].querySelector('[md-date="'+date+'"]');
216 | }
217 |
218 | function removeShowMore(animate) {
219 | if (!showMoreData) { return; }
220 |
221 | var el = showMoreData.element;
222 | showMoreData = undefined;
223 |
224 | if (animate) {
225 | angular.element(el).removeClass('no-transition');
226 | $timeout(function () {
227 | angular.element(el).removeClass('show');
228 | }, 0);
229 | $timeout(function () {
230 | el.remove();
231 | el = undefined;
232 | }, 400);
233 | } else {
234 | el.remove();
235 | el = undefined;
236 | }
237 | }
238 |
239 |
240 | function getClosestCell(el) {
241 | var target = angular.element(el).parent();
242 | while (target.hasClass('md-event-calendar-month-cell') === false) {
243 | target = target.parent();
244 | }
245 | return target;
246 | }
247 |
248 | function getIdFromHash(id) {
249 | return parseInt(id.replace('object_', ''));
250 | }
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/src/js/eventCalendarNext.directive.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('material.components.eventCalendar')
3 | .directive('mdEventCalendarNext', mdEventCalendarNextDirective);
4 |
5 | /**
6 | * @ngdoc directive
7 | * @name mdEventCalendarNextDirective
8 | * @module material.components.eventCalendar
9 | *
10 | * @restrict E
11 | **/
12 | function mdEventCalendarNextDirective() {
13 | var directive = {
14 | restrict: 'E',
15 | require: '^mdEventCalendar',
16 | template: ''+
17 | '
'+
18 | ' '
19 | };
20 | return directive;
21 | }
22 |
--------------------------------------------------------------------------------
/src/js/eventCalendarPrev.directive.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('material.components.eventCalendar')
3 | .directive('mdEventCalendarPrev', mdEventCalendarPrevDirective);
4 |
5 | /**
6 | * @ngdoc directive
7 | * @name mdEventCalendarPrevDirective
8 | * @module material.components.eventCalendar
9 | *
10 | * @restrict E
11 | **/
12 | function mdEventCalendarPrevDirective() {
13 | var directive = {
14 | restrict: 'E',
15 | require: '^mdEventCalendar',
16 | template: ''+
17 | '
'+
18 | ' '
19 | };
20 | return directive;
21 | }
22 |
--------------------------------------------------------------------------------
/src/js/eventCalendarTitle.directive.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('material.components.eventCalendar')
3 | .directive('mdEventCalendarTitle', mdEventCalendarTitleDirective);
4 |
5 | /**
6 | * @ngdoc directive
7 | * @name mdEventCalendarTitleDirective
8 | * @module material.components.eventCalendar
9 | *
10 | * @restrict E
11 | **/
12 | function mdEventCalendarTitleDirective() {
13 | var directive = {
14 | restrict: 'E',
15 | require: '^mdEventCalendar',
16 | template: '',
17 | link: link
18 | };
19 | return directive;
20 |
21 | function link(scope, element, attrs, ctrl) {
22 | scope.mdEventCalendar = ctrl;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/js/eventCalendarToday.directive.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('material.components.eventCalendar')
3 | .directive('mdEventCalendarToday', mdEventCalendarTodayDirective);
4 |
5 | /**
6 | * @ngdoc directive
7 | * @name mdEventCalendarTodayDirective
8 | * @module material.components.eventCalendar
9 | *
10 | * @restrict E
11 | **/
12 | function mdEventCalendarTodayDirective() {
13 | var directive = {
14 | restrict: 'E',
15 | require: '^mdEventCalendar',
16 | template: 'Today '
17 | };
18 | return directive;
19 | }
20 |
--------------------------------------------------------------------------------
/src/js/utij.js:
--------------------------------------------------------------------------------
1 | angular
2 | .module('material.components.eventCalendar')
3 | .factory('$$mdEventCalendarUtil', utilService);
4 |
5 |
6 | function utilService($injector, $locale, $filter) {
7 | var $mdDateLocale = $injector.has('$mdDateLocale') ? $injector.get('$mdDateLocale') : undefined;
8 | var dateFilter = $filter('date');
9 | var months = $mdDateLocale ? $mdDateLocale.months : $locale.DATETIME_FORMATS.MONTH;
10 | var shortMonths = $mdDateLocale ? $mdDateLocale.shortMonths : $locale.DATETIME_FORMATS.SHORTMONTH;
11 | var days = $mdDateLocale ? $mdDateLocale.days : $locale.DATETIME_FORMATS.DAY;
12 | var shortDays = $mdDateLocale ? $mdDateLocale.shortDays : $locale.DATETIME_FORMATS.SHORTDAY.map(function(day) {
13 | return day.substring(0, 1);
14 | });
15 | // The default dates are simply the numbers 1 through 31.
16 | var defaultDates = Array(32);
17 | for (var i = 1; i <= 31; i++) {
18 | defaultDates[i] = i;
19 | }
20 |
21 |
22 | var service = {
23 | months: months,
24 | shortMonths: shortMonths,
25 | days: days,
26 | dates: $mdDateLocale ? $mdDateLocale.dates : defaultDates,
27 | shortDays: shortDays,
28 | isValidDate: isValidDate,
29 | createDateAtMidnight: createDateAtMidnight,
30 | getDateInNextMonth: getDateInNextMonth,
31 | getDateInPreviousMonth: getDateInPreviousMonth,
32 | getFirstDateOfMonth: getFirstDateOfMonth,
33 | getNumberOfDaysInMonth: getNumberOfDaysInMonth,
34 | weekNumberFormatter: $mdDateLocale ? $mdDateLocale.weekNumberFormatter : weekNumberFormatter,
35 | isSameMonthAndYear: isSameMonthAndYear,
36 | isSameDay: isSameDay,
37 | isDateWithinRange: isDateWithinRange,
38 | formatEventTime: formatEventTime
39 | };
40 | return service;
41 |
42 |
43 |
44 |
45 | function formatEventTime(date) {
46 | return dateFilter(date, 'h:mm a');
47 | }
48 |
49 |
50 |
51 | /**
52 | * Checks if a date is within a min and max range, ignoring the time component.
53 | * If minDate or maxDate are not dates, they are ignored.
54 | * @param {Date} date
55 | * @param {Date} minDate
56 | * @param {Date} maxDate
57 | */
58 | function isDateWithinRange(date, minDate, maxDate) {
59 | var dateAtMidnight = createDateAtMidnight(date);
60 | var minDateAtMidnight = isValidDate(minDate) ? createDateAtMidnight(minDate) : null;
61 | var maxDateAtMidnight = isValidDate(maxDate) ? createDateAtMidnight(maxDate) : null;
62 | return (!minDateAtMidnight || minDateAtMidnight <= dateAtMidnight) &&
63 | (!maxDateAtMidnight || maxDateAtMidnight >= dateAtMidnight);
64 | }
65 |
66 |
67 |
68 | /**
69 | * Default week number formatter.
70 | * @param number
71 | * @returns {string}
72 | */
73 | function weekNumberFormatter(number) {
74 | return 'Week ' + number;
75 | }
76 |
77 |
78 | /**
79 | * Sets a date's time to midnight.
80 | * @param {Date} date
81 | */
82 | function setDateTimeToMidnight(date) {
83 | if (isValidDate(date)) {
84 | date.setHours(0, 0, 0, 0);
85 | }
86 | }
87 |
88 | /**
89 | * Checks whether a date is valid.
90 | * @param {Date} date
91 | * @return {boolean} Whether the date is a valid Date.
92 | */
93 | function isValidDate(date) {
94 | return date && date.getTime && !isNaN(date.getTime());
95 | }
96 |
97 |
98 | /**
99 | * Creates a date with the time set to midnight.
100 | * Drop-in replacement for two forms of the Date constructor:
101 | * 1. No argument for Date representing now.
102 | * 2. Single-argument value representing number of seconds since Unix Epoch
103 | * or a Date object.
104 | * @param {number|Date=} opt_value
105 | * @return {Date} New date with time set to midnight.
106 | */
107 | function createDateAtMidnight(opt_value) {
108 | var date;
109 | if (opt_value === undefined) {
110 | date = new Date();
111 | } else {
112 | date = new Date(opt_value);
113 | }
114 | setDateTimeToMidnight(date);
115 | return date;
116 | }
117 |
118 |
119 |
120 | /**
121 | * Get an arbitrary date in the month after the given date's month.
122 | * @param date
123 | * @returns {Date}
124 | */
125 | function getDateInNextMonth(date) {
126 | return new Date(date.getFullYear(), date.getMonth() + 1, 1);
127 | }
128 |
129 |
130 |
131 | /**
132 | * Get an arbitrary date in the month before the given date's month.
133 | * @param date
134 | * @returns {Date}
135 | */
136 | function getDateInPreviousMonth(date) {
137 | return new Date(date.getFullYear(), date.getMonth() - 1, 1);
138 | }
139 |
140 |
141 |
142 |
143 | /**
144 | * Gets the first day of the month for the given date's month.
145 | * @param {Date} date
146 | * @returns {Date}
147 | */
148 | function getFirstDateOfMonth(date) {
149 | return new Date(date.getFullYear(), date.getMonth(), 1);
150 | }
151 |
152 |
153 | /**
154 | * Gets the number of days in the month for the given date's month.
155 | * @param date
156 | * @returns {number}
157 | */
158 | function getNumberOfDaysInMonth(date) {
159 | return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
160 | }
161 |
162 |
163 | /**
164 | * Gets whether two dates have the same month and year.
165 | * @param {Date} d1
166 | * @param {Date} d2
167 | * @returns {boolean}
168 | */
169 | function isSameMonthAndYear(d1, d2) {
170 | return d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth();
171 | }
172 |
173 |
174 | /**
175 | * Gets whether two dates are the same day (not not necesarily the same time).
176 | * @param {Date} d1
177 | * @param {Date} d2
178 | * @returns {boolean}
179 | */
180 | function isSameDay(d1, d2) {
181 | return d1.getDate() == d2.getDate() && isSameMonthAndYear(d1, d2);
182 | }
183 | }
184 |
--------------------------------------------------------------------------------