Simple Calendar – Google Calendar Plugin - Version 3.0.0

Version Description

  • November 8, 2015 =
  • Announcement: Plugin renamed to Simple Calendar.
  • Announcement: Visit our new website at simplecalendar.io!
  • Feature: Modular and extensible plugin, add-ons ready.
  • Feature: Reworked default calendar views, now fully responsive (and titles in grid).
  • Feature: Completely redesigned the admin user interface.
  • Feature: Many new settings panel in admin dashboard, with better semantics and organization.
  • Feature: A System Report page to help you troubleshoot problems and improve support response.
  • Tweak: Use categories to organize your calendars.
  • Tweak: Feeds moved from 'gce_feed' to 'calendar' post type slug, permalink change.
  • Fix: Timezones handling are more accurate.
  • Fix: Incompatibilities with themes and other plugins.
  • Fix: Several other bugfixes.
  • Refactor: Plugin rebuilt from ground up: namespaces, closures, Composer support, entirely OOP.
  • Dev: PHP 5.3 minimum required.
  • Dev: All requests to Google from now on will be handled with the official Google API PHP Client.
Download this release

Release Info

Developer pderksen
Plugin Icon 128x128 Simple Calendar – Google Calendar Plugin
Version 3.0.0
Comparing to
See all releases

Code changes from version 2.4.0 to 3.0.0

Files changed (127) hide show
  1. assets/banner-772x250.jpg +0 -0
  2. assets/css/admin-activation.css +0 -0
  3. assets/css/admin-activation.min.css +5 -0
  4. assets/css/admin-add-calendar.css +34 -0
  5. assets/css/admin-add-calendar.min.css +6 -0
  6. assets/css/admin.css +563 -0
  7. assets/css/admin.min.css +6 -0
  8. assets/css/default-calendar-grid.css +402 -0
  9. assets/css/default-calendar-grid.min.css +6 -0
  10. assets/css/default-calendar-list.css +354 -0
  11. assets/css/default-calendar-list.min.css +6 -0
  12. assets/css/vendor/qtip.css +124 -0
  13. assets/css/vendor/qtip.min.css +3 -0
  14. assets/css/vendor/select2.css +431 -0
  15. assets/css/vendor/select2.min.css +1 -0
  16. assets/fonts/simple-calendar.eot +0 -0
  17. assets/fonts/simple-calendar.svg +33 -0
  18. assets/fonts/simple-calendar.ttf +0 -0
  19. assets/fonts/simple-calendar.woff +0 -0
  20. assets/gcal-icon-16x16.png +0 -0
  21. assets/icon-128x128.png +0 -0
  22. assets/icon-256x256.png +0 -0
  23. assets/images/simple-calendar-icon-fill.svg +32 -0
  24. assets/images/simple-calendar-icon-strokes.svg +31 -0
  25. assets/images/welcome/calendar-settings-appearance.png +0 -0
  26. assets/images/welcome/google-calendar-pro-list-view-annotated.png +0 -0
  27. assets/images/welcome/grid-view-custom-colors.png +0 -0
  28. assets/images/welcome/grid-view-widget-dark-theme.png +0 -0
  29. assets/images/welcome/icon-185x185.png +0 -0
  30. assets/images/welcome/list-view-widget.png +0 -0
  31. assets/js/admin-add-calendar.js +122 -0
  32. assets/js/admin-add-calendar.min.js +6 -0
  33. assets/js/admin.js +535 -0
  34. assets/js/admin.min.js +6 -0
  35. assets/js/default-calendar.js +436 -0
  36. assets/js/default-calendar.min.js +6 -0
  37. js/imagesloaded.pkgd.js → assets/js/vendor/imagesloaded.js +0 -0
  38. js/imagesloaded.pkgd.min.js → assets/js/vendor/imagesloaded.min.js +0 -0
  39. js/jquery.qtip.js → assets/js/vendor/qtip.js +724 -4
  40. assets/js/vendor/qtip.min.js +5 -0
  41. assets/js/vendor/select2.js +5403 -0
  42. assets/js/vendor/select2.min.js +2 -0
  43. assets/js/vendor/tiptip.js +191 -0
  44. assets/js/vendor/tiptip.min.js +21 -0
  45. assets/screenshot-1.png +0 -0
  46. assets/screenshot-2.png +0 -0
  47. assets/screenshot-3.png +0 -0
  48. assets/screenshot-4.png +0 -0
  49. assets/screenshot-5.png +0 -0
  50. assets/screenshot-6.png +0 -0
  51. assets/screenshot-7.png +0 -0
  52. class-google-calendar-events-admin.php +0 -374
  53. class-google-calendar-events.php +0 -256
  54. css/admin-shortcode.css +0 -35
  55. css/admin.css +0 -67
  56. css/gce-style.css +0 -217
  57. css/jquery.qtip.css +0 -589
  58. css/jquery.qtip.min.css +0 -3
  59. gce-requirements.php +0 -135
  60. google-calendar-events.php +49 -72
  61. includes/abstracts/admin-page.php +195 -0
  62. includes/abstracts/calendar-view.php +104 -0
  63. includes/abstracts/calendar.php +781 -0
  64. includes/abstracts/feed.php +262 -0
  65. includes/abstracts/field.php +354 -0
  66. includes/abstracts/meta-box.php +45 -0
  67. includes/abstracts/widget.php +68 -0
  68. includes/admin/admin-functions.php +0 -128
  69. includes/admin/admin-notice.php +0 -37
  70. includes/admin/ajax.php +172 -0
  71. includes/admin/assets.php +162 -0
  72. includes/admin/bulk-actions.php +227 -0
  73. includes/admin/fields/checkbox.php +104 -0
  74. includes/admin/fields/date-picker.php +107 -0
  75. includes/admin/fields/datetime-format.php +379 -0
  76. includes/admin/fields/license.php +97 -0
  77. includes/admin/fields/nonce.php +42 -0
  78. includes/admin/fields/radio.php +98 -0
  79. includes/admin/fields/select.php +123 -0
  80. includes/admin/fields/standard.php +76 -0
  81. includes/admin/fields/textarea.php +70 -0
  82. includes/admin/menus.php +262 -0
  83. includes/admin/meta-boxes.php +183 -0
  84. includes/admin/metaboxes/attach-calendar.php +84 -0
  85. includes/admin/metaboxes/get-shortcode.php +45 -0
  86. includes/admin/metaboxes/newsletter.php +45 -0
  87. includes/admin/metaboxes/settings.php +905 -0
  88. includes/admin/notice.php +189 -0
  89. includes/admin/notices.php +200 -0
  90. includes/admin/pages.php +270 -0
  91. includes/admin/pages/add-ons.php +81 -0
  92. includes/admin/pages/advanced.php +129 -0
  93. includes/admin/pages/calendars.php +182 -0
  94. includes/admin/pages/feeds.php +123 -0
  95. includes/admin/pages/licenses.php +131 -0
  96. includes/admin/pages/system-status.php +709 -0
  97. includes/admin/post-types.php +354 -0
  98. includes/admin/updater.php +371 -0
  99. includes/admin/upgrade.php +0 -472
  100. includes/admin/welcome.php +384 -0
  101. includes/ajax.php +55 -0
  102. includes/assets.php +381 -0
  103. includes/autoload.php +52 -0
  104. includes/calendars/admin/default-calendar-admin.php +353 -0
  105. includes/calendars/default-calendar.php +247 -0
  106. includes/calendars/views/default-calendar-grid.php +594 -0
  107. includes/calendars/views/default-calendar-list.php +636 -0
  108. includes/class-gce-display.php +0 -413
  109. includes/class-gce-event.php +0 -598
  110. includes/class-gce-feed.php +0 -344
  111. includes/events/event-builder.php +952 -0
  112. includes/events/event.php +603 -0
  113. includes/events/events.php +605 -0
  114. includes/feeds/admin/google-admin.php +355 -0
  115. includes/feeds/admin/grouped-calendars-admin.php +189 -0
  116. includes/feeds/google.php +426 -0
  117. includes/feeds/grouped-calendars.php +208 -0
  118. includes/functions/admin.php +450 -0
  119. includes/functions/deprecated.php +50 -0
  120. includes/functions/shared.php +409 -0
  121. includes/gce-feed-cpt.php +0 -473
  122. includes/installation.php +172 -0
  123. includes/main.php +265 -0
  124. includes/misc-functions.php +0 -377
  125. includes/objects.php +322 -0
  126. includes/php-calendar.php +0 -110
  127. includes/post-types.php +194 -0
assets/banner-772x250.jpg DELETED
Binary file
assets/css/admin-activation.css ADDED
File without changes
assets/css/admin-activation.min.css ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ /*! Simple Calendar - 3.0.0
2
+ * https://simplecalendar.io
3
+ * Copyright (c) Moonstone Media 2015
4
+ * Licensed GPLv2+ */
5
+
assets/css/admin-add-calendar.css ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body #_simcal_attach_calendar_id,
2
+ body .simcal-calendar-widget-settings select {
3
+ width: 100%; }
4
+
5
+ body .select2-container--default .select2-selection--single {
6
+ border-color: #ccc;
7
+ border-radius: 0; }
8
+
9
+ body .select2-container {
10
+ z-index: 1000000; }
11
+
12
+ body.post-type-gce_feed #gce-insert-shortcode-button {
13
+ display: none; }
14
+
15
+ .wp-media-buttons a.add_calendar span.wp-media-buttons-icon {
16
+ background: none; }
17
+
18
+ .gce-insert-shortcode-panel {
19
+ max-width: 90%;
20
+ position: relative; }
21
+ .gce-insert-shortcode-panel select {
22
+ display: block;
23
+ max-width: 470px;
24
+ width: 100%; }
25
+
26
+ body .gce-insert-shortcode-modal {
27
+ height: 220px !important;
28
+ margin-left: -250px !important;
29
+ width: 500px !important; }
30
+
31
+ body .gce-insert-shortcode-modal-title {
32
+ background: #fff !important;
33
+ border-bottom: none !important;
34
+ height: 0 !important; }
assets/css/admin-add-calendar.min.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /*! Simple Calendar - 3.0.0
2
+ * https://simplecalendar.io
3
+ * Copyright (c) Moonstone Media 2015
4
+ * Licensed GPLv2+ */
5
+
6
+ body #_simcal_attach_calendar_id,body .simcal-calendar-widget-settings select{width:100%}body .select2-container--default .select2-selection--single{border-color:#ccc;border-radius:0}body .select2-container{z-index:1000000}body.post-type-gce_feed #gce-insert-shortcode-button{display:none}.wp-media-buttons a.add_calendar span.wp-media-buttons-icon{background:0 0}.gce-insert-shortcode-panel{max-width:90%;position:relative}.gce-insert-shortcode-panel select{display:block;max-width:470px;width:100%}body .gce-insert-shortcode-modal{height:220px!important;margin-left:-250px!important;width:500px!important}body .gce-insert-shortcode-modal-title{background:#fff!important;border-bottom:none!important;height:0!important}
assets/css/admin.css ADDED
@@ -0,0 +1,563 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family: 'simple-calendar';
3
+ src: url("../fonts/simple-calendar.eot?43976014");
4
+ src: url("../fonts/simple-calendar.eot?43976014#iefix") format("embedded-opentype"), url("../fonts/simple-calendar.woff?43976014") format("woff"), url("../fonts/simple-calendar.ttf?43976014") format("truetype"), url("../fonts/simple-calendar.svg?43976014#simple-calendar") format("svg");
5
+ font-weight: normal;
6
+ font-style: normal; }
7
+
8
+ [class^="simcal-icon-"]:before,
9
+ [class*=" simcal-icon-"]:before {
10
+ display: inline-block;
11
+ font-family: "simple-calendar";
12
+ font-style: normal;
13
+ font-weight: normal;
14
+ margin-right: .2em;
15
+ speak: none;
16
+ text-decoration: inherit;
17
+ text-align: center;
18
+ width: 1em;
19
+ font-variant: normal;
20
+ text-transform: none;
21
+ margin-left: .2em;
22
+ -webkit-font-smoothing: antialiased;
23
+ -moz-osx-font-smoothing: grayscale; }
24
+
25
+ .simcal-icon-spin {
26
+ -webkit-animation: spin 2s infinite linear;
27
+ animation: spin 2s infinite linear;
28
+ display: inline-block; }
29
+
30
+ @-webkit-keyframes spin {
31
+ 0% {
32
+ -webkit-transform: rotate(0deg);
33
+ transform: rotate(0deg); }
34
+ 100% {
35
+ -webkit-transform: rotate(359deg);
36
+ transform: rotate(359deg); } }
37
+
38
+ @keyframes spin {
39
+ 0% {
40
+ -webkit-transform: rotate(0deg);
41
+ transform: rotate(0deg); }
42
+ 100% {
43
+ -webkit-transform: rotate(359deg);
44
+ transform: rotate(359deg); } }
45
+
46
+ .simcal-icon-animate:before {
47
+ transition: all .2s ease-in-out; }
48
+
49
+ .simcal-icon-rotate-180:before {
50
+ -webkit-transform: rotate(180deg);
51
+ transform: rotate(180deg); }
52
+
53
+ .simcal-icon-calendar-empty:before {
54
+ content: '\e800'; }
55
+
56
+ .simcal-icon-calendar:before {
57
+ content: '\e801'; }
58
+
59
+ .simcal-icon-calendar-logo:before {
60
+ content: '\e802'; }
61
+
62
+ .simcal-icon-settings:before {
63
+ content: '\e804'; }
64
+
65
+ .simcal-icon-toggles:before {
66
+ content: '\e805'; }
67
+
68
+ .simcal-icon-list:before {
69
+ content: '\e806'; }
70
+
71
+ .simcal-icon-event:before {
72
+ content: '\e807'; }
73
+
74
+ .simcal-icon-help:before {
75
+ content: '\e808'; }
76
+
77
+ .simcal-icon-panel:before {
78
+ content: '\e80a'; }
79
+
80
+ .simcal-icon-grid:before {
81
+ content: '\e80b'; }
82
+
83
+ .simcal-icon-google:before {
84
+ content: '\e80c'; }
85
+
86
+ .simcal-icon-docs:before {
87
+ content: '\e80f'; }
88
+
89
+ .simcal-icon-hourglass:before {
90
+ content: '\e811'; }
91
+
92
+ .simcal-icon-globe:before {
93
+ content: '\e812'; }
94
+
95
+ .simcal-icon-timezones:before {
96
+ content: '\e813'; }
97
+
98
+ .simcal-icon-warning:before {
99
+ content: '\e815'; }
100
+
101
+ .simcal-icon-wordpress:before {
102
+ content: '\e814'; }
103
+
104
+ .simcal-icon-up:before {
105
+ content: '\e80e'; }
106
+
107
+ .simcal-icon-right:before {
108
+ content: '\e809'; }
109
+
110
+ .simcal-icon-down:before {
111
+ content: '\e80d'; }
112
+
113
+ .simcal-icon-left:before {
114
+ content: '\e803'; }
115
+
116
+ .simcal-icon-spinner:before {
117
+ content: '\e810'; }
118
+
119
+ .simcal-help-tip {
120
+ color: rgba(0, 0, 0, 0.46);
121
+ cursor: help; }
122
+
123
+ #tiptip_holder {
124
+ display: none;
125
+ left: 0;
126
+ position: absolute;
127
+ top: 0;
128
+ z-index: 99999; }
129
+
130
+ #tiptip_holder.tip_top {
131
+ padding-bottom: 5px; }
132
+
133
+ #tiptip_holder.tip_bottom {
134
+ padding-top: 5px; }
135
+
136
+ #tiptip_holder.tip_right {
137
+ padding-left: 5px; }
138
+
139
+ #tiptip_holder.tip_left {
140
+ padding-right: 5px; }
141
+
142
+ #tiptip_content {
143
+ background-color: rgba(25, 25, 25, 0.92);
144
+ border: 1px solid rgba(255, 255, 255, 0.25);
145
+ border-radius: 3px;
146
+ color: #fff;
147
+ font-size: 11px;
148
+ padding: 4px 8px; }
149
+
150
+ #tiptip_arrow, #tiptip_arrow_inner {
151
+ border-color: transparent;
152
+ border-style: solid;
153
+ border-width: 6px;
154
+ height: 0;
155
+ position: absolute;
156
+ width: 0; }
157
+
158
+ #tiptip_holder.tip_top #tiptip_arrow_inner {
159
+ border-top-color: rgba(25, 25, 25, 0.92);
160
+ margin-left: -6px;
161
+ margin-top: -7px; }
162
+
163
+ #tiptip_holder.tip_bottom #tiptip_arrow_inner {
164
+ border-bottom-color: rgba(25, 25, 25, 0.92);
165
+ margin-left: -6px;
166
+ margin-top: -5px; }
167
+
168
+ #tiptip_holder.tip_right #tiptip_arrow_inner {
169
+ border-right-color: rgba(25, 25, 25, 0.92);
170
+ margin-left: -5px;
171
+ margin-top: -6px; }
172
+
173
+ #tiptip_holder.tip_left #tiptip_arrow_inner {
174
+ border-left-color: rgba(25, 25, 25, 0.92);
175
+ margin-left: -7px;
176
+ margin-top: -6px; }
177
+
178
+ @media screen and (-webkit-min-device-pixel-ratio: 0) {
179
+ #tiptip_content {
180
+ background-color: rgba(45, 45, 45, 0.88);
181
+ padding: 4px 8px 5px 8px; }
182
+ #tiptip_holder.tip_bottom #tiptip_arrow_inner {
183
+ border-bottom-color: rgba(45, 45, 45, 0.88); }
184
+ #tiptip_holder.tip_top #tiptip_arrow_inner {
185
+ border-top-color: rgba(20, 20, 20, 0.92); } }
186
+
187
+ .simcal-dismiss-notice {
188
+ color: #ccc;
189
+ float: right;
190
+ margin-top: 9px;
191
+ text-decoration: none; }
192
+ .simcal-dismiss-notice:hover {
193
+ color: #aaa; }
194
+ .simcal-dismiss-notice:focus, .simcal-dismiss-notice:active {
195
+ outline: 0; }
196
+
197
+ .wp-admin .simcal-field-inline,
198
+ .wp-admin .simcal-field-inline > input,
199
+ .wp-admin .simcal-field-inline > select {
200
+ display: inline-block;
201
+ vertical-align: baseline; }
202
+
203
+ body .simcal-field-tiny,
204
+ body .simcal-panel-field .simcal-field-tiny {
205
+ max-width: 60px; }
206
+
207
+ body .simcal-field-small,
208
+ body .simcal-panel-field .simcal-field-small {
209
+ max-width: 100px; }
210
+
211
+ body .simcal-field-textarea,
212
+ body .simcal-field-select-enhanced {
213
+ max-width: 500px;
214
+ width: 100%; }
215
+
216
+ .select2-container {
217
+ z-index: 999999; }
218
+
219
+ body .simcal-field-textarea {
220
+ min-height: 64px;
221
+ vertical-align: top; }
222
+
223
+ .simcal-field-radios > i {
224
+ margin-left: 5px;
225
+ vertical-align: middle; }
226
+
227
+ .simcal-field-radios-inline {
228
+ display: inline-block;
229
+ margin: 0; }
230
+ .simcal-field-radios-inline > li {
231
+ display: inline-block;
232
+ margin-right: 10px; }
233
+ .simcal-field-radios-inline > li:last-child {
234
+ margin-right: 0; }
235
+
236
+ .simcal-field-bool > input {
237
+ line-height: 2.4; }
238
+
239
+ .simcal-field-date-picker > input[type="text"] {
240
+ max-width: 120px; }
241
+
242
+ #ui-datepicker-div.simcal-date-picker {
243
+ background-color: #fff;
244
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
245
+ padding: 8px 10px; }
246
+ #ui-datepicker-div.simcal-date-picker .ui-datepicker-header > a {
247
+ display: inline-block;
248
+ cursor: pointer;
249
+ margin-top: 7px; }
250
+ #ui-datepicker-div.simcal-date-picker .ui-datepicker-header > a:first-of-type {
251
+ float: left; }
252
+ #ui-datepicker-div.simcal-date-picker .ui-datepicker-header > a:last-of-type {
253
+ float: right; }
254
+ #ui-datepicker-div.simcal-date-picker .ui-datepicker-header .ui-datepicker-title {
255
+ margin: 0 10% 4px; }
256
+ #ui-datepicker-div.simcal-date-picker .ui-datepicker-header .ui-datepicker-title > select {
257
+ width: 48.5%; }
258
+ #ui-datepicker-div.simcal-date-picker .ui-datepicker-today {
259
+ background-color: #eee; }
260
+ #ui-datepicker-div.simcal-date-picker > table tr td,
261
+ #ui-datepicker-div.simcal-date-picker > table tr th {
262
+ padding: 2px 4px;
263
+ text-align: center; }
264
+ #ui-datepicker-div.simcal-date-picker > table tr td a,
265
+ #ui-datepicker-div.simcal-date-picker > table tr th a {
266
+ text-decoration: none; }
267
+
268
+ .simcal-field-datetime-format {
269
+ padding: 10px 0 5px; }
270
+ .simcal-field-datetime-format > div {
271
+ background-color: rgba(0, 0, 0, 0.03);
272
+ border: 1px dashed rgba(0, 0, 0, 0.18);
273
+ border-radius: 3px;
274
+ cursor: move;
275
+ display: inline-block;
276
+ float: left;
277
+ margin: 0 10px 15px 0;
278
+ padding: 10px; }
279
+ .simcal-field-datetime-format select {
280
+ display: block; }
281
+ .simcal-field-datetime-format > span {
282
+ clear: both;
283
+ display: block; }
284
+ .simcal-field-datetime-format .ui-sortable-helper {
285
+ box-shadow: -2px 4px 8px rgba(0, 0, 0, 0.12);
286
+ -webkit-transform: rotate(2deg);
287
+ transform: rotate(2deg); }
288
+
289
+ #simcal-settings-page .simcal-wide-text {
290
+ width: 40em; }
291
+
292
+ #simcal-settings-page .select2 {
293
+ max-width: 500px; }
294
+
295
+ #simcal-settings-page .select2-search__field {
296
+ border: 0 !important;
297
+ box-shadow: none !important;
298
+ margin: 0; }
299
+ #simcal-settings-page .select2-search__field:focus {
300
+ border: 0 !important;
301
+ box-shadow: none !important; }
302
+
303
+ #simcal-settings-page .select2-selection {
304
+ border: 1px solid #ddd;
305
+ border-radius: 0; }
306
+
307
+ #simcal-settings-page .select2-container--open .select2-selection {
308
+ border-color: #5b9dd9;
309
+ box-shadow: 0 0 2px rgba(30, 140, 190, 0.8); }
310
+
311
+ .about-wrap .simcal-badge {
312
+ position: absolute;
313
+ top: 0;
314
+ right: 0; }
315
+
316
+ .simcal-badge {
317
+ background-image: url("../images/welcome/icon-185x185.png");
318
+ height: 185px;
319
+ width: 185px; }
320
+
321
+ #simcal-welcome .whats-new-wrap img {
322
+ margin: 1em 2em 1em 0;
323
+ border: 1px solid #d6d6d6;
324
+ box-sizing: border-box;
325
+ vertical-align: top; }
326
+
327
+ #simcal-system-status-report textarea {
328
+ font-family: monospace;
329
+ min-height: 200px;
330
+ padding: 5px 20px;
331
+ white-space: pre;
332
+ width: 100%; }
333
+
334
+ .simcal-system-status-report-panel {
335
+ margin: 20px 0; }
336
+ .simcal-system-status-report-panel thead th {
337
+ font-weight: bold; }
338
+ .simcal-system-status-report-panel tbody td,
339
+ .simcal-system-status-report-panel thead th {
340
+ font-size: 14px;
341
+ vertical-align: top; }
342
+ .simcal-system-status-report-panel tbody .tooltip {
343
+ text-align: center;
344
+ width: 20px; }
345
+ .simcal-system-status-report-panel tbody .label {
346
+ width: 30%; }
347
+ .simcal-system-status-report-panel tbody tr:nth-child(odd) td {
348
+ background-color: rgba(0, 0, 0, 0.02); }
349
+ .simcal-system-status-report-panel dl {
350
+ margin: 0;
351
+ padding: 0; }
352
+ .simcal-system-status-report-panel dt {
353
+ float: left;
354
+ font-style: italic;
355
+ margin: 0 4px 0 0; }
356
+ .simcal-system-status-report-panel dd {
357
+ margin: 0; }
358
+ .simcal-system-status-report-panel mark {
359
+ background-color: transparent;
360
+ font-weight: bold; }
361
+ .simcal-system-status-report-panel mark.ok {
362
+ color: green; }
363
+ .simcal-system-status-report-panel mark.error {
364
+ color: red; }
365
+
366
+ #simcal-reset-licenses {
367
+ color: red; }
368
+
369
+ .ui-datepicker {
370
+ left: -9999px;
371
+ position: absolute; }
372
+
373
+ #simcal-clear-cache {
374
+ float: right;
375
+ margin: 0 10px 10px; }
376
+
377
+ #simcal-get-shortcode .inside {
378
+ padding: 5px 15px 10px;
379
+ text-align: center; }
380
+
381
+ #simcal-get-shortcode input {
382
+ width: 100%; }
383
+
384
+ #simcal-newsletter {
385
+ background-color: #fcf8e3; }
386
+ #simcal-newsletter input {
387
+ width: 100%; }
388
+
389
+ #simcal-calendar-settings {
390
+ display: none; }
391
+ #simcal-calendar-settings .hndle {
392
+ line-height: 2;
393
+ padding: 10px; }
394
+ #simcal-calendar-settings .inside {
395
+ margin: 0;
396
+ padding: 0; }
397
+ #simcal-calendar-settings .select2-container {
398
+ border-radius: 0;
399
+ max-width: 500px; }
400
+ #simcal-calendar-settings .select2-search__field {
401
+ border: 0 !important;
402
+ box-shadow: none !important;
403
+ margin: 0; }
404
+ #simcal-calendar-settings .select2-search__field:focus {
405
+ border: 0 !important;
406
+ box-shadow: none !important; }
407
+ #simcal-calendar-settings .select2-selection {
408
+ border: 1px solid #ddd;
409
+ border-radius: 0; }
410
+ #simcal-calendar-settings .select2-container--open .select2-selection {
411
+ border-color: #5b9dd9;
412
+ box-shadow: 0 0 2px rgba(30, 140, 190, 0.8); }
413
+ #simcal-calendar-settings .select2-selection__choice {
414
+ margin-bottom: 0; }
415
+ #simcal-calendar-settings .wp-picker-container {
416
+ vertical-align: inherit; }
417
+
418
+ .simcal-box-handle {
419
+ display: inline-block;
420
+ line-height: 1;
421
+ margin-left: 8px; }
422
+ .simcal-box-handle:before {
423
+ content: '\2014';
424
+ display: inline-block;
425
+ font-weight: normal; }
426
+ .simcal-box-handle label {
427
+ font-size: 12px;
428
+ font-weight: normal;
429
+ margin-right: 10px;
430
+ vertical-align: baseline; }
431
+ .simcal-box-handle select {
432
+ font-weight: bold;
433
+ margin: -3px 0 0 .5em;
434
+ vertical-align: middle; }
435
+
436
+ .simcal-tabs {
437
+ background: #fafafa;
438
+ border-right: 1px solid #eee;
439
+ box-sizing: border-box;
440
+ float: left;
441
+ line-height: 1em;
442
+ margin: 0;
443
+ padding: 0 0 10px;
444
+ position: relative;
445
+ width: 20%; }
446
+ .simcal-tabs a {
447
+ border-bottom: 1px solid #eee;
448
+ display: block;
449
+ line-height: 20px !important;
450
+ margin: 0;
451
+ padding: 10px;
452
+ text-decoration: none; }
453
+ .simcal-tabs a:focus {
454
+ box-shadow: none; }
455
+ .simcal-tabs li {
456
+ display: block;
457
+ margin: 0;
458
+ padding: 0; }
459
+ .simcal-tabs li.active a {
460
+ background-color: #eee;
461
+ color: #555;
462
+ position: relative; }
463
+ .simcal-tabs:after {
464
+ background-color: #fafafa;
465
+ border-right: 1px solid #eee;
466
+ bottom: -9999em;
467
+ content: "";
468
+ display: block;
469
+ height: 9999em;
470
+ left: 0;
471
+ position: absolute;
472
+ width: 100%; }
473
+
474
+ .simcal-panels-wrap {
475
+ background: #fff;
476
+ overflow: hidden; }
477
+
478
+ .simcal-panel {
479
+ margin-left: 20%;
480
+ max-width: 80%; }
481
+ .simcal-panel > table {
482
+ padding-bottom: 11px;
483
+ width: 100%; }
484
+ .simcal-panel > table thead th {
485
+ border-bottom: 1px solid #eee;
486
+ font-size: 14px;
487
+ font-weight: bold;
488
+ padding: 12px 0 15px 10px;
489
+ text-align: left; }
490
+ .simcal-panel > table > tbody tr:first-child td,
491
+ .simcal-panel > table > tbody tr:first-child th {
492
+ padding-top: 14px; }
493
+ .simcal-panel > table > tbody tr:last-child td,
494
+ .simcal-panel > table > tbody tr:last-child th {
495
+ border-bottom: 1px solid #eee;
496
+ padding-bottom: 14px; }
497
+ .simcal-panel > table > tbody:last-of-type tr:last-child td,
498
+ .simcal-panel > table > tbody:last-of-type tr:last-child th {
499
+ border-bottom: 0; }
500
+
501
+ .simcal-panel-field > td {
502
+ width: 82%; }
503
+
504
+ .simcal-panel-field > th {
505
+ font-weight: normal;
506
+ min-width: 100px;
507
+ width: 15%; }
508
+
509
+ .simcal-panel-field > td,
510
+ .simcal-panel-field > th {
511
+ padding: 9px 3% 9px 10px;
512
+ text-align: left;
513
+ vertical-align: text-top; }
514
+
515
+ .simcal-panel-field .simcal-field-text {
516
+ max-width: 500px;
517
+ width: 90%; }
518
+
519
+ @media screen and (max-width: 1023px) {
520
+ .simcal-box-handle {
521
+ display: block;
522
+ line-height: 2;
523
+ margin: 16px 0 0; }
524
+ .simcal-box-handle:before {
525
+ display: none; }
526
+ .simcal-box-handle label {
527
+ display: table;
528
+ margin-bottom: 16px;
529
+ width: 100%; }
530
+ .simcal-box-handle label > span {
531
+ display: table-cell;
532
+ width: 20%; }
533
+ .simcal-box-handle label > select {
534
+ display: table-cell;
535
+ width: 80%; }
536
+ .simcal-box-handle .simcal-tabs {
537
+ width: 16%; }
538
+ .simcal-box-handle .simcal-tabs i {
539
+ font-size: 18px;
540
+ line-height: 1; }
541
+ .simcal-box-handle .simcal-tabs span {
542
+ display: none; }
543
+ .simcal-box-handle .simcal-panel {
544
+ margin-left: 16%;
545
+ max-width: 84%; }
546
+ .simcal-box-handle .simcal-panel-section {
547
+ display: block;
548
+ overflow: hidden; }
549
+ .simcal-box-handle .simcal-panel-field th {
550
+ display: block;
551
+ width: 100%; }
552
+ .simcal-box-handle .simcal-panel-field td {
553
+ display: inline-block;
554
+ width: 100%; }
555
+ .simcal-box-handle .simcal-panel-field select {
556
+ max-width: 86%; } }
557
+
558
+ input.simcal-shortcode {
559
+ border: dashed 1px rgba(0, 0, 0, 0.3);
560
+ color: rgba(0, 0, 0, 0.58);
561
+ font-family: monospace;
562
+ font-weight: bold;
563
+ text-align: center; }
assets/css/admin.min.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /*! Simple Calendar - 3.0.0
2
+ * https://simplecalendar.io
3
+ * Copyright (c) Moonstone Media 2015
4
+ * Licensed GPLv2+ */
5
+
6
+ @font-face{font-family:simple-calendar;src:url(../fonts/simple-calendar.eot?43976014);src:url(../fonts/simple-calendar.eot?43976014#iefix) format("embedded-opentype"),url(../fonts/simple-calendar.woff?43976014) format("woff"),url(../fonts/simple-calendar.ttf?43976014) format("truetype"),url(../fonts/simple-calendar.svg?43976014#simple-calendar) format("svg");font-weight:400;font-style:normal}[class*=" simcal-icon-"]:before,[class^=simcal-icon-]:before{display:inline-block;font-family:simple-calendar;font-style:normal;font-weight:400;margin-right:.2em;speak:none;text-decoration:inherit;text-align:center;width:1em;font-variant:normal;text-transform:none;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#ui-datepicker-div.simcal-date-picker>table tr td a,#ui-datepicker-div.simcal-date-picker>table tr th a,.simcal-tabs a{text-decoration:none}.simcal-icon-spin{-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear;display:inline-block}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.simcal-icon-animate:before{transition:all .2s ease-in-out}.simcal-icon-rotate-180:before{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.simcal-icon-calendar-empty:before{content:'\e800'}.simcal-icon-calendar:before{content:'\e801'}.simcal-icon-calendar-logo:before{content:'\e802'}.simcal-icon-settings:before{content:'\e804'}.simcal-icon-toggles:before{content:'\e805'}.simcal-icon-list:before{content:'\e806'}.simcal-icon-event:before{content:'\e807'}.simcal-icon-help:before{content:'\e808'}.simcal-icon-panel:before{content:'\e80a'}.simcal-icon-grid:before{content:'\e80b'}.simcal-icon-google:before{content:'\e80c'}.simcal-icon-docs:before{content:'\e80f'}.simcal-icon-hourglass:before{content:'\e811'}.simcal-icon-globe:before{content:'\e812'}.simcal-icon-timezones:before{content:'\e813'}.simcal-icon-warning:before{content:'\e815'}.simcal-icon-wordpress:before{content:'\e814'}.simcal-icon-up:before{content:'\e80e'}.simcal-icon-right:before{content:'\e809'}.simcal-icon-down:before{content:'\e80d'}.simcal-icon-left:before{content:'\e803'}.simcal-icon-spinner:before{content:'\e810'}.simcal-help-tip{color:rgba(0,0,0,.46);cursor:help}#tiptip_holder{display:none;left:0;position:absolute;top:0;z-index:99999}#tiptip_holder.tip_top{padding-bottom:5px}#tiptip_holder.tip_bottom{padding-top:5px}#tiptip_holder.tip_right{padding-left:5px}#tiptip_holder.tip_left{padding-right:5px}#tiptip_content{background-color:rgba(25,25,25,.92);border:1px solid rgba(255,255,255,.25);border-radius:3px;color:#fff;font-size:11px;padding:4px 8px}#tiptip_arrow,#tiptip_arrow_inner{border-color:transparent;border-style:solid;border-width:6px;height:0;position:absolute;width:0}#tiptip_holder.tip_top #tiptip_arrow_inner{border-top-color:rgba(25,25,25,.92);margin-left:-6px;margin-top:-7px}#tiptip_holder.tip_bottom #tiptip_arrow_inner{border-bottom-color:rgba(25,25,25,.92);margin-left:-6px;margin-top:-5px}#tiptip_holder.tip_right #tiptip_arrow_inner{border-right-color:rgba(25,25,25,.92);margin-left:-5px;margin-top:-6px}#tiptip_holder.tip_left #tiptip_arrow_inner{border-left-color:rgba(25,25,25,.92);margin-left:-7px;margin-top:-6px}@media screen and (-webkit-min-device-pixel-ratio:0){#tiptip_content{background-color:rgba(45,45,45,.88);padding:4px 8px 5px}#tiptip_holder.tip_bottom #tiptip_arrow_inner{border-bottom-color:rgba(45,45,45,.88)}#tiptip_holder.tip_top #tiptip_arrow_inner{border-top-color:rgba(20,20,20,.92)}}.simcal-dismiss-notice{color:#ccc;float:right;margin-top:9px;text-decoration:none}.simcal-dismiss-notice:hover{color:#aaa}.simcal-dismiss-notice:active,.simcal-dismiss-notice:focus{outline:0}.wp-admin .simcal-field-inline,.wp-admin .simcal-field-inline>input,.wp-admin .simcal-field-inline>select{display:inline-block;vertical-align:baseline}body .simcal-field-tiny,body .simcal-panel-field .simcal-field-tiny{max-width:60px}body .simcal-field-small,body .simcal-panel-field .simcal-field-small{max-width:100px}body .simcal-field-select-enhanced,body .simcal-field-textarea{max-width:500px;width:100%}.select2-container{z-index:999999}body .simcal-field-textarea{min-height:64px;vertical-align:top}.simcal-field-radios>i{margin-left:5px;vertical-align:middle}.simcal-field-radios-inline{display:inline-block;margin:0}.simcal-field-radios-inline>li{display:inline-block;margin-right:10px}.simcal-field-radios-inline>li:last-child{margin-right:0}.simcal-field-bool>input{line-height:2.4}.simcal-field-date-picker>input[type=text]{max-width:120px}#ui-datepicker-div.simcal-date-picker{background-color:#fff;box-shadow:0 2px 10px rgba(0,0,0,.1);padding:8px 10px}#ui-datepicker-div.simcal-date-picker .ui-datepicker-header>a{display:inline-block;cursor:pointer;margin-top:7px}#ui-datepicker-div.simcal-date-picker .ui-datepicker-header>a:first-of-type{float:left}#ui-datepicker-div.simcal-date-picker .ui-datepicker-header>a:last-of-type{float:right}#ui-datepicker-div.simcal-date-picker .ui-datepicker-header .ui-datepicker-title{margin:0 10% 4px}#ui-datepicker-div.simcal-date-picker .ui-datepicker-header .ui-datepicker-title>select{width:48.5%}#ui-datepicker-div.simcal-date-picker .ui-datepicker-today{background-color:#eee}#ui-datepicker-div.simcal-date-picker>table tr td,#ui-datepicker-div.simcal-date-picker>table tr th{padding:2px 4px;text-align:center}.simcal-field-datetime-format{padding:10px 0 5px}.simcal-field-datetime-format>div{background-color:rgba(0,0,0,.03);border:1px dashed rgba(0,0,0,.18);border-radius:3px;cursor:move;display:inline-block;float:left;margin:0 10px 15px 0;padding:10px}.simcal-field-datetime-format select{display:block}.simcal-field-datetime-format>span{clear:both;display:block}.simcal-field-datetime-format .ui-sortable-helper{box-shadow:-2px 4px 8px rgba(0,0,0,.12);-webkit-transform:rotate(2deg);transform:rotate(2deg)}#simcal-settings-page .select2-search__field,#simcal-settings-page .select2-search__field:focus{border:0!important;box-shadow:none!important}#simcal-settings-page .simcal-wide-text{width:40em}#simcal-settings-page .select2{max-width:500px}#simcal-settings-page .select2-search__field{margin:0}#simcal-settings-page .select2-selection{border:1px solid #ddd;border-radius:0}#simcal-settings-page .select2-container--open .select2-selection{border-color:#5b9dd9;box-shadow:0 0 2px rgba(30,140,190,.8)}.about-wrap .simcal-badge{position:absolute;top:0;right:0}.simcal-badge{background-image:url(../images/welcome/icon-185x185.png);height:185px;width:185px}#simcal-welcome .whats-new-wrap img{margin:1em 2em 1em 0;border:1px solid #d6d6d6;box-sizing:border-box;vertical-align:top}#simcal-system-status-report textarea{font-family:monospace;min-height:200px;padding:5px 20px;white-space:pre;width:100%}.simcal-system-status-report-panel{margin:20px 0}.simcal-system-status-report-panel thead th{font-weight:700}.simcal-system-status-report-panel tbody td,.simcal-system-status-report-panel thead th{font-size:14px;vertical-align:top}.simcal-system-status-report-panel tbody .tooltip{text-align:center;width:20px}.simcal-system-status-report-panel tbody .label{width:30%}.simcal-system-status-report-panel tbody tr:nth-child(odd) td{background-color:rgba(0,0,0,.02)}.simcal-system-status-report-panel dl{margin:0;padding:0}.simcal-system-status-report-panel dt{float:left;font-style:italic;margin:0 4px 0 0}.simcal-system-status-report-panel dd{margin:0}.simcal-system-status-report-panel mark{background-color:transparent;font-weight:700}.simcal-system-status-report-panel mark.ok{color:green}#simcal-reset-licenses,.simcal-system-status-report-panel mark.error{color:red}.ui-datepicker{left:-9999px;position:absolute}#simcal-clear-cache{float:right;margin:0 10px 10px}#simcal-get-shortcode .inside{padding:5px 15px 10px;text-align:center}#simcal-get-shortcode input,#simcal-newsletter input{width:100%}#simcal-newsletter{background-color:#fcf8e3}#simcal-calendar-settings{display:none}#simcal-calendar-settings .hndle{line-height:2;padding:10px}#simcal-calendar-settings .inside{margin:0;padding:0}#simcal-calendar-settings .select2-container{border-radius:0;max-width:500px}#simcal-calendar-settings .select2-search__field{border:0!important;box-shadow:none!important;margin:0}#simcal-calendar-settings .select2-search__field:focus{border:0!important;box-shadow:none!important}#simcal-calendar-settings .select2-selection{border:1px solid #ddd;border-radius:0}#simcal-calendar-settings .select2-container--open .select2-selection{border-color:#5b9dd9;box-shadow:0 0 2px rgba(30,140,190,.8)}#simcal-calendar-settings .select2-selection__choice{margin-bottom:0}#simcal-calendar-settings .wp-picker-container{vertical-align:inherit}.simcal-box-handle{display:inline-block;line-height:1;margin-left:8px}.simcal-box-handle:before{content:'\2014';display:inline-block;font-weight:400}.simcal-box-handle label{font-size:12px;font-weight:400;margin-right:10px;vertical-align:baseline}.simcal-box-handle select{font-weight:700;margin:-3px 0 0 .5em;vertical-align:middle}.simcal-tabs{background:#fafafa;border-right:1px solid #eee;box-sizing:border-box;float:left;line-height:1em;margin:0;padding:0 0 10px;position:relative;width:20%}.simcal-tabs a{border-bottom:1px solid #eee;display:block;line-height:20px!important;margin:0;padding:10px}.simcal-tabs a:focus{box-shadow:none}.simcal-tabs li{display:block;margin:0;padding:0}.simcal-tabs li.active a{background-color:#eee;color:#555;position:relative}.simcal-tabs:after{background-color:#fafafa;border-right:1px solid #eee;bottom:-9999em;content:"";display:block;height:9999em;left:0;position:absolute;width:100%}.simcal-panels-wrap{background:#fff;overflow:hidden}.simcal-panel{margin-left:20%;max-width:80%}.simcal-panel>table{padding-bottom:11px;width:100%}.simcal-panel>table thead th{border-bottom:1px solid #eee;font-size:14px;font-weight:700;padding:12px 0 15px 10px;text-align:left}.simcal-panel>table>tbody tr:first-child td,.simcal-panel>table>tbody tr:first-child th{padding-top:14px}.simcal-panel>table>tbody tr:last-child td,.simcal-panel>table>tbody tr:last-child th{border-bottom:1px solid #eee;padding-bottom:14px}.simcal-panel>table>tbody:last-of-type tr:last-child td,.simcal-panel>table>tbody:last-of-type tr:last-child th{border-bottom:0}.simcal-panel-field>td{width:82%}.simcal-panel-field>th{font-weight:400;min-width:100px;width:15%}.simcal-panel-field>td,.simcal-panel-field>th{padding:9px 3% 9px 10px;text-align:left;vertical-align:text-top}.simcal-panel-field .simcal-field-text{max-width:500px;width:90%}@media screen and (max-width:1023px){.simcal-box-handle{display:block;line-height:2;margin:16px 0 0}.simcal-box-handle:before{display:none}.simcal-box-handle label{display:table;margin-bottom:16px;width:100%}.simcal-box-handle label>span{display:table-cell;width:20%}.simcal-box-handle label>select{display:table-cell;width:80%}.simcal-box-handle .simcal-tabs{width:16%}.simcal-box-handle .simcal-tabs i{font-size:18px;line-height:1}.simcal-box-handle .simcal-tabs span{display:none}.simcal-box-handle .simcal-panel{margin-left:16%;max-width:84%}.simcal-box-handle .simcal-panel-section{display:block;overflow:hidden}.simcal-box-handle .simcal-panel-field th{display:block;width:100%}.simcal-box-handle .simcal-panel-field td{display:inline-block;width:100%}.simcal-box-handle .simcal-panel-field select{max-width:86%}}input.simcal-shortcode{border:1px dashed rgba(0,0,0,.3);color:rgba(0,0,0,.58);font-family:monospace;font-weight:700;text-align:center}
assets/css/default-calendar-grid.css ADDED
@@ -0,0 +1,402 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family: 'simple-calendar';
3
+ src: url("../fonts/simple-calendar.eot?43976014");
4
+ src: url("../fonts/simple-calendar.eot?43976014#iefix") format("embedded-opentype"), url("../fonts/simple-calendar.woff?43976014") format("woff"), url("../fonts/simple-calendar.ttf?43976014") format("truetype"), url("../fonts/simple-calendar.svg?43976014#simple-calendar") format("svg");
5
+ font-weight: normal;
6
+ font-style: normal; }
7
+
8
+ [class^="simcal-icon-"]:before,
9
+ [class*=" simcal-icon-"]:before {
10
+ display: inline-block;
11
+ font-family: "simple-calendar";
12
+ font-style: normal;
13
+ font-weight: normal;
14
+ margin-right: .2em;
15
+ speak: none;
16
+ text-decoration: inherit;
17
+ text-align: center;
18
+ width: 1em;
19
+ font-variant: normal;
20
+ text-transform: none;
21
+ margin-left: .2em;
22
+ -webkit-font-smoothing: antialiased;
23
+ -moz-osx-font-smoothing: grayscale; }
24
+
25
+ .simcal-icon-spin {
26
+ -webkit-animation: spin 2s infinite linear;
27
+ animation: spin 2s infinite linear;
28
+ display: inline-block; }
29
+
30
+ @-webkit-keyframes spin {
31
+ 0% {
32
+ -webkit-transform: rotate(0deg);
33
+ transform: rotate(0deg); }
34
+ 100% {
35
+ -webkit-transform: rotate(359deg);
36
+ transform: rotate(359deg); } }
37
+
38
+ @keyframes spin {
39
+ 0% {
40
+ -webkit-transform: rotate(0deg);
41
+ transform: rotate(0deg); }
42
+ 100% {
43
+ -webkit-transform: rotate(359deg);
44
+ transform: rotate(359deg); } }
45
+
46
+ .simcal-icon-animate:before {
47
+ transition: all .2s ease-in-out; }
48
+
49
+ .simcal-icon-rotate-180:before {
50
+ -webkit-transform: rotate(180deg);
51
+ transform: rotate(180deg); }
52
+
53
+ .simcal-icon-calendar-empty:before {
54
+ content: '\e800'; }
55
+
56
+ .simcal-icon-calendar:before {
57
+ content: '\e801'; }
58
+
59
+ .simcal-icon-calendar-logo:before {
60
+ content: '\e802'; }
61
+
62
+ .simcal-icon-settings:before {
63
+ content: '\e804'; }
64
+
65
+ .simcal-icon-toggles:before {
66
+ content: '\e805'; }
67
+
68
+ .simcal-icon-list:before {
69
+ content: '\e806'; }
70
+
71
+ .simcal-icon-event:before {
72
+ content: '\e807'; }
73
+
74
+ .simcal-icon-help:before {
75
+ content: '\e808'; }
76
+
77
+ .simcal-icon-panel:before {
78
+ content: '\e80a'; }
79
+
80
+ .simcal-icon-grid:before {
81
+ content: '\e80b'; }
82
+
83
+ .simcal-icon-google:before {
84
+ content: '\e80c'; }
85
+
86
+ .simcal-icon-docs:before {
87
+ content: '\e80f'; }
88
+
89
+ .simcal-icon-hourglass:before {
90
+ content: '\e811'; }
91
+
92
+ .simcal-icon-globe:before {
93
+ content: '\e812'; }
94
+
95
+ .simcal-icon-timezones:before {
96
+ content: '\e813'; }
97
+
98
+ .simcal-icon-warning:before {
99
+ content: '\e815'; }
100
+
101
+ .simcal-icon-wordpress:before {
102
+ content: '\e814'; }
103
+
104
+ .simcal-icon-up:before {
105
+ content: '\e80e'; }
106
+
107
+ .simcal-icon-right:before {
108
+ content: '\e809'; }
109
+
110
+ .simcal-icon-down:before {
111
+ content: '\e80d'; }
112
+
113
+ .simcal-icon-left:before {
114
+ content: '\e803'; }
115
+
116
+ .simcal-icon-spinner:before {
117
+ content: '\e810'; }
118
+
119
+ .simcal-calendar {
120
+ position: relative; }
121
+
122
+ .simcal-powered {
123
+ display: block;
124
+ margin: -10px 0 20px; }
125
+
126
+ .simcal-align-left {
127
+ text-align: left; }
128
+
129
+ .simcal-align-right {
130
+ text-align: right; }
131
+
132
+ .simcal-default-calendar .simcal-current h3 {
133
+ margin: 0;
134
+ padding: 0; }
135
+
136
+ .simcal-default-calendar .simcal-nav {
137
+ vertical-align: middle; }
138
+
139
+ .simcal-default-calendar .simcal-nav-button {
140
+ background: transparent;
141
+ border: 0;
142
+ box-shadow: none;
143
+ cursor: pointer;
144
+ margin: 0;
145
+ outline: none;
146
+ padding: 0;
147
+ transition: margin .2s ease-out;
148
+ width: 100%; }
149
+ .simcal-default-calendar .simcal-nav-button:focus, .simcal-default-calendar .simcal-nav-button:hover {
150
+ background: transparent;
151
+ border: 0;
152
+ box-shadow: none;
153
+ outline: none; }
154
+ .simcal-default-calendar .simcal-nav-button.simcal-prev:hover {
155
+ margin-left: -10px; }
156
+ .simcal-default-calendar .simcal-nav-button.simcal-next:hover {
157
+ margin-right: -10px; }
158
+ .simcal-default-calendar .simcal-nav-button:disabled {
159
+ cursor: default; }
160
+ .simcal-default-calendar .simcal-nav-button:disabled:hover {
161
+ margin: 0; }
162
+
163
+ .simcal-default-calendar .simcal-events-toggle {
164
+ border: 0;
165
+ box-shadow: none;
166
+ text-align: center;
167
+ width: 100%; }
168
+ .simcal-default-calendar .simcal-events-toggle:active, .simcal-default-calendar .simcal-events-toggle:focus, .simcal-default-calendar .simcal-events-toggle:hover {
169
+ border: 0;
170
+ box-shadow: none;
171
+ outline: none; }
172
+
173
+ .simcal-default-calendar.simcal-event-bubble {
174
+ background-color: #fff;
175
+ border: 1px solid rgba(0, 0, 0, 0.1);
176
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); }
177
+ .simcal-default-calendar.simcal-event-bubble:before {
178
+ border-bottom: 5px solid #fff;
179
+ border-left: 5px solid transparent;
180
+ border-right: 5px solid transparent;
181
+ content: ' ';
182
+ display: block;
183
+ font-size: 0;
184
+ height: 0;
185
+ left: 50%;
186
+ line-height: 0;
187
+ margin: -5px 0 0 -5px;
188
+ position: absolute;
189
+ width: 0;
190
+ z-index: 16000; }
191
+ .simcal-default-calendar.simcal-event-bubble:after {
192
+ border-bottom: 5px solid rgba(0, 0, 0, 0.18);
193
+ border-left: 5px solid transparent;
194
+ border-right: 5px solid transparent;
195
+ content: ' ';
196
+ display: block;
197
+ font-size: 0;
198
+ height: 0;
199
+ left: 50%;
200
+ line-height: 0;
201
+ margin: -6px 0 0 -5px;
202
+ position: absolute;
203
+ top: 0;
204
+ width: 0;
205
+ z-index: 14999; }
206
+ .simcal-default-calendar.simcal-event-bubble .simcal-event {
207
+ list-style: none; }
208
+ .simcal-default-calendar.simcal-event-bubble .simcal-events {
209
+ margin: 0; }
210
+
211
+ .simcal-default-calendar ul.simcal-attachments,
212
+ .simcal-default-calendar ul.simcal-attendees {
213
+ margin: 0;
214
+ padding: 0; }
215
+
216
+ .simcal-default-calendar li.simcal-attachment,
217
+ .simcal-default-calendar li.simcal-attendee {
218
+ list-style: none;
219
+ margin-bottom: 4px; }
220
+ .simcal-default-calendar li.simcal-attachment:last-child,
221
+ .simcal-default-calendar li.simcal-attendee:last-child {
222
+ margin-bottom: 0; }
223
+ .simcal-default-calendar li.simcal-attachment small,
224
+ .simcal-default-calendar li.simcal-attendee small {
225
+ opacity: .9;
226
+ text-transform: lowercase; }
227
+
228
+ .simcal-default-calendar li.simcal-attachment a,
229
+ .simcal-default-calendar li.simcal-attendee a,
230
+ .simcal-default-calendar .simcal-organizer a {
231
+ border-bottom: 0;
232
+ text-decoration: none; }
233
+
234
+ .simcal-default-calendar li.simcal-attachment img,
235
+ .simcal-default-calendar li.simcal-attendee img,
236
+ .simcal-default-calendar .simcal-organizer img {
237
+ display: inline-block;
238
+ margin: 0 4px;
239
+ max-height: 24px;
240
+ max-width: 24px; }
241
+
242
+ .simcal-default-calendar .simcal-tooltip-content {
243
+ font-size: 1.2em;
244
+ line-height: 1.4;
245
+ padding: 5px; }
246
+
247
+ .simcal-default-calendar .simcal-ajax-loader {
248
+ height: 100%;
249
+ left: 0;
250
+ position: absolute;
251
+ top: 0;
252
+ width: 100%; }
253
+ .simcal-default-calendar .simcal-ajax-loader > i {
254
+ font-size: 48px;
255
+ left: 50%;
256
+ line-height: 1;
257
+ margin: -36px 0 0 -36px;
258
+ position: absolute;
259
+ top: 50%; }
260
+ .simcal-default-calendar .simcal-ajax-loader.simcal-spinner-top > i {
261
+ top: 20%; }
262
+ .simcal-default-calendar .simcal-ajax-loader.simcal-spinner-bottom > i {
263
+ bottom: 20%;
264
+ top: auto; }
265
+
266
+ .simcal-default-calendar-light .simcal-nav-button {
267
+ color: rgba(0, 0, 0, 0.6); }
268
+ .simcal-default-calendar-light .simcal-nav-button:disabled {
269
+ color: rgba(255, 255, 255, 0.9) !important; }
270
+ .simcal-default-calendar-light .simcal-nav-button:disabled:hover {
271
+ color: rgba(255, 255, 255, 0.9) !important; }
272
+ .simcal-default-calendar-light .simcal-nav-button:focus, .simcal-default-calendar-light .simcal-nav-button:hover {
273
+ color: rgba(0, 0, 0, 0.9); }
274
+
275
+ .simcal-default-calendar-light .simcal-events-toggle {
276
+ color: rgba(0, 0, 0, 0.6); }
277
+ .simcal-default-calendar-light .simcal-events-toggle:hover {
278
+ background-color: rgba(0, 0, 0, 0.1);
279
+ color: white; }
280
+
281
+ .simcal-default-calendar-light .simcal-ajax-loader {
282
+ background-color: rgba(0, 0, 0, 0.1); }
283
+ .simcal-default-calendar-light .simcal-ajax-loader > i {
284
+ color: rgba(0, 0, 0, 0.3); }
285
+
286
+ .simcal-default-calendar-dark .simcal-nav-button {
287
+ color: rgba(255, 255, 255, 0.6); }
288
+ .simcal-default-calendar-dark .simcal-nav-button:disabled {
289
+ color: rgba(0, 0, 0, 0.9) !important; }
290
+ .simcal-default-calendar-dark .simcal-nav-button:disabled:hover {
291
+ color: rgba(0, 0, 0, 0.9) !important; }
292
+ .simcal-default-calendar-dark .simcal-nav-button:focus, .simcal-default-calendar-dark .simcal-nav-button:hover {
293
+ color: rgba(255, 255, 255, 0.9); }
294
+
295
+ .simcal-default-calendar-dark .simcal-events-toggle {
296
+ color: rgba(255, 255, 255, 0.6); }
297
+ .simcal-default-calendar-dark .simcal-events-toggle:hover {
298
+ background-color: rgba(255, 255, 255, 0.1);
299
+ color: black; }
300
+
301
+ .simcal-default-calendar-dark .simcal-ajax-loader {
302
+ background-color: rgba(255, 255, 255, 0.1); }
303
+ .simcal-default-calendar-dark .simcal-ajax-loader > i {
304
+ color: rgba(255, 255, 255, 0.3); }
305
+
306
+ .simcal-default-calendar-grid > table {
307
+ table-layout: fixed;
308
+ width: 100%; }
309
+ .simcal-default-calendar-grid > table thead th,
310
+ .simcal-default-calendar-grid > table tbody td {
311
+ text-align: center;
312
+ vertical-align: top; }
313
+ .simcal-default-calendar-grid > table tbody td {
314
+ padding: 0 !important; }
315
+
316
+ .simcal-default-calendar-grid .simcal-calendar-head .simcal-nav {
317
+ padding: 10px 0;
318
+ vertical-align: middle; }
319
+
320
+ .simcal-default-calendar-grid .simcal-day > div {
321
+ box-sizing: content-box;
322
+ display: block;
323
+ height: 100%; }
324
+
325
+ .simcal-default-calendar-grid .simcal-day-void {
326
+ border-width: 0;
327
+ height: 100%;
328
+ min-height: 32px; }
329
+
330
+ .simcal-default-calendar-grid .simcal-day-number {
331
+ display: block;
332
+ line-height: 1;
333
+ padding: 2px 4px 3px;
334
+ vertical-align: middle; }
335
+
336
+ .simcal-default-calendar-grid .simcal-no-events {
337
+ display: block;
338
+ min-height: 32px; }
339
+
340
+ .simcal-default-calendar-grid.simcal-default-calendar-light .simcal-day:hover {
341
+ background-color: rgba(0, 0, 0, 0.1); }
342
+
343
+ .simcal-default-calendar-grid.simcal-default-calendar-light .simcal-day-void {
344
+ background-color: rgba(0, 0, 0, 0.04); }
345
+ .simcal-default-calendar-grid.simcal-default-calendar-light .simcal-day-void:hover {
346
+ background-color: rgba(0, 0, 0, 0.04); }
347
+
348
+ .simcal-default-calendar-grid.simcal-default-calendar-light .simcal-day-number {
349
+ background: rgba(0, 0, 0, 0.1); }
350
+
351
+ .simcal-default-calendar-grid.simcal-default-calendar-dark .simcal-day:hover {
352
+ background-color: rgba(255, 255, 255, 0.18); }
353
+
354
+ .simcal-default-calendar-grid.simcal-default-calendar-dark .simcal-day-void {
355
+ background-color: rgba(255, 255, 255, 0.05); }
356
+ .simcal-default-calendar-grid.simcal-default-calendar-dark .simcal-day-void:hover {
357
+ background-color: rgba(255, 255, 255, 0.05); }
358
+
359
+ .simcal-default-calendar-grid.simcal-default-calendar-dark .simcal-day-number {
360
+ background: rgba(255, 255, 255, 0.1); }
361
+
362
+ .simcal-default-calendar-grid .simcal-events {
363
+ font-size: .68em;
364
+ line-height: 1.4;
365
+ list-style: none;
366
+ margin: 0;
367
+ padding: 0;
368
+ text-align: left; }
369
+ .simcal-default-calendar-grid .simcal-events > .simcal-event {
370
+ border-bottom-style: solid;
371
+ border-bottom-width: 1px;
372
+ cursor: pointer;
373
+ list-style: none;
374
+ margin: 0 0 2px 0;
375
+ padding: 4px; }
376
+ .simcal-default-calendar-grid .simcal-events > .simcal-event:hover {
377
+ text-decoration: underline; }
378
+ .simcal-default-calendar-grid .simcal-events > .simcal-event:last-child {
379
+ border-bottom: 0;
380
+ margin-bottom: 0; }
381
+
382
+ .simcal-default-calendar-grid.simcal-default-calendar-light .simcal-event {
383
+ border-bottom-color: rgba(0, 0, 0, 0.1); }
384
+
385
+ .simcal-default-calendar-grid.simcal-default-calendar-dark .simcal-event {
386
+ border-bottom-color: rgba(255, 255, 255, 0.1); }
387
+
388
+ .simcal-default-calendar-grid .simcal-events-dots {
389
+ cursor: pointer;
390
+ display: block;
391
+ line-height: .7;
392
+ margin: 3px 0;
393
+ text-align: center; }
394
+
395
+ .simcal-default-calendar-grid .simcal-events-toggle {
396
+ background: transparent;
397
+ display: block;
398
+ font-size: 10px;
399
+ padding: 2px 0; }
400
+
401
+ .qtip-content .simcal-event-details p {
402
+ margin-bottom: 1em; }
assets/css/default-calendar-grid.min.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /*! Simple Calendar - 3.0.0
2
+ * https://simplecalendar.io
3
+ * Copyright (c) Moonstone Media 2015
4
+ * Licensed GPLv2+ */
5
+
6
+ .simcal-default-calendar .simcal-nav-button,.simcal-default-calendar .simcal-nav-button:focus,.simcal-default-calendar .simcal-nav-button:hover{background:0 0;border:0;box-shadow:none;outline:0}@font-face{font-family:simple-calendar;src:url(../fonts/simple-calendar.eot?43976014);src:url(../fonts/simple-calendar.eot?43976014#iefix) format("embedded-opentype"),url(../fonts/simple-calendar.woff?43976014) format("woff"),url(../fonts/simple-calendar.ttf?43976014) format("truetype"),url(../fonts/simple-calendar.svg?43976014#simple-calendar) format("svg");font-weight:400;font-style:normal}[class*=" simcal-icon-"]:before,[class^=simcal-icon-]:before{display:inline-block;font-family:simple-calendar;font-style:normal;font-weight:400;margin-right:.2em;speak:none;text-decoration:inherit;text-align:center;width:1em;font-variant:normal;text-transform:none;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.simcal-icon-spin{-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear;display:inline-block}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.simcal-icon-animate:before{transition:all .2s ease-in-out}.simcal-icon-rotate-180:before{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.simcal-icon-calendar-empty:before{content:'\e800'}.simcal-icon-calendar:before{content:'\e801'}.simcal-icon-calendar-logo:before{content:'\e802'}.simcal-icon-settings:before{content:'\e804'}.simcal-icon-toggles:before{content:'\e805'}.simcal-icon-list:before{content:'\e806'}.simcal-icon-event:before{content:'\e807'}.simcal-icon-help:before{content:'\e808'}.simcal-icon-panel:before{content:'\e80a'}.simcal-icon-grid:before{content:'\e80b'}.simcal-icon-google:before{content:'\e80c'}.simcal-icon-docs:before{content:'\e80f'}.simcal-icon-hourglass:before{content:'\e811'}.simcal-icon-globe:before{content:'\e812'}.simcal-icon-timezones:before{content:'\e813'}.simcal-icon-warning:before{content:'\e815'}.simcal-icon-wordpress:before{content:'\e814'}.simcal-icon-up:before{content:'\e80e'}.simcal-icon-right:before{content:'\e809'}.simcal-icon-down:before{content:'\e80d'}.simcal-icon-left:before{content:'\e803'}.simcal-icon-spinner:before{content:'\e810'}.simcal-calendar{position:relative}.simcal-powered{display:block;margin:-10px 0 20px}.simcal-align-left{text-align:left}.simcal-align-right{text-align:right}.simcal-default-calendar .simcal-current h3{margin:0;padding:0}.simcal-default-calendar .simcal-nav{vertical-align:middle}.simcal-default-calendar .simcal-nav-button{cursor:pointer;margin:0;padding:0;transition:margin .2s ease-out;width:100%}.simcal-default-calendar .simcal-nav-button.simcal-prev:hover{margin-left:-10px}.simcal-default-calendar .simcal-nav-button.simcal-next:hover{margin-right:-10px}.simcal-default-calendar .simcal-nav-button:disabled{cursor:default}.simcal-default-calendar .simcal-nav-button:disabled:hover{margin:0}.simcal-default-calendar .simcal-events-toggle{border:0;box-shadow:none;text-align:center;width:100%}.simcal-default-calendar .simcal-events-toggle:active,.simcal-default-calendar .simcal-events-toggle:focus,.simcal-default-calendar .simcal-events-toggle:hover{border:0;box-shadow:none;outline:0}.simcal-default-calendar.simcal-event-bubble{background-color:#fff;border:1px solid rgba(0,0,0,.1);box-shadow:0 1px 2px rgba(0,0,0,.1)}.simcal-default-calendar.simcal-event-bubble:after,.simcal-default-calendar.simcal-event-bubble:before{border-left:5px solid transparent;border-right:5px solid transparent;content:' ';display:block;font-size:0;height:0;left:50%;line-height:0;width:0;position:absolute}.simcal-default-calendar.simcal-event-bubble:before{border-bottom:5px solid #fff;margin:-5px 0 0 -5px;z-index:16000}.simcal-default-calendar.simcal-event-bubble:after{border-bottom:5px solid rgba(0,0,0,.18);margin:-6px 0 0 -5px;top:0;z-index:14999}.simcal-default-calendar.simcal-event-bubble .simcal-event{list-style:none}.simcal-default-calendar.simcal-event-bubble .simcal-events{margin:0}.simcal-default-calendar ul.simcal-attachments,.simcal-default-calendar ul.simcal-attendees{margin:0;padding:0}.simcal-default-calendar li.simcal-attachment,.simcal-default-calendar li.simcal-attendee{list-style:none;margin-bottom:4px}.simcal-default-calendar li.simcal-attachment:last-child,.simcal-default-calendar li.simcal-attendee:last-child{margin-bottom:0}.simcal-default-calendar li.simcal-attachment small,.simcal-default-calendar li.simcal-attendee small{opacity:.9;text-transform:lowercase}.simcal-default-calendar .simcal-organizer a,.simcal-default-calendar li.simcal-attachment a,.simcal-default-calendar li.simcal-attendee a{border-bottom:0;text-decoration:none}.simcal-default-calendar .simcal-organizer img,.simcal-default-calendar li.simcal-attachment img,.simcal-default-calendar li.simcal-attendee img{display:inline-block;margin:0 4px;max-height:24px;max-width:24px}.simcal-default-calendar .simcal-tooltip-content{font-size:1.2em;line-height:1.4;padding:5px}.simcal-default-calendar .simcal-ajax-loader{height:100%;left:0;position:absolute;top:0;width:100%}.simcal-default-calendar .simcal-ajax-loader>i{font-size:48px;left:50%;line-height:1;margin:-36px 0 0 -36px;position:absolute;top:50%}.simcal-default-calendar .simcal-ajax-loader.simcal-spinner-top>i{top:20%}.simcal-default-calendar .simcal-ajax-loader.simcal-spinner-bottom>i{bottom:20%;top:auto}.simcal-default-calendar-light .simcal-nav-button{color:rgba(0,0,0,.6)}.simcal-default-calendar-light .simcal-nav-button:disabled,.simcal-default-calendar-light .simcal-nav-button:disabled:hover{color:rgba(255,255,255,.9)!important}.simcal-default-calendar-light .simcal-nav-button:focus,.simcal-default-calendar-light .simcal-nav-button:hover{color:rgba(0,0,0,.9)}.simcal-default-calendar-light .simcal-events-toggle{color:rgba(0,0,0,.6)}.simcal-default-calendar-light .simcal-events-toggle:hover{background-color:rgba(0,0,0,.1);color:#fff}.simcal-default-calendar-light .simcal-ajax-loader{background-color:rgba(0,0,0,.1)}.simcal-default-calendar-light .simcal-ajax-loader>i{color:rgba(0,0,0,.3)}.simcal-default-calendar-dark .simcal-nav-button{color:rgba(255,255,255,.6)}.simcal-default-calendar-dark .simcal-nav-button:disabled,.simcal-default-calendar-dark .simcal-nav-button:disabled:hover{color:rgba(0,0,0,.9)!important}.simcal-default-calendar-dark .simcal-nav-button:focus,.simcal-default-calendar-dark .simcal-nav-button:hover{color:rgba(255,255,255,.9)}.simcal-default-calendar-dark .simcal-events-toggle{color:rgba(255,255,255,.6)}.simcal-default-calendar-dark .simcal-events-toggle:hover{background-color:rgba(255,255,255,.1);color:#000}.simcal-default-calendar-dark .simcal-ajax-loader{background-color:rgba(255,255,255,.1)}.simcal-default-calendar-dark .simcal-ajax-loader>i{color:rgba(255,255,255,.3)}.simcal-default-calendar-grid>table{table-layout:fixed;width:100%}.simcal-default-calendar-grid>table tbody td,.simcal-default-calendar-grid>table thead th{text-align:center;vertical-align:top}.simcal-default-calendar-grid>table tbody td{padding:0!important}.simcal-default-calendar-grid .simcal-calendar-head .simcal-nav{padding:10px 0;vertical-align:middle}.simcal-default-calendar-grid .simcal-day>div{box-sizing:content-box;display:block;height:100%}.simcal-default-calendar-grid .simcal-day-void{border-width:0;height:100%;min-height:32px}.simcal-default-calendar-grid .simcal-day-number{display:block;line-height:1;padding:2px 4px 3px;vertical-align:middle}.simcal-default-calendar-grid .simcal-no-events{display:block;min-height:32px}.simcal-default-calendar-grid.simcal-default-calendar-light .simcal-day:hover{background-color:rgba(0,0,0,.1)}.simcal-default-calendar-grid.simcal-default-calendar-light .simcal-day-void,.simcal-default-calendar-grid.simcal-default-calendar-light .simcal-day-void:hover{background-color:rgba(0,0,0,.04)}.simcal-default-calendar-grid.simcal-default-calendar-light .simcal-day-number{background:rgba(0,0,0,.1)}.simcal-default-calendar-grid.simcal-default-calendar-dark .simcal-day:hover{background-color:rgba(255,255,255,.18)}.simcal-default-calendar-grid.simcal-default-calendar-dark .simcal-day-void,.simcal-default-calendar-grid.simcal-default-calendar-dark .simcal-day-void:hover{background-color:rgba(255,255,255,.05)}.simcal-default-calendar-grid.simcal-default-calendar-dark .simcal-day-number{background:rgba(255,255,255,.1)}.simcal-default-calendar-grid .simcal-events{font-size:.68em;line-height:1.4;list-style:none;margin:0;padding:0;text-align:left}.simcal-default-calendar-grid .simcal-events>.simcal-event{border-bottom-style:solid;border-bottom-width:1px;cursor:pointer;list-style:none;margin:0 0 2px;padding:4px}.simcal-default-calendar-grid .simcal-events>.simcal-event:hover{text-decoration:underline}.simcal-default-calendar-grid .simcal-events>.simcal-event:last-child{border-bottom:0;margin-bottom:0}.simcal-default-calendar-grid.simcal-default-calendar-light .simcal-event{border-bottom-color:rgba(0,0,0,.1)}.simcal-default-calendar-grid.simcal-default-calendar-dark .simcal-event{border-bottom-color:rgba(255,255,255,.1)}.simcal-default-calendar-grid .simcal-events-dots{cursor:pointer;display:block;line-height:.7;margin:3px 0;text-align:center}.simcal-default-calendar-grid .simcal-events-toggle{background:0 0;display:block;font-size:10px;padding:2px 0}.qtip-content .simcal-event-details p{margin-bottom:1em}
assets/css/default-calendar-list.css ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @font-face {
2
+ font-family: 'simple-calendar';
3
+ src: url("../fonts/simple-calendar.eot?43976014");
4
+ src: url("../fonts/simple-calendar.eot?43976014#iefix") format("embedded-opentype"), url("../fonts/simple-calendar.woff?43976014") format("woff"), url("../fonts/simple-calendar.ttf?43976014") format("truetype"), url("../fonts/simple-calendar.svg?43976014#simple-calendar") format("svg");
5
+ font-weight: normal;
6
+ font-style: normal; }
7
+
8
+ [class^="simcal-icon-"]:before,
9
+ [class*=" simcal-icon-"]:before {
10
+ display: inline-block;
11
+ font-family: "simple-calendar";
12
+ font-style: normal;
13
+ font-weight: normal;
14
+ margin-right: .2em;
15
+ speak: none;
16
+ text-decoration: inherit;
17
+ text-align: center;
18
+ width: 1em;
19
+ font-variant: normal;
20
+ text-transform: none;
21
+ margin-left: .2em;
22
+ -webkit-font-smoothing: antialiased;
23
+ -moz-osx-font-smoothing: grayscale; }
24
+
25
+ .simcal-icon-spin {
26
+ -webkit-animation: spin 2s infinite linear;
27
+ animation: spin 2s infinite linear;
28
+ display: inline-block; }
29
+
30
+ @-webkit-keyframes spin {
31
+ 0% {
32
+ -webkit-transform: rotate(0deg);
33
+ transform: rotate(0deg); }
34
+ 100% {
35
+ -webkit-transform: rotate(359deg);
36
+ transform: rotate(359deg); } }
37
+
38
+ @keyframes spin {
39
+ 0% {
40
+ -webkit-transform: rotate(0deg);
41
+ transform: rotate(0deg); }
42
+ 100% {
43
+ -webkit-transform: rotate(359deg);
44
+ transform: rotate(359deg); } }
45
+
46
+ .simcal-icon-animate:before {
47
+ transition: all .2s ease-in-out; }
48
+
49
+ .simcal-icon-rotate-180:before {
50
+ -webkit-transform: rotate(180deg);
51
+ transform: rotate(180deg); }
52
+
53
+ .simcal-icon-calendar-empty:before {
54
+ content: '\e800'; }
55
+
56
+ .simcal-icon-calendar:before {
57
+ content: '\e801'; }
58
+
59
+ .simcal-icon-calendar-logo:before {
60
+ content: '\e802'; }
61
+
62
+ .simcal-icon-settings:before {
63
+ content: '\e804'; }
64
+
65
+ .simcal-icon-toggles:before {
66
+ content: '\e805'; }
67
+
68
+ .simcal-icon-list:before {
69
+ content: '\e806'; }
70
+
71
+ .simcal-icon-event:before {
72
+ content: '\e807'; }
73
+
74
+ .simcal-icon-help:before {
75
+ content: '\e808'; }
76
+
77
+ .simcal-icon-panel:before {
78
+ content: '\e80a'; }
79
+
80
+ .simcal-icon-grid:before {
81
+ content: '\e80b'; }
82
+
83
+ .simcal-icon-google:before {
84
+ content: '\e80c'; }
85
+
86
+ .simcal-icon-docs:before {
87
+ content: '\e80f'; }
88
+
89
+ .simcal-icon-hourglass:before {
90
+ content: '\e811'; }
91
+
92
+ .simcal-icon-globe:before {
93
+ content: '\e812'; }
94
+
95
+ .simcal-icon-timezones:before {
96
+ content: '\e813'; }
97
+
98
+ .simcal-icon-warning:before {
99
+ content: '\e815'; }
100
+
101
+ .simcal-icon-wordpress:before {
102
+ content: '\e814'; }
103
+
104
+ .simcal-icon-up:before {
105
+ content: '\e80e'; }
106
+
107
+ .simcal-icon-right:before {
108
+ content: '\e809'; }
109
+
110
+ .simcal-icon-down:before {
111
+ content: '\e80d'; }
112
+
113
+ .simcal-icon-left:before {
114
+ content: '\e803'; }
115
+
116
+ .simcal-icon-spinner:before {
117
+ content: '\e810'; }
118
+
119
+ .simcal-calendar {
120
+ position: relative; }
121
+
122
+ .simcal-powered {
123
+ display: block;
124
+ margin: -10px 0 20px; }
125
+
126
+ .simcal-align-left {
127
+ text-align: left; }
128
+
129
+ .simcal-align-right {
130
+ text-align: right; }
131
+
132
+ .simcal-default-calendar .simcal-current h3 {
133
+ margin: 0;
134
+ padding: 0; }
135
+
136
+ .simcal-default-calendar .simcal-nav {
137
+ vertical-align: middle; }
138
+
139
+ .simcal-default-calendar .simcal-nav-button {
140
+ background: transparent;
141
+ border: 0;
142
+ box-shadow: none;
143
+ cursor: pointer;
144
+ margin: 0;
145
+ outline: none;
146
+ padding: 0;
147
+ transition: margin .2s ease-out;
148
+ width: 100%; }
149
+ .simcal-default-calendar .simcal-nav-button:focus, .simcal-default-calendar .simcal-nav-button:hover {
150
+ background: transparent;
151
+ border: 0;
152
+ box-shadow: none;
153
+ outline: none; }
154
+ .simcal-default-calendar .simcal-nav-button.simcal-prev:hover {
155
+ margin-left: -10px; }
156
+ .simcal-default-calendar .simcal-nav-button.simcal-next:hover {
157
+ margin-right: -10px; }
158
+ .simcal-default-calendar .simcal-nav-button:disabled {
159
+ cursor: default; }
160
+ .simcal-default-calendar .simcal-nav-button:disabled:hover {
161
+ margin: 0; }
162
+
163
+ .simcal-default-calendar .simcal-events-toggle {
164
+ border: 0;
165
+ box-shadow: none;
166
+ text-align: center;
167
+ width: 100%; }
168
+ .simcal-default-calendar .simcal-events-toggle:active, .simcal-default-calendar .simcal-events-toggle:focus, .simcal-default-calendar .simcal-events-toggle:hover {
169
+ border: 0;
170
+ box-shadow: none;
171
+ outline: none; }
172
+
173
+ .simcal-default-calendar.simcal-event-bubble {
174
+ background-color: #fff;
175
+ border: 1px solid rgba(0, 0, 0, 0.1);
176
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); }
177
+ .simcal-default-calendar.simcal-event-bubble:before {
178
+ border-bottom: 5px solid #fff;
179
+ border-left: 5px solid transparent;
180
+ border-right: 5px solid transparent;
181
+ content: ' ';
182
+ display: block;
183
+ font-size: 0;
184
+ height: 0;
185
+ left: 50%;
186
+ line-height: 0;
187
+ margin: -5px 0 0 -5px;
188
+ position: absolute;
189
+ width: 0;
190
+ z-index: 16000; }
191
+ .simcal-default-calendar.simcal-event-bubble:after {
192
+ border-bottom: 5px solid rgba(0, 0, 0, 0.18);
193
+ border-left: 5px solid transparent;
194
+ border-right: 5px solid transparent;
195
+ content: ' ';
196
+ display: block;
197
+ font-size: 0;
198
+ height: 0;
199
+ left: 50%;
200
+ line-height: 0;
201
+ margin: -6px 0 0 -5px;
202
+ position: absolute;
203
+ top: 0;
204
+ width: 0;
205
+ z-index: 14999; }
206
+ .simcal-default-calendar.simcal-event-bubble .simcal-event {
207
+ list-style: none; }
208
+ .simcal-default-calendar.simcal-event-bubble .simcal-events {
209
+ margin: 0; }
210
+
211
+ .simcal-default-calendar ul.simcal-attachments,
212
+ .simcal-default-calendar ul.simcal-attendees {
213
+ margin: 0;
214
+ padding: 0; }
215
+
216
+ .simcal-default-calendar li.simcal-attachment,
217
+ .simcal-default-calendar li.simcal-attendee {
218
+ list-style: none;
219
+ margin-bottom: 4px; }
220
+ .simcal-default-calendar li.simcal-attachment:last-child,
221
+ .simcal-default-calendar li.simcal-attendee:last-child {
222
+ margin-bottom: 0; }
223
+ .simcal-default-calendar li.simcal-attachment small,
224
+ .simcal-default-calendar li.simcal-attendee small {
225
+ opacity: .9;
226
+ text-transform: lowercase; }
227
+
228
+ .simcal-default-calendar li.simcal-attachment a,
229
+ .simcal-default-calendar li.simcal-attendee a,
230
+ .simcal-default-calendar .simcal-organizer a {
231
+ border-bottom: 0;
232
+ text-decoration: none; }
233
+
234
+ .simcal-default-calendar li.simcal-attachment img,
235
+ .simcal-default-calendar li.simcal-attendee img,
236
+ .simcal-default-calendar .simcal-organizer img {
237
+ display: inline-block;
238
+ margin: 0 4px;
239
+ max-height: 24px;
240
+ max-width: 24px; }
241
+
242
+ .simcal-default-calendar .simcal-tooltip-content {
243
+ font-size: 1.2em;
244
+ line-height: 1.4;
245
+ padding: 5px; }
246
+
247
+ .simcal-default-calendar .simcal-ajax-loader {
248
+ height: 100%;
249
+ left: 0;
250
+ position: absolute;
251
+ top: 0;
252
+ width: 100%; }
253
+ .simcal-default-calendar .simcal-ajax-loader > i {
254
+ font-size: 48px;
255
+ left: 50%;
256
+ line-height: 1;
257
+ margin: -36px 0 0 -36px;
258
+ position: absolute;
259
+ top: 50%; }
260
+ .simcal-default-calendar .simcal-ajax-loader.simcal-spinner-top > i {
261
+ top: 20%; }
262
+ .simcal-default-calendar .simcal-ajax-loader.simcal-spinner-bottom > i {
263
+ bottom: 20%;
264
+ top: auto; }
265
+
266
+ .simcal-default-calendar-light .simcal-nav-button {
267
+ color: rgba(0, 0, 0, 0.6); }
268
+ .simcal-default-calendar-light .simcal-nav-button:disabled {
269
+ color: rgba(255, 255, 255, 0.9) !important; }
270
+ .simcal-default-calendar-light .simcal-nav-button:disabled:hover {
271
+ color: rgba(255, 255, 255, 0.9) !important; }
272
+ .simcal-default-calendar-light .simcal-nav-button:focus, .simcal-default-calendar-light .simcal-nav-button:hover {
273
+ color: rgba(0, 0, 0, 0.9); }
274
+
275
+ .simcal-default-calendar-light .simcal-events-toggle {
276
+ color: rgba(0, 0, 0, 0.6); }
277
+ .simcal-default-calendar-light .simcal-events-toggle:hover {
278
+ background-color: rgba(0, 0, 0, 0.1);
279
+ color: white; }
280
+
281
+ .simcal-default-calendar-light .simcal-ajax-loader {
282
+ background-color: rgba(0, 0, 0, 0.1); }
283
+ .simcal-default-calendar-light .simcal-ajax-loader > i {
284
+ color: rgba(0, 0, 0, 0.3); }
285
+
286
+ .simcal-default-calendar-dark .simcal-nav-button {
287
+ color: rgba(255, 255, 255, 0.6); }
288
+ .simcal-default-calendar-dark .simcal-nav-button:disabled {
289
+ color: rgba(0, 0, 0, 0.9) !important; }
290
+ .simcal-default-calendar-dark .simcal-nav-button:disabled:hover {
291
+ color: rgba(0, 0, 0, 0.9) !important; }
292
+ .simcal-default-calendar-dark .simcal-nav-button:focus, .simcal-default-calendar-dark .simcal-nav-button:hover {
293
+ color: rgba(255, 255, 255, 0.9); }
294
+
295
+ .simcal-default-calendar-dark .simcal-events-toggle {
296
+ color: rgba(255, 255, 255, 0.6); }
297
+ .simcal-default-calendar-dark .simcal-events-toggle:hover {
298
+ background-color: rgba(255, 255, 255, 0.1);
299
+ color: black; }
300
+
301
+ .simcal-default-calendar-dark .simcal-ajax-loader {
302
+ background-color: rgba(255, 255, 255, 0.1); }
303
+ .simcal-default-calendar-dark .simcal-ajax-loader > i {
304
+ color: rgba(255, 255, 255, 0.3); }
305
+
306
+ .simcal-default-calendar-list {
307
+ min-height: 100px;
308
+ padding: 10px 0;
309
+ text-align: left; }
310
+ .simcal-default-calendar-list .simcal-calendar-head {
311
+ display: table;
312
+ padding-bottom: 10px;
313
+ width: 100%; }
314
+ .simcal-default-calendar-list .simcal-calendar-head .simcal-nav {
315
+ display: table-cell;
316
+ text-align: center;
317
+ vertical-align: middle;
318
+ width: 20%; }
319
+ .simcal-default-calendar-list .simcal-calendar-head .simcal-nav:nth-child(2) {
320
+ width: 60%; }
321
+ .simcal-default-calendar-list .simcal-calendar-head .simcal-current span {
322
+ display: inline-block; }
323
+ .simcal-default-calendar-list dl.simcal-month {
324
+ margin-bottom: 10px;
325
+ padding: 0 10px 10px; }
326
+ .simcal-default-calendar-list dd.simcal-day {
327
+ list-style: none;
328
+ margin: 0; }
329
+ .simcal-default-calendar-list dt.simcal-day-label {
330
+ font-size: .9em;
331
+ list-style: none;
332
+ margin: 20px 0 10px; }
333
+ .simcal-default-calendar-list dt.simcal-day-label > span {
334
+ display: inline-block;
335
+ padding: 4px 8px; }
336
+ .simcal-default-calendar-list ul.simcal-events {
337
+ margin: 10px 14px 0; }
338
+ .simcal-default-calendar-list ul.simcal-events li.simcal-event {
339
+ list-style-type: none;
340
+ margin-bottom: 10px;
341
+ word-wrap: break-word; }
342
+ .simcal-default-calendar-list .simcal-event-details {
343
+ font-size: .84em;
344
+ line-height: 1.5;
345
+ overflow: hidden; }
346
+ .simcal-default-calendar-list .simcal-event-details p {
347
+ margin: 0 0 1em; }
348
+ .simcal-default-calendar-list .simcal-events-toggle {
349
+ display: inline-block;
350
+ padding: 0; }
351
+ .simcal-default-calendar-list.simcal-default-calendar-light .simcal-events-toggle {
352
+ background-color: rgba(0, 0, 0, 0.1); }
353
+ .simcal-default-calendar-list.simcal-default-calendar-dark .simcal-events-toggle {
354
+ background-color: rgba(255, 255, 255, 0.1); }
assets/css/default-calendar-list.min.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /*! Simple Calendar - 3.0.0
2
+ * https://simplecalendar.io
3
+ * Copyright (c) Moonstone Media 2015
4
+ * Licensed GPLv2+ */
5
+
6
+ .simcal-default-calendar .simcal-nav-button,.simcal-default-calendar .simcal-nav-button:focus,.simcal-default-calendar .simcal-nav-button:hover{background:0 0;border:0;box-shadow:none;outline:0}@font-face{font-family:simple-calendar;src:url(../fonts/simple-calendar.eot?43976014);src:url(../fonts/simple-calendar.eot?43976014#iefix) format("embedded-opentype"),url(../fonts/simple-calendar.woff?43976014) format("woff"),url(../fonts/simple-calendar.ttf?43976014) format("truetype"),url(../fonts/simple-calendar.svg?43976014#simple-calendar) format("svg");font-weight:400;font-style:normal}[class*=" simcal-icon-"]:before,[class^=simcal-icon-]:before{display:inline-block;font-family:simple-calendar;font-style:normal;font-weight:400;margin-right:.2em;speak:none;text-decoration:inherit;text-align:center;width:1em;font-variant:normal;text-transform:none;margin-left:.2em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.simcal-icon-spin{-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear;display:inline-block}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.simcal-icon-animate:before{transition:all .2s ease-in-out}.simcal-icon-rotate-180:before{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.simcal-icon-calendar-empty:before{content:'\e800'}.simcal-icon-calendar:before{content:'\e801'}.simcal-icon-calendar-logo:before{content:'\e802'}.simcal-icon-settings:before{content:'\e804'}.simcal-icon-toggles:before{content:'\e805'}.simcal-icon-list:before{content:'\e806'}.simcal-icon-event:before{content:'\e807'}.simcal-icon-help:before{content:'\e808'}.simcal-icon-panel:before{content:'\e80a'}.simcal-icon-grid:before{content:'\e80b'}.simcal-icon-google:before{content:'\e80c'}.simcal-icon-docs:before{content:'\e80f'}.simcal-icon-hourglass:before{content:'\e811'}.simcal-icon-globe:before{content:'\e812'}.simcal-icon-timezones:before{content:'\e813'}.simcal-icon-warning:before{content:'\e815'}.simcal-icon-wordpress:before{content:'\e814'}.simcal-icon-up:before{content:'\e80e'}.simcal-icon-right:before{content:'\e809'}.simcal-icon-down:before{content:'\e80d'}.simcal-icon-left:before{content:'\e803'}.simcal-icon-spinner:before{content:'\e810'}.simcal-calendar{position:relative}.simcal-powered{display:block;margin:-10px 0 20px}.simcal-align-left{text-align:left}.simcal-align-right{text-align:right}.simcal-default-calendar .simcal-current h3{margin:0;padding:0}.simcal-default-calendar .simcal-nav{vertical-align:middle}.simcal-default-calendar .simcal-nav-button{cursor:pointer;margin:0;padding:0;transition:margin .2s ease-out;width:100%}.simcal-default-calendar .simcal-nav-button.simcal-prev:hover{margin-left:-10px}.simcal-default-calendar .simcal-nav-button.simcal-next:hover{margin-right:-10px}.simcal-default-calendar .simcal-nav-button:disabled{cursor:default}.simcal-default-calendar .simcal-nav-button:disabled:hover{margin:0}.simcal-default-calendar .simcal-events-toggle{border:0;box-shadow:none;text-align:center;width:100%}.simcal-default-calendar .simcal-events-toggle:active,.simcal-default-calendar .simcal-events-toggle:focus,.simcal-default-calendar .simcal-events-toggle:hover{border:0;box-shadow:none;outline:0}.simcal-default-calendar.simcal-event-bubble{background-color:#fff;border:1px solid rgba(0,0,0,.1);box-shadow:0 1px 2px rgba(0,0,0,.1)}.simcal-default-calendar.simcal-event-bubble:after,.simcal-default-calendar.simcal-event-bubble:before{border-left:5px solid transparent;border-right:5px solid transparent;content:' ';display:block;font-size:0;height:0;left:50%;line-height:0;width:0;position:absolute}.simcal-default-calendar.simcal-event-bubble:before{border-bottom:5px solid #fff;margin:-5px 0 0 -5px;z-index:16000}.simcal-default-calendar.simcal-event-bubble:after{border-bottom:5px solid rgba(0,0,0,.18);margin:-6px 0 0 -5px;top:0;z-index:14999}.simcal-default-calendar.simcal-event-bubble .simcal-event{list-style:none}.simcal-default-calendar.simcal-event-bubble .simcal-events{margin:0}.simcal-default-calendar ul.simcal-attachments,.simcal-default-calendar ul.simcal-attendees{margin:0;padding:0}.simcal-default-calendar li.simcal-attachment,.simcal-default-calendar li.simcal-attendee{list-style:none;margin-bottom:4px}.simcal-default-calendar li.simcal-attachment:last-child,.simcal-default-calendar li.simcal-attendee:last-child{margin-bottom:0}.simcal-default-calendar li.simcal-attachment small,.simcal-default-calendar li.simcal-attendee small{opacity:.9;text-transform:lowercase}.simcal-default-calendar .simcal-organizer a,.simcal-default-calendar li.simcal-attachment a,.simcal-default-calendar li.simcal-attendee a{border-bottom:0;text-decoration:none}.simcal-default-calendar .simcal-organizer img,.simcal-default-calendar li.simcal-attachment img,.simcal-default-calendar li.simcal-attendee img{display:inline-block;margin:0 4px;max-height:24px;max-width:24px}.simcal-default-calendar .simcal-tooltip-content{font-size:1.2em;line-height:1.4;padding:5px}.simcal-default-calendar .simcal-ajax-loader{height:100%;left:0;position:absolute;top:0;width:100%}.simcal-default-calendar .simcal-ajax-loader>i{font-size:48px;left:50%;line-height:1;margin:-36px 0 0 -36px;position:absolute;top:50%}.simcal-default-calendar .simcal-ajax-loader.simcal-spinner-top>i{top:20%}.simcal-default-calendar .simcal-ajax-loader.simcal-spinner-bottom>i{bottom:20%;top:auto}.simcal-default-calendar-light .simcal-nav-button{color:rgba(0,0,0,.6)}.simcal-default-calendar-light .simcal-nav-button:disabled,.simcal-default-calendar-light .simcal-nav-button:disabled:hover{color:rgba(255,255,255,.9)!important}.simcal-default-calendar-light .simcal-nav-button:focus,.simcal-default-calendar-light .simcal-nav-button:hover{color:rgba(0,0,0,.9)}.simcal-default-calendar-light .simcal-events-toggle{color:rgba(0,0,0,.6)}.simcal-default-calendar-light .simcal-events-toggle:hover{background-color:rgba(0,0,0,.1);color:#fff}.simcal-default-calendar-light .simcal-ajax-loader{background-color:rgba(0,0,0,.1)}.simcal-default-calendar-light .simcal-ajax-loader>i{color:rgba(0,0,0,.3)}.simcal-default-calendar-dark .simcal-nav-button{color:rgba(255,255,255,.6)}.simcal-default-calendar-dark .simcal-nav-button:disabled,.simcal-default-calendar-dark .simcal-nav-button:disabled:hover{color:rgba(0,0,0,.9)!important}.simcal-default-calendar-dark .simcal-nav-button:focus,.simcal-default-calendar-dark .simcal-nav-button:hover{color:rgba(255,255,255,.9)}.simcal-default-calendar-dark .simcal-events-toggle{color:rgba(255,255,255,.6)}.simcal-default-calendar-dark .simcal-events-toggle:hover{background-color:rgba(255,255,255,.1);color:#000}.simcal-default-calendar-dark .simcal-ajax-loader{background-color:rgba(255,255,255,.1)}.simcal-default-calendar-dark .simcal-ajax-loader>i{color:rgba(255,255,255,.3)}.simcal-default-calendar-list{min-height:100px;padding:10px 0;text-align:left}.simcal-default-calendar-list .simcal-calendar-head{display:table;padding-bottom:10px;width:100%}.simcal-default-calendar-list .simcal-calendar-head .simcal-nav{display:table-cell;text-align:center;vertical-align:middle;width:20%}.simcal-default-calendar-list .simcal-calendar-head .simcal-nav:nth-child(2){width:60%}.simcal-default-calendar-list .simcal-calendar-head .simcal-current span{display:inline-block}.simcal-default-calendar-list dl.simcal-month{margin-bottom:10px;padding:0 10px 10px}.simcal-default-calendar-list dd.simcal-day{list-style:none;margin:0}.simcal-default-calendar-list dt.simcal-day-label{font-size:.9em;list-style:none;margin:20px 0 10px}.simcal-default-calendar-list dt.simcal-day-label>span{display:inline-block;padding:4px 8px}.simcal-default-calendar-list ul.simcal-events{margin:10px 14px 0}.simcal-default-calendar-list ul.simcal-events li.simcal-event{list-style-type:none;margin-bottom:10px;word-wrap:break-word}.simcal-default-calendar-list .simcal-event-details{font-size:.84em;line-height:1.5;overflow:hidden}.simcal-default-calendar-list .simcal-event-details p{margin:0 0 1em}.simcal-default-calendar-list .simcal-events-toggle{display:inline-block;padding:0}.simcal-default-calendar-list.simcal-default-calendar-light .simcal-events-toggle{background-color:rgba(0,0,0,.1)}.simcal-default-calendar-list.simcal-default-calendar-dark .simcal-events-toggle{background-color:rgba(255,255,255,.1)}
assets/css/vendor/qtip.css ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * qTip2 - Pretty powerful tooltips - v2.2.1
3
+ * http://qtip2.com
4
+ *
5
+ * Copyright (c) 2014
6
+ * Released under the MIT licenses
7
+ * http://jquery.org/license
8
+ *
9
+ * Date: Sun Sep 7 2014 12:09 GMT+0100+0100
10
+ * Plugins: None
11
+ * Styles: core
12
+ */
13
+ .qtip{
14
+ position: absolute;
15
+ left: -28000px;
16
+ top: -28000px;
17
+ display: none;
18
+
19
+ max-width: 280px;
20
+ min-width: 50px;
21
+
22
+ font-size: 10.5px;
23
+ line-height: 12px;
24
+
25
+ direction: ltr;
26
+
27
+ box-shadow: none;
28
+ padding: 0;
29
+ }
30
+
31
+ .qtip-content{
32
+ position: relative;
33
+ padding: 5px 9px;
34
+ overflow: hidden;
35
+
36
+ text-align: left;
37
+ word-wrap: break-word;
38
+ }
39
+
40
+ .qtip-titlebar{
41
+ position: relative;
42
+ padding: 5px 35px 5px 10px;
43
+ overflow: hidden;
44
+
45
+ border-width: 0 0 1px;
46
+ font-weight: bold;
47
+ }
48
+
49
+ .qtip-titlebar + .qtip-content{ border-top-width: 0 !important; }
50
+
51
+ /* Default close button class */
52
+ .qtip-close{
53
+ position: absolute;
54
+ right: -9px; top: -9px;
55
+ z-index: 11; /* Overlap .qtip-tip */
56
+
57
+ cursor: pointer;
58
+ outline: medium none;
59
+
60
+ border: 1px solid transparent;
61
+ }
62
+
63
+ .qtip-titlebar .qtip-close{
64
+ right: 4px; top: 50%;
65
+ margin-top: -9px;
66
+ }
67
+
68
+ * html .qtip-titlebar .qtip-close{ top: 16px; } /* IE fix */
69
+
70
+ .qtip-titlebar .ui-icon,
71
+ .qtip-icon .ui-icon{
72
+ display: block;
73
+ text-indent: -1000em;
74
+ direction: ltr;
75
+ }
76
+
77
+ .qtip-icon, .qtip-icon .ui-icon{
78
+ -moz-border-radius: 3px;
79
+ -webkit-border-radius: 3px;
80
+ border-radius: 3px;
81
+ text-decoration: none;
82
+ }
83
+
84
+ .qtip-icon .ui-icon{
85
+ width: 18px;
86
+ height: 14px;
87
+
88
+ line-height: 14px;
89
+ text-align: center;
90
+ text-indent: 0;
91
+ font: normal bold 10px/13px Tahoma,sans-serif;
92
+
93
+ color: inherit;
94
+ background: transparent none no-repeat -100em -100em;
95
+ }
96
+
97
+ /* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */
98
+ .qtip-focus{}
99
+
100
+ /* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */
101
+ .qtip-hover{}
102
+
103
+ /* Default tooltip style */
104
+ .qtip-default{
105
+ border: 1px solid #F1D031;
106
+
107
+ background-color: #FFFFA3;
108
+ color: #555;
109
+ }
110
+
111
+ .qtip-default .qtip-titlebar{
112
+ background-color: #FFEF93;
113
+ }
114
+
115
+ .qtip-default .qtip-icon{
116
+ border-color: #CCC;
117
+ background: #F1F1F1;
118
+ color: #777;
119
+ }
120
+
121
+ .qtip-default .qtip-titlebar .qtip-close{
122
+ border-color: #AAA;
123
+ color: #111;
124
+ }
assets/css/vendor/qtip.min.css ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ /* qTip2 v2.2.1 | Plugins: None | Styles: core | qtip2.com | Licensed MIT | Sun Sep 07 2014 00:09:32 */
2
+
3
+ .qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;direction:ltr;box-shadow:none;padding:0}.qtip-content{position:relative;padding:5px 9px;overflow:hidden;text-align:left;word-wrap:break-word}.qtip-titlebar{position:relative;padding:5px 35px 5px 10px;overflow:hidden;border-width:0 0 1px;font-weight:700}.qtip-titlebar+.qtip-content{border-top-width:0!important}.qtip-close{position:absolute;right:-9px;top:-9px;z-index:11;cursor:pointer;outline:0;border:1px solid transparent}.qtip-titlebar .qtip-close{right:4px;top:50%;margin-top:-9px}* html .qtip-titlebar .qtip-close{top:16px}.qtip-icon .ui-icon,.qtip-titlebar .ui-icon{display:block;text-indent:-1000em;direction:ltr}.qtip-icon,.qtip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none}.qtip-icon .ui-icon{width:18px;height:14px;line-height:14px;text-align:center;text-indent:0;font:400 bold 10px/13px Tahoma,sans-serif;color:inherit;background:-100em -100em no-repeat}.qtip-default{border:1px solid #F1D031;background-color:#FFFFA3;color:#555}.qtip-default .qtip-titlebar{background-color:#FFEF93}.qtip-default .qtip-icon{border-color:#CCC;background:#F1F1F1;color:#777}.qtip-default .qtip-titlebar .qtip-close{border-color:#AAA;color:#111}
assets/css/vendor/select2.css ADDED
@@ -0,0 +1,431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .select2-container {
2
+ box-sizing: border-box;
3
+ display: inline-block;
4
+ margin: 0;
5
+ position: relative;
6
+ vertical-align: middle; }
7
+ .select2-container .select2-selection--single {
8
+ box-sizing: border-box;
9
+ cursor: pointer;
10
+ display: block;
11
+ height: 28px;
12
+ user-select: none;
13
+ -webkit-user-select: none; }
14
+ .select2-container .select2-selection--single .select2-selection__rendered {
15
+ display: block;
16
+ padding-left: 8px;
17
+ padding-right: 20px;
18
+ overflow: hidden;
19
+ text-overflow: ellipsis;
20
+ white-space: nowrap; }
21
+ .select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
22
+ padding-right: 8px;
23
+ padding-left: 20px; }
24
+ .select2-container .select2-selection--multiple {
25
+ box-sizing: border-box;
26
+ cursor: pointer;
27
+ display: block;
28
+ min-height: 32px;
29
+ user-select: none;
30
+ -webkit-user-select: none; }
31
+ .select2-container .select2-selection--multiple .select2-selection__rendered {
32
+ display: inline-block;
33
+ overflow: hidden;
34
+ padding-left: 8px;
35
+ text-overflow: ellipsis;
36
+ white-space: nowrap; }
37
+ .select2-container .select2-search--inline {
38
+ float: left; }
39
+ .select2-container .select2-search--inline .select2-search__field {
40
+ box-sizing: border-box;
41
+ border: none;
42
+ font-size: 100%;
43
+ margin-top: 5px; }
44
+ .select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
45
+ -webkit-appearance: none; }
46
+
47
+ .select2-dropdown {
48
+ background-color: white;
49
+ border: 1px solid #aaa;
50
+ border-radius: 4px;
51
+ box-sizing: border-box;
52
+ display: block;
53
+ position: absolute;
54
+ left: -100000px;
55
+ width: 100%;
56
+ z-index: 1051; }
57
+
58
+ .select2-results {
59
+ display: block; }
60
+
61
+ .select2-results__options {
62
+ list-style: none;
63
+ margin: 0;
64
+ padding: 0; }
65
+
66
+ .select2-results__option {
67
+ padding: 6px;
68
+ user-select: none;
69
+ -webkit-user-select: none; }
70
+ .select2-results__option[aria-selected] {
71
+ cursor: pointer; }
72
+
73
+ .select2-container--open .select2-dropdown {
74
+ left: 0; }
75
+
76
+ .select2-container--open .select2-dropdown--above {
77
+ border-bottom: none;
78
+ border-bottom-left-radius: 0;
79
+ border-bottom-right-radius: 0; }
80
+
81
+ .select2-container--open .select2-dropdown--below {
82
+ border-top: none;
83
+ border-top-left-radius: 0;
84
+ border-top-right-radius: 0; }
85
+
86
+ .select2-search--dropdown {
87
+ display: block;
88
+ padding: 4px; }
89
+ .select2-search--dropdown .select2-search__field {
90
+ padding: 4px;
91
+ width: 100%;
92
+ box-sizing: border-box; }
93
+ .select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
94
+ -webkit-appearance: none; }
95
+ .select2-search--dropdown.select2-search--hide {
96
+ display: none; }
97
+
98
+ .select2-close-mask {
99
+ border: 0;
100
+ margin: 0;
101
+ padding: 0;
102
+ display: block;
103
+ position: fixed;
104
+ left: 0;
105
+ top: 0;
106
+ min-height: 100%;
107
+ min-width: 100%;
108
+ height: auto;
109
+ width: auto;
110
+ opacity: 0;
111
+ z-index: 99;
112
+ background-color: #fff;
113
+ filter: alpha(opacity=0); }
114
+
115
+ .select2-hidden-accessible {
116
+ border: 0;
117
+ clip: rect(0 0 0 0);
118
+ height: 1px;
119
+ margin: -1px;
120
+ overflow: hidden;
121
+ padding: 0;
122
+ position: absolute;
123
+ width: 1px; }
124
+
125
+ .select2-container--default .select2-selection--single {
126
+ background-color: #fff;
127
+ border: 1px solid #aaa;
128
+ border-radius: 4px; }
129
+ .select2-container--default .select2-selection--single .select2-selection__rendered {
130
+ color: #444;
131
+ line-height: 28px; }
132
+ .select2-container--default .select2-selection--single .select2-selection__clear {
133
+ cursor: pointer;
134
+ float: right;
135
+ font-weight: bold; }
136
+ .select2-container--default .select2-selection--single .select2-selection__placeholder {
137
+ color: #999; }
138
+ .select2-container--default .select2-selection--single .select2-selection__arrow {
139
+ height: 26px;
140
+ position: absolute;
141
+ top: 1px;
142
+ right: 1px;
143
+ width: 20px; }
144
+ .select2-container--default .select2-selection--single .select2-selection__arrow b {
145
+ border-color: #888 transparent transparent transparent;
146
+ border-style: solid;
147
+ border-width: 5px 4px 0 4px;
148
+ height: 0;
149
+ left: 50%;
150
+ margin-left: -4px;
151
+ margin-top: -2px;
152
+ position: absolute;
153
+ top: 50%;
154
+ width: 0; }
155
+ .select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
156
+ float: left; }
157
+ .select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
158
+ left: 1px;
159
+ right: auto; }
160
+ .select2-container--default.select2-container--disabled .select2-selection--single {
161
+ background-color: #eee;
162
+ cursor: default; }
163
+ .select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
164
+ display: none; }
165
+ .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
166
+ border-color: transparent transparent #888 transparent;
167
+ border-width: 0 4px 5px 4px; }
168
+ .select2-container--default .select2-selection--multiple {
169
+ background-color: white;
170
+ border: 1px solid #aaa;
171
+ border-radius: 4px;
172
+ cursor: text; }
173
+ .select2-container--default .select2-selection--multiple .select2-selection__rendered {
174
+ box-sizing: border-box;
175
+ list-style: none;
176
+ margin: 0;
177
+ padding: 0 5px;
178
+ width: 100%; }
179
+ .select2-container--default .select2-selection--multiple .select2-selection__placeholder {
180
+ color: #999;
181
+ margin-top: 5px;
182
+ float: left; }
183
+ .select2-container--default .select2-selection--multiple .select2-selection__clear {
184
+ cursor: pointer;
185
+ float: right;
186
+ font-weight: bold;
187
+ margin-top: 5px;
188
+ margin-right: 10px; }
189
+ .select2-container--default .select2-selection--multiple .select2-selection__choice {
190
+ background-color: #e4e4e4;
191
+ border: 1px solid #aaa;
192
+ border-radius: 4px;
193
+ cursor: default;
194
+ float: left;
195
+ margin-right: 5px;
196
+ margin-top: 5px;
197
+ padding: 0 5px; }
198
+ .select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
199
+ color: #999;
200
+ cursor: pointer;
201
+ display: inline-block;
202
+ font-weight: bold;
203
+ margin-right: 2px; }
204
+ .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
205
+ color: #333; }
206
+ .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder {
207
+ float: right; }
208
+ .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
209
+ margin-left: 5px;
210
+ margin-right: auto; }
211
+ .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
212
+ margin-left: 2px;
213
+ margin-right: auto; }
214
+ .select2-container--default.select2-container--focus .select2-selection--multiple {
215
+ border: solid black 1px;
216
+ outline: 0; }
217
+ .select2-container--default.select2-container--disabled .select2-selection--multiple {
218
+ background-color: #eee;
219
+ cursor: default; }
220
+ .select2-container--default.select2-container--disabled .select2-selection__choice__remove {
221
+ display: none; }
222
+ .select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
223
+ border-top-left-radius: 0;
224
+ border-top-right-radius: 0; }
225
+ .select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
226
+ border-bottom-left-radius: 0;
227
+ border-bottom-right-radius: 0; }
228
+ .select2-container--default .select2-search--dropdown .select2-search__field {
229
+ border: 1px solid #aaa; }
230
+ .select2-container--default .select2-search--inline .select2-search__field {
231
+ background: transparent;
232
+ border: none;
233
+ outline: 0; }
234
+ .select2-container--default .select2-results > .select2-results__options {
235
+ max-height: 200px;
236
+ overflow-y: auto; }
237
+ .select2-container--default .select2-results__option[role=group] {
238
+ padding: 0; }
239
+ .select2-container--default .select2-results__option[aria-disabled=true] {
240
+ color: #999; }
241
+ .select2-container--default .select2-results__option[aria-selected=true] {
242
+ background-color: #ddd; }
243
+ .select2-container--default .select2-results__option .select2-results__option {
244
+ padding-left: 1em; }
245
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__group {
246
+ padding-left: 0; }
247
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__option {
248
+ margin-left: -1em;
249
+ padding-left: 2em; }
250
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
251
+ margin-left: -2em;
252
+ padding-left: 3em; }
253
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
254
+ margin-left: -3em;
255
+ padding-left: 4em; }
256
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
257
+ margin-left: -4em;
258
+ padding-left: 5em; }
259
+ .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
260
+ margin-left: -5em;
261
+ padding-left: 6em; }
262
+ .select2-container--default .select2-results__option--highlighted[aria-selected] {
263
+ background-color: #5897fb;
264
+ color: white; }
265
+ .select2-container--default .select2-results__group {
266
+ cursor: default;
267
+ display: block;
268
+ padding: 6px; }
269
+
270
+ .select2-container--classic .select2-selection--single {
271
+ background-color: #f6f6f6;
272
+ border: 1px solid #aaa;
273
+ border-radius: 4px;
274
+ outline: 0;
275
+ background-image: -webkit-linear-gradient(top, #ffffff 50%, #eeeeee 100%);
276
+ background-image: -o-linear-gradient(top, #ffffff 50%, #eeeeee 100%);
277
+ background-image: linear-gradient(to bottom, #ffffff 50%, #eeeeee 100%);
278
+ background-repeat: repeat-x;
279
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0); }
280
+ .select2-container--classic .select2-selection--single:focus {
281
+ border: 1px solid #5897fb; }
282
+ .select2-container--classic .select2-selection--single .select2-selection__rendered {
283
+ color: #444;
284
+ line-height: 28px; }
285
+ .select2-container--classic .select2-selection--single .select2-selection__clear {
286
+ cursor: pointer;
287
+ float: right;
288
+ font-weight: bold;
289
+ margin-right: 10px; }
290
+ .select2-container--classic .select2-selection--single .select2-selection__placeholder {
291
+ color: #999; }
292
+ .select2-container--classic .select2-selection--single .select2-selection__arrow {
293
+ background-color: #ddd;
294
+ border: none;
295
+ border-left: 1px solid #aaa;
296
+ border-top-right-radius: 4px;
297
+ border-bottom-right-radius: 4px;
298
+ height: 26px;
299
+ position: absolute;
300
+ top: 1px;
301
+ right: 1px;
302
+ width: 20px;
303
+ background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
304
+ background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
305
+ background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
306
+ background-repeat: repeat-x;
307
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#cccccc', GradientType=0); }
308
+ .select2-container--classic .select2-selection--single .select2-selection__arrow b {
309
+ border-color: #888 transparent transparent transparent;
310
+ border-style: solid;
311
+ border-width: 5px 4px 0 4px;
312
+ height: 0;
313
+ left: 50%;
314
+ margin-left: -4px;
315
+ margin-top: -2px;
316
+ position: absolute;
317
+ top: 50%;
318
+ width: 0; }
319
+ .select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
320
+ float: left; }
321
+ .select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
322
+ border: none;
323
+ border-right: 1px solid #aaa;
324
+ border-radius: 0;
325
+ border-top-left-radius: 4px;
326
+ border-bottom-left-radius: 4px;
327
+ left: 1px;
328
+ right: auto; }
329
+ .select2-container--classic.select2-container--open .select2-selection--single {
330
+ border: 1px solid #5897fb; }
331
+ .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
332
+ background: transparent;
333
+ border: none; }
334
+ .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
335
+ border-color: transparent transparent #888 transparent;
336
+ border-width: 0 4px 5px 4px; }
337
+ .select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
338
+ border-top: none;
339
+ border-top-left-radius: 0;
340
+ border-top-right-radius: 0;
341
+ background-image: -webkit-linear-gradient(top, #ffffff 0%, #eeeeee 50%);
342
+ background-image: -o-linear-gradient(top, #ffffff 0%, #eeeeee 50%);
343
+ background-image: linear-gradient(to bottom, #ffffff 0%, #eeeeee 50%);
344
+ background-repeat: repeat-x;
345
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0); }
346
+ .select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
347
+ border-bottom: none;
348
+ border-bottom-left-radius: 0;
349
+ border-bottom-right-radius: 0;
350
+ background-image: -webkit-linear-gradient(top, #eeeeee 50%, #ffffff 100%);
351
+ background-image: -o-linear-gradient(top, #eeeeee 50%, #ffffff 100%);
352
+ background-image: linear-gradient(to bottom, #eeeeee 50%, #ffffff 100%);
353
+ background-repeat: repeat-x;
354
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0); }
355
+ .select2-container--classic .select2-selection--multiple {
356
+ background-color: white;
357
+ border: 1px solid #aaa;
358
+ border-radius: 4px;
359
+ cursor: text;
360
+ outline: 0; }
361
+ .select2-container--classic .select2-selection--multiple:focus {
362
+ border: 1px solid #5897fb; }
363
+ .select2-container--classic .select2-selection--multiple .select2-selection__rendered {
364
+ list-style: none;
365
+ margin: 0;
366
+ padding: 0 5px; }
367
+ .select2-container--classic .select2-selection--multiple .select2-selection__clear {
368
+ display: none; }
369
+ .select2-container--classic .select2-selection--multiple .select2-selection__choice {
370
+ background-color: #e4e4e4;
371
+ border: 1px solid #aaa;
372
+ border-radius: 4px;
373
+ cursor: default;
374
+ float: left;
375
+ margin-right: 5px;
376
+ margin-top: 5px;
377
+ padding: 0 5px; }
378
+ .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
379
+ color: #888;
380
+ cursor: pointer;
381
+ display: inline-block;
382
+ font-weight: bold;
383
+ margin-right: 2px; }
384
+ .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
385
+ color: #555; }
386
+ .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
387
+ float: right; }
388
+ .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
389
+ margin-left: 5px;
390
+ margin-right: auto; }
391
+ .select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
392
+ margin-left: 2px;
393
+ margin-right: auto; }
394
+ .select2-container--classic.select2-container--open .select2-selection--multiple {
395
+ border: 1px solid #5897fb; }
396
+ .select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
397
+ border-top: none;
398
+ border-top-left-radius: 0;
399
+ border-top-right-radius: 0; }
400
+ .select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
401
+ border-bottom: none;
402
+ border-bottom-left-radius: 0;
403
+ border-bottom-right-radius: 0; }
404
+ .select2-container--classic .select2-search--dropdown .select2-search__field {
405
+ border: 1px solid #aaa;
406
+ outline: 0; }
407
+ .select2-container--classic .select2-search--inline .select2-search__field {
408
+ outline: 0; }
409
+ .select2-container--classic .select2-dropdown {
410
+ background-color: white;
411
+ border: 1px solid transparent; }
412
+ .select2-container--classic .select2-dropdown--above {
413
+ border-bottom: none; }
414
+ .select2-container--classic .select2-dropdown--below {
415
+ border-top: none; }
416
+ .select2-container--classic .select2-results > .select2-results__options {
417
+ max-height: 200px;
418
+ overflow-y: auto; }
419
+ .select2-container--classic .select2-results__option[role=group] {
420
+ padding: 0; }
421
+ .select2-container--classic .select2-results__option[aria-disabled=true] {
422
+ color: grey; }
423
+ .select2-container--classic .select2-results__option--highlighted[aria-selected] {
424
+ background-color: #3875d7;
425
+ color: white; }
426
+ .select2-container--classic .select2-results__group {
427
+ cursor: default;
428
+ display: block;
429
+ padding: 6px; }
430
+ .select2-container--classic.select2-container--open .select2-dropdown {
431
+ border-color: #5897fb; }
assets/css/vendor/select2.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle;}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none;}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px;}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none;}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap;}.select2-container .select2-search--inline{float:left;}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none;}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051;}.select2-results{display:block;}.select2-results__options{list-style:none;margin:0;padding:0;}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none;}.select2-results__option[aria-selected]{cursor:pointer;}.select2-container--open .select2-dropdown{left:0;}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0;}.select2-search--dropdown{display:block;padding:4px;}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box;}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none;}.select2-search--dropdown.select2-search--hide{display:none;}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0);}.select2-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px;}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px;}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999;}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px;}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0;}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left;}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto;}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default;}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none;}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px;}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%;}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left;}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px;}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px;}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px;}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder{float:right;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto;}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto;}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0;}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default;}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none;}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0;}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa;}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto;}.select2-container--default .select2-results__option[role=group]{padding:0;}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999;}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd;}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em;}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em;}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white;}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px;}.select2-container--classic .select2-selection--single{background-color:#f6f6f6;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #ffffff 50%, #eeeeee 100%);background-image:-o-linear-gradient(top, #ffffff 50%, #eeeeee 100%);background-image:linear-gradient(to bottom, #ffffff 50%, #eeeeee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb;}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px;}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px;}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999;}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);background-image:-o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);background-image:linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#cccccc', GradientType=0);}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0;}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left;}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto;}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb;}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none;}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px;}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #ffffff 0%, #eeeeee 50%);background-image:-o-linear-gradient(top, #ffffff 0%, #eeeeee 50%);background-image:linear-gradient(to bottom, #ffffff 0%, #eeeeee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eeeeee 50%, #ffffff 100%);background-image:-o-linear-gradient(top, #eeeeee 50%, #ffffff 100%);background-image:linear-gradient(to bottom, #eeeeee 50%, #ffffff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0;}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb;}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px;}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none;}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px;}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px;}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto;}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto;}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb;}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0;}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0;}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;}.select2-container--classic .select2-dropdown{background-color:white;border:1px solid transparent;}.select2-container--classic .select2-dropdown--above{border-bottom:none;}.select2-container--classic .select2-dropdown--below{border-top:none;}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto;}.select2-container--classic .select2-results__option[role=group]{padding:0;}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey;}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:white;}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px;}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb;}
assets/fonts/simple-calendar.eot ADDED
Binary file
assets/fonts/simple-calendar.svg ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <metadata>Copyright (C) 2015 by original authors @ fontello.com</metadata>
5
+ <defs>
6
+ <font id="simple-calendar" horiz-adv-x="1000" >
7
+ <font-face font-family="simple-calendar" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
8
+ <missing-glyph horiz-adv-x="1000" />
9
+ <glyph glyph-name="calendar-empty" unicode="&#xe800;" d="m71-79h786v572h-786v-572z m215 679v161q0 8-5 13t-13 5h-36q-8 0-13-5t-5-13v-161q0-8 5-13t13-5h36q8 0 13 5t5 13z m428 0v161q0 8-5 13t-13 5h-35q-8 0-13-5t-5-13v-161q0-8 5-13t13-5h35q8 0 13 5t5 13z m215 36v-715q0-29-22-50t-50-21h-786q-29 0-50 21t-21 50v715q0 29 21 50t50 21h72v54q0 36 26 63t63 26h36q37 0 63-26t26-63v-54h214v54q0 36 27 63t63 26h35q37 0 63-26t27-63v-54h71q29 0 50-21t22-50z" horiz-adv-x="928.6" />
10
+ <glyph glyph-name="calendar" unicode="&#xe801;" d="m71-79h161v161h-161v-161z m197 0h178v161h-178v-161z m-197 197h161v178h-161v-178z m197 0h178v178h-178v-178z m-197 214h161v161h-161v-161z m411-411h179v161h-179v-161z m-214 411h178v161h-178v-161z m428-411h161v161h-161v-161z m-214 197h179v178h-179v-178z m-196 482v161q0 7-6 12t-12 6h-36q-7 0-12-6t-6-12v-161q0-7 6-13t12-5h36q7 0 12 5t6 13z m410-482h161v178h-161v-178z m-214 214h179v161h-179v-161z m214 0h161v161h-161v-161z m18 268v161q0 7-5 12t-13 6h-35q-8 0-13-6t-5-12v-161q0-7 5-13t13-5h35q8 0 13 5t5 13z m215 36v-715q0-29-22-50t-50-21h-786q-29 0-50 21t-21 50v715q0 29 21 50t50 21h72v54q0 36 26 63t63 26h36q37 0 63-26t26-63v-54h214v54q0 36 27 63t63 26h35q37 0 63-26t27-63v-54h71q29 0 50-21t22-50z" horiz-adv-x="928.6" />
11
+ <glyph glyph-name="calendar-logo" unicode="&#xe802;" d="m0-150l0 649 893 0 0-649-893 0z m0 705l0 221 109 0 0-141 200 0 0 141 275 0 0-141 199 0 0 141 110 0 0-221-893 0z m168 139l0 156 82 0 0-156-82 0z m59-619q0-112 123-112 47 0 84 32 39 31 39 80 0 68-78 90 48 15 64 48 12 28-2 73-27 62-107 62-51 0-86-26t-37-77l72 0q0 45 49 46 43 0 45-52 0-49-84-47l0-57q48 0 68-8 23-11 23-46 0-57-54-61-43 0-47 55l-72 0z m281 146q49 14 88 47l0-297 70 0 0 371-64 0q-38-37-94-58l0-63z m135 473l0 156 82 0 0-156-82 0z" horiz-adv-x="893" />
12
+ <glyph glyph-name="left-open" unicode="&#xe803;" d="m653 682l-296-296 296-297q11-10 11-25t-11-25l-92-93q-11-10-25-10t-25 10l-414 415q-11 10-11 25t11 25l414 414q10 10 25 10t25-10l92-93q11-10 11-25t-11-25z" horiz-adv-x="714.3" />
13
+ <glyph glyph-name="settings" unicode="&#xe804;" d="m500 350q0 59-42 101t-101 42-101-42-42-101 42-101 101-42 101 42 42 101z m429-286q0 29-22 51t-50 21-50-21-21-51q0-29 21-50t50-21 51 21 21 50z m0 572q0 29-22 50t-50 21-50-21-21-50q0-30 21-51t50-21 51 21 21 51z m-215-235v-103q0-6-4-11t-9-6l-86-14q-6-19-18-42 19-27 50-64 4-6 4-11 0-7-4-11-13-17-46-50t-44-33q-6 0-11 4l-64 50q-21-11-43-17-6-60-13-87-4-13-17-13h-104q-6 0-11 4t-5 10l-13 85q-19 6-42 18l-66-50q-4-4-11-4-6 0-12 4-80 75-80 90 0 5 4 10 5 8 23 30t26 34q-13 24-20 46l-85 13q-5 1-9 5t-4 11v103q0 6 4 11t9 6l86 14q7 19 18 42-19 27-50 64-4 6-4 11 0 7 4 11 12 17 46 50t44 33q6 0 12-4l64-50q19 10 43 18 6 60 13 86 3 13 16 13h104q6 0 11-4t6-10l13-85q19-6 41-17l66 49q5 4 11 4 7 0 12-4 81-75 81-90 0-5-4-10-7-9-24-30t-25-34q13-27 19-46l85-12q5-2 9-6t4-11z m357-298v-78q0-9-83-17-6-15-16-29 28-63 28-77 0-2-2-4-68-40-69-40-5 0-26 27t-29 37q-11-1-17-1t-17 1q-7-11-29-37t-25-27q-1 0-69 40-3 2-3 4 0 14 29 77-10 14-17 29-83 8-83 17v78q0 9 83 18 7 16 17 29-29 63-29 77 0 2 3 4 2 1 19 11t33 19 17 9q4 0 25-26t29-38q12 1 17 1t17-1q28 40 51 63l4 1q2 0 69-39 2-2 2-4 0-14-28-77 9-13 16-29 83-9 83-18z m0 572v-78q0-9-83-18-6-15-16-29 28-63 28-77 0-2-2-4-68-39-69-39-5 0-26 26t-29 38q-11-1-17-1t-17 1q-7-12-29-38t-25-26q-1 0-69 39-3 2-3 4 0 14 29 77-10 14-17 29-83 9-83 18v78q0 9 83 17 7 16 17 29-29 63-29 77 0 2 3 4 2 1 19 11t33 19 17 9q4 0 25-26t29-38q12 2 17 2t17-2q28 40 51 63l4 1q2 0 69-39 2-2 2-4 0-14-28-77 9-13 16-29 83-8 83-17z" horiz-adv-x="1071.4" />
14
+ <glyph glyph-name="toggles" unicode="&#xe805;" d="m196 64v-71h-196v71h196z m197 72q14 0 25-11t11-25v-143q0-14-11-25t-25-11h-143q-14 0-25 11t-11 25v143q0 15 11 25t25 11h143z m89 214v-71h-482v71h482z m-357 286v-72h-125v72h125z m732-572v-71h-411v71h411z m-536 643q15 0 26-10t10-26v-142q0-15-10-26t-26-10h-142q-15 0-26 10t-10 26v142q0 15 10 26t26 10h142z m358-286q14 0 25-10t10-25v-143q0-15-10-25t-25-11h-143q-15 0-25 11t-11 25v143q0 14 11 25t25 10h143z m178-71v-71h-125v71h125z m0 286v-72h-482v72h482z" horiz-adv-x="857.1" />
15
+ <glyph glyph-name="list" unicode="&#xe806;" d="m650 400q22 0 36-15t14-35-15-35-35-15l-600 0q-20 0-35 15t-15 35 14 35 36 15l600 0z m-600 100q-20 0-35 15t-15 35 14 35 36 15l600 0q22 0 36-15t14-35-15-35-35-15l-600 0z m600-300q22 0 36-15t14-35-15-35-35-15l-600 0q-20 0-35 15t-15 35 14 35 36 15l600 0z" horiz-adv-x="700" />
16
+ <glyph glyph-name="event" unicode="&#xe807;" d="m460 810q190 0 325-135t135-325-135-325-325-135-325 135-135 325 135 325 325 135z m0-820q150 0 255 106t105 254q0 150-105 255t-255 105q-148 0-254-105t-106-255q0-148 106-254t254-106z m36 620l0-244 150-150-50-50-170 170 0 274 70 0z" horiz-adv-x="920" />
17
+ <glyph glyph-name="help" unicode="&#xe808;" d="m0 350q0 95 37 182t100 149 149 100 183 37q95 0 182-37t149-100 100-149 37-182q0-95-37-182t-100-150-149-100-182-37q-96 0-183 37t-149 100-100 150-37 182z m117 0q0-71 28-137t75-112 112-75 137-28 136 28 112 75 75 112 28 137q0 95-47 176t-128 128-176 47-177-47-128-128-47-176z m216 176l31-101q45 29 93 29 49 0 49-23 0-4-2-8t-3-5-6-5-7-5-8-4l-8-4-40-21q-22-12-28-25t-6-37v-37h117v16q0 7 0 10t3 6 6 5q1 1 11 5t17 8 19 11 18 13q19 17 29 37t11 55q0 49-42 83t-106 34q-83 0-148-37z m48-334q0-32 21-49t55-17q33 0 54 17t20 49-20 49-54 18q-35 0-55-18t-21-49z" horiz-adv-x="937.5" />
18
+ <glyph glyph-name="right-open" unicode="&#xe809;" d="m618 361l-414-415q-11-10-25-10t-26 10l-92 93q-11 11-11 25t11 25l296 297-296 296q-11 11-11 25t11 25l92 93q11 10 26 10t25-10l414-414q10-11 10-25t-10-25z" horiz-adv-x="714.3" />
19
+ <glyph glyph-name="panel" unicode="&#xe80a;" d="m311 504l489 0c36 0 65-30 65-65l0-404c0-35-29-65-65-65l-489 0c-35 0-65 30-65 65l0 404c0 35 30 65 65 65z m454-94l-419 0 0-346 419 0 0 346z m-146 181l-101 0 0 44-417 0 0-336 58 0 0-95-93 0c-36 0-66 30-66 65l0 395c0 36 30 66 66 66l488 0c36 0 65-30 65-66l0-73z" horiz-adv-x="865" />
20
+ <glyph glyph-name="grid" unicode="&#xe80b;" d="m0-22l0 744 970 0 0-744-970 0z m519 452l0-159 159 0 0 159-159 0z m0-384l159 0 0 157-159 0 0-157z m159 609l-159 0 0-158 159 0 0 158z m225-384l0 159-158 0 0-159 158 0z m-158-225l158 0 0 157-158 0 0-157z m158 451l0 158-158 0 0-158 158 0z m-835-67l0-159 158 0 0 159-158 0z m0-384l158 0 0 157-158 0 0-157z m158 609l-158 0 0-158 158 0 0 158z m225-384l0 159-157 0 0-159 157 0z m-157-225l157 0 0 157-157 0 0-157z m157 451l0 158-157 0 0-158 157 0z" horiz-adv-x="970" />
21
+ <glyph glyph-name="google" unicode="&#xe80c;" d="m547 103q0 14-3 27t-9 24-15 23-16 19-22 20-20 16-23 16-20 15q-9 1-28 1-29 0-58-4t-60-14-54-25-38-42-15-59q0-31 13-57t34-42 48-28 56-16 57-5q32 0 62 7t55 22 41 41 15 61z m-65 479q0 33-9 70t-27 72-47 57-65 23q-24 0-46-11t-37-29q-26-33-26-89 0-26 6-55t17-57 29-52 42-37 54-15q20 0 43 10t37 24q29 31 29 89z m-62 268h232l-76-49h-74q42-35 63-74t22-90q0-40-14-72t-33-52-39-36-33-34-14-37q0-20 18-40t43-38 51-41 43-58 18-79q0-51-28-96-39-68-117-101t-166-32q-74 0-138 24t-96 76q-20 33-20 73 0 46 25 84t66 64q73 46 225 56-17 23-26 41t-9 41q0 22 12 47-26-2-38-2-83 0-139 54t-57 137q0 45 20 88t55 73q43 37 102 55t122 18z" horiz-adv-x="714.3" />
22
+ <glyph glyph-name="down-open" unicode="&#xe80d;" d="m939 399l-414-413q-10-11-25-11t-25 11l-414 413q-11 11-11 26t11 25l92 92q11 11 26 11t25-11l296-296 296 296q11 11 25 11t26-11l92-92q11-11 11-25t-11-26z" horiz-adv-x="1000" />
23
+ <glyph glyph-name="up-open" unicode="&#xe80e;" d="m939 107l-92-92q-11-10-26-10t-25 10l-296 297-296-297q-11-10-25-10t-26 10l-92 92q-11 11-11 26t11 25l414 414q11 10 25 10t25-10l414-414q11-11 11-25t-11-26z" horiz-adv-x="1000" />
24
+ <glyph glyph-name="docs" unicode="&#xe80f;" d="m970 480q38-10 30-46l-150-556q-4-16-18-23t-30-3l-406 110q-16 4-24 18t-4 28l24 92-180-48q-40-10-50 26l-160 602q-10 36 28 48l454 122q16 4 30-3t18-23l66-244z m-888 190l144-542 392 106-144 540z m702-742l132 492-298 82 76-282q10-34-28-46l-196-52-26-102z" horiz-adv-x="1001" />
25
+ <glyph glyph-name="spin3" unicode="&#xe810;" d="m494 850c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" />
26
+ <glyph glyph-name="hourglass" unicode="&#xe811;" d="m0-37v70q0 22 12 49t24 46 38 55l24 33t26 36 22 33 18 35 5 30-5 30-18 35-22 33-26 36l-24 32q-26 37-38 55t-24 46-12 49v70q0 34 24 58t59 24h419q35 0 59-24t25-58v-70q0-22-12-49t-24-46-38-55l-24-32t-27-36-22-33-17-36-6-29 6-30 17-35 22-33 27-37l24-32q25-36 38-55t24-46 12-49v-70q0-34-25-58t-59-24h-419q-34 0-59 24t-24 58z m100 35h40q0 9 2 17t9 18 12 16 17 18 19 16 22 17 23 16 25 18 24 17q8-6 26-19t28-19 25-19 25-19 19-19 16-19 10-19 4-20h40v35q0 7-14 31-8 14-42 63-53 75-71 108-34 64-34 115 0 26 7 54t25 57 30 50 36 51l7 10q33 47 42 64 6 9 9 17t4 11l1 2v35h-386v-35q1-8 13-30 10-17 43-64 53-75 71-108 34-63 34-114 0-27-8-54t-25-58-29-49-36-52q-1-1-2-2t-2-2-2-3-1-3q-34-49-43-63-13-24-13-31v-35z" horiz-adv-x="585.9" />
27
+ <glyph glyph-name="globe" unicode="&#xe812;" d="m429 779q116 0 215-58t156-156 57-215-57-215-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58z m152-291q-1-1-5-5t-7-6q1 0 2 3t3 6 2 4q3 4 12 8 8 4 29 7 19 5 29-6-1 1 5 7t8 7q2 1 8 2t9 5l1 12q-7-1-10 4t-3 12q0-2-4-5 0 4-2 5t-7-1-5-1q-5 2-8 5t-5 9-2 8q-1 3-5 6t-5 6q-1 1-2 3t-2 4-2 3-3 1-4-3-4-5-2-3q-2 1-4 1t-2-1-3-2-3-2q-1-1-4-1t-5-1q8 3-1 6-5 2-9 1 5 3 5 7t-5 8h3q-1 2-5 5t-10 4-7 4q-5 3-19 5t-18 0q-3-3-3-5t2-8 2-7q1-4-3-7t-3-7q0-4 7-9t6-12q-2-4-9-9t-9-6q-3-5-1-11t6-9q1-1 1-2t-2-3-3-2-4-2l-1-1q-7-3-12 3t-7 15q-4 14-9 17-13 4-17-1-2 7-22 15-14 5-33 2 4 0 0 8-4 9-10 7 1 3 2 10t0 7q2 7 7 13 1 1 4 5t5 7 1 4q19-3 28 6 2 2 6 9t6 10q5 3 8 3t8-3 8-3q8-1 8 6t-4 11q7 0 2 10-3 4-5 5-6 2-15-3-4-2 2-4-1 0-6-6t-9-10-9 3q0 0-3 7t-5 8q-5 0-9-9 1 5-6 9t-14 4q11 7-4 15-4 3-12 3t-11-2q-2-4-3-7t3-4 6-3 6-2 5-2q8-6 5-8-1 0-5-2t-6-2-4-3q-2-2 0-7t-1-8q-3 3-5 10t-4 9q4-5-14-4l-5 1q-3 0-9-1t-12-1-7 5q-3 4 0 11 0 2 2 1-2 2-6 5t-6 5q-25-8-52-23 3 0 6 1 3 1 8 3t5 4q19 7 24 3l3 3q7-9 11-14-4 3-17 1-11-4-12-7 4-7 2-10-2 2-6 6t-8 6-8 3q-9 0-13-1-81-45-131-124 4-4 7-4 2-1 3-5t1-6 6 1q5-4 2-10 1 0 25-15 10-10 11-12 2-6-5-10-1 1-5 5t-5 2q-2-3 0-10t6-7q-4 0-5-9t-2-20 0-13l1-1q-2-6 3-19t12-11q-7-1 11-24 3-4 4-5 2-1 7-4t9-6 5-5q2-3 6-13t8-13q-2-3 5-11t6-13q-1 0-2-1t-1 0q2-4 9-8t8-7q1-2 1-6t2-6 4-1q2 11-13 34-8 14-9 17-2 2-4 8t-2 8q1 0 3 0t5-2 4-3 1-1q-1-4 1-10t7-10 10-11 6-7q4-4 8-11t0-8q5 0 11-5t10-11q3-5 4-15t3-13q1-4 5-8t7-5l9-5t7-3q3-2 10-6t12-7q6-2 9-2t8 1 8 2q8 1 16-8t12-12q20-10 30-6-1 0 1-4t4-9 5-8 3-5q3-3 10-8t10-8q4 2 4 5-1-5 4-11t10-6q8 2 8 18-17-9-27 10 0 0-2 3t-2 5-1 4 0 5 2 1q5 0 6 2t-1 7-2 8q-1 4-6 11t-7 8q-3-5-9-4t-9 5q0-1-1-3t-1-4q-7 0-8 0 1 2 1 10t2 13q1 2 3 6t5 9 2 7-3 5-9 1q-11 0-15-11-1-2-2-6t-2-6-5-4q-4-2-14-1t-13 3q-8 4-13 16t-5 20q0 6 1 15t2 14-3 14q2 1 5 5t5 6q2 1 3 1t2 0 3 1 1 3q0 1-2 2-2 1-2 1 4-1 16 1t15-1q9-6 12 1 0 1-1 6t0 7q3-15 16-5 2-1 9-3t9-2q2-1 4-3t3-3 3 0 5 4q5-8 7-13 6-23 10-25 4-2 6-1t3 5 0 8-1 7l-1 4v11l0 4q-8 2-10 7t0 10 9 10q0 1 4 2t9 4 7 4q12 11 8 20 4 0 6 5 0 0-2 2t-5 2-2 2q5 2 1 8 3 2 4 7t4 5q5-7 12-1 4 5 1 9 2 4 11 6t10 5q4-1 5 1t0 7 2 7q2 2 8 5t8 2l9 7q2 2 0 2 10-1 18 6 5 6-4 11 2 3-2 5t-8 3q2 1 7 1t5 1q9 5-4 9-9 2-24-7z m-90-490q114 21 195 106-1 2-7 2t-7 2q-10 4-13 5 1 4-1 7t-5 5-7 5-6 4q-1 1-4 3t-4 3-4 2-5 2-5-1l-2-1q-2 0-3-1t-3-2-2-1 0-2q-12 10-20 13-3 0-7 3t-5 4-6 0-6-4q-3-2-4-8t-1-7q-4 3 0 10t1 10q-1 3-6 2t-6-2-7-5-5-3-4-3-5-5q-2-2-4-6t-2-7q-1 3-7 4t-5 3q1-5 2-19t3-22q4-17-7-26-15-14-16-23-2-12 7-14 0-4-5-12t-4-12q0-3 2-9z" horiz-adv-x="857.1" />
28
+ <glyph glyph-name="globe-1" unicode="&#xe813;" d="m406 755c227 0 405-180 405-404 0-226-178-406-405-406-225 0-406 180-406 406 0 224 181 404 406 404z m23-734c65 0 100 113 116 169-39-4-77-6-116-7l0-162z m-129 82c19-45 48-82 81-82l0 162c-38 1-77 3-115 7 8-32 19-61 34-87z m-222 202c16-19 57-44 132-57-5 31-7 66-7 100 0 25 1 45 3 68-57 12-91 20-117 32-9-30-14-64-14-97 0-16 0-32 3-46z m170 43c0-37 2-75 7-109 43-8 84-11 126-13l0 177c-45 1-90 3-132 8 0-22-1-41-1-63z m131 331c-56-23-104-110-124-225 41-4 84-6 126-8l0 233-2 0z m82-18c-11 8-23 18-32 18l0-233c44 2 85 4 127 8-16 90-52 169-95 207z m-32-435c44 2 85 5 126 12 6 33 8 72 8 110 0 22 0 41-2 62-42-4-86-6-132-7l0-177z m176 190c3-23 3-43 3-68 0-34-3-70-7-102 130 22 135 52 135 105 0 32-5 65-15 95-23-10-66-25-116-30z m99 74c-35 78-101 140-179 168 38-49 61-118 74-199 67 12 95 24 105 31z m-531 93c-29-27-50-59-65-92 11-10 30-11 103-31 14 80 38 146 75 196-42-20-80-37-113-73z m0-465c30-33 67-55 106-72-29 40-48 92-60 152-50 11-95 25-125 41 19-46 42-88 79-121z m466 0c35 32 59 72 75 116-33-15-75-28-121-38-14-60-32-108-62-150 39 17 78 39 108 72z" horiz-adv-x="811" />
29
+ <glyph glyph-name="wordpress" unicode="&#xe814;" d="m0 350q4 209 151 354t349 146q207-4 353-151t147-349q-4-207-151-353t-349-147q-209 4-353 151t-147 349z m35 0q2-193 140-328t325-137q193 4 328 141t137 324q-4 193-141 328t-324 137q-193-4-329-141t-136-324z m41-30q-8 108 33 200l205-553q-109 55-173 155-57 89-65 198z m69 264q62 90 157 140t198 52q168-4 287-111-47 1-70-40-4-25-6-39t7-35 14-33 18-31l18-30q33-62 7-144l-62-219-154 459q43 4 46 4 12 4 15 14t-7 15q-6 6-11 6l-94-8-70 0q-4 0-27 4t-37 2-17-13q-3-16 14-20 33-4 47-6l68-179-93-278-157 459q43 4 49 4 18 2 16 20-4 15-20 15-70-10-136-8z m234-642l129 367 133-359q-131-45-262-8z m334 39q6 19 21 62l120 346q15 49 23 111 2 28-2 49 76-162 33-312-51-172-195-256z" horiz-adv-x="1000" />
30
+ <glyph glyph-name="attention-1" unicode="&#xe815;" d="m571 83v106q0 8-5 13t-12 5h-108q-7 0-12-5t-5-13v-106q0-8 5-13t12-6h108q7 0 12 6t5 13z m-1 208l10 257q0 6-5 10-7 6-14 6h-122q-7 0-14-6-5-4-5-12l9-255q0-5 6-9t13-3h103q8 0 13 3t6 9z m-7 522l428-786q20-35-1-70-10-17-26-26t-35-10h-858q-18 0-35 10t-26 26q-21 35-1 70l429 786q9 17 26 27t36 10 36-10 27-27z" horiz-adv-x="1000" />
31
+ </font>
32
+ </defs>
33
+ </svg>
assets/fonts/simple-calendar.ttf ADDED
Binary file
assets/fonts/simple-calendar.woff ADDED
Binary file
assets/gcal-icon-16x16.png DELETED
Binary file
assets/icon-128x128.png DELETED
Binary file
assets/icon-256x256.png DELETED
Binary file
assets/images/simple-calendar-icon-fill.svg ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
4
+ <g>
5
+ <path d="M513.4,1013.3c-163.8,0-327.7,0-491.5,0c-9.7,0-8.8,0.9-8.8-8.9c0-243.7,0.2-487.3-0.3-731c-0.1-39.6,27.7-76.4,69.6-82.3
6
+ c3-0.4,6-0.5,9-0.5c35,0,70,0.1,105-0.1c5.1,0,6.5,1.4,6.5,6.5c-0.2,26.8,0.1,53.7-0.2,80.5c-0.2,15.2,3.9,28.2,15.9,38.2
7
+ c2.7,2.2,5,4.8,7.4,7.4c8,8.6,18.1,12.4,29.6,12.4c30.7,0.2,61.3,0.2,92,0c11.2-0.1,21.2-3.9,29.1-12.2c3.4-3.6,7-7.2,10.6-10.6
8
+ c9-8.4,12.7-18.9,12.7-31c0.1-28.2,0-56.3,0-84.5c0-6.6,0-6.6,6.6-6.6c71.5,0,143,0,214.5,0c6.6,0,6.6,0,6.6,6.7
9
+ c0,26.8,0.2,53.7-0.1,80.5c-0.2,15,5.1,26.8,15.4,38c13.6,14.7,28.9,20.8,48.7,20.1c25.6-1,51.3-0.4,77-0.2
10
+ c13.9,0.1,24.8-5.1,35.4-14.4c15.9-14,22.1-30.4,21.1-51.3c-1.1-24.3-0.1-48.7-0.4-73c0-5.1,1.4-6.5,6.5-6.5
11
+ c34.7,0.2,69.3,0,104,0.1c42.3,0.2,78.3,36,78.3,78.3c0.1,246,0,492,0,738c0,6.4,0,6.4-7.3,6.4
12
+ C842,1013.3,677.7,1013.3,513.4,1013.3z M439.5,678.9c1.6-1,2.2-1.5,3-1.8c11.8-5.8,21.5-14.2,29-24.8
13
+ c24.3-34.1,24.3-70.9,8.6-107.8c-13.3-31.3-39.3-49.3-71-59.4c-27.6-8.8-56-9.4-84.5-5.6c-20.3,2.7-39.2,9.6-55.5,22.2
14
+ c-35.5,27.2-48.4,65.1-50.4,108c-0.2,3.7,2.1,3.9,4.8,3.9c22.2,0,44.3-0.1,66.5,0c3.8,0,5.4-1.1,5.3-5.1c-0.1-7.7,1-15.3,3-22.7
15
+ c5.4-20,16.5-34.7,37.8-39.7c8.1-1.9,16.3-2.1,24.4-1.3c21.4,2.1,36.3,13.1,43.5,33.7c3.5,10.1,3.9,20.4,2.9,31
16
+ c-2.2,23.4-16.2,39.7-39.1,44.6c-13.2,2.8-26.6,4.1-40.2,4c-3.7,0-5,1.2-4.9,4.9c0.1,16.5,0.2,33,0,49.5c0,4,1.5,5.2,5.3,5.1
17
+ c12.9-0.3,25.6,0.9,38.3,3c26.3,4.2,43.5,18.8,49.2,42c3.3,13.5,3,27.1-0.2,40.6c-2.3,9.7-6.8,18.4-13.8,25.7
18
+ c-15.4,16.1-34.5,21.6-56,18.9c-27.5-3.4-43.5-18-50.5-44.8c-1.9-7.3-3.1-14.7-2.8-22.2c0.2-4-1.1-5.6-5.4-5.6
19
+ c-23.7,0.2-47.3,0.2-71,0c-4,0-5.4,1.2-4.9,5.2c1.4,13.2,2.7,26.5,5.9,39.5c11.1,45.2,38.2,75.6,83.4,88.2
20
+ c36.7,10.3,73.8,10.1,110.5,0.1c24.5-6.6,44.7-20.1,61-39.8c24.5-29.7,34.4-63.9,31.4-101.9c-2.2-27.4-12.5-51.3-33.6-69.8
21
+ C460.7,689.3,451.9,682,439.5,678.9z M755,692.5c0-68.6-0.1-137.3,0.1-205.9c0-5.1-1.4-6.5-6.5-6.4c-18.3,0.3-36.7,0.4-55,0
22
+ c-5.7-0.1-7.2,1.9-8.2,7c-5.9,32.1-24.4,53-56.3,61.1c-17.5,4.5-35.6,5-53.5,5.8c-3.9,0.2-5.2,1.4-5.2,5.3c0.2,15.3,0.2,30.7,0,46
23
+ c-0.1,4.3,1.6,5.4,5.6,5.3c14.2-0.2,28.3-0.1,42.5-0.1c15.5,0,31,0.1,46.5-0.1c4.1,0,5.9,1.3,5.5,5.4c-0.1,1.7,0,3.3,0,5
24
+ c0,92.1,0,184.3,0,276.4c0,7.2,0,7.2,7,7.2c23.5,0,47,0,70.5,0c7,0,7,0,7-7.2C755,829.1,755,760.8,755,692.5z"/>
25
+ <path d="M376.7,199.2c0,24.2,0,48.3,0,72.5c-0.1,23.1-17.4,40.5-40.4,40.6c-23.3,0.1-46.6,0.1-70,0c-22.8-0.1-40.3-17.4-40.3-40.1
26
+ c-0.1-48.5-0.1-96.9,0-145.4c0-22.4,17.5-40,39.9-40.1c23.7-0.1,47.3-0.1,71,0c22.4,0.1,39.8,17.7,39.8,40.1
27
+ C376.8,150.9,376.7,175.1,376.7,199.2z"/>
28
+ <path d="M801.7,199.6c0,24,0,48,0,72c0,23.4-17.3,40.7-40.7,40.8c-23.2,0.1-46.3,0.1-69.5,0c-23.1-0.1-40.5-17.4-40.5-40.5
29
+ c-0.1-48.3-0.1-96.6,0-144.9c0-22.7,17.5-40.2,40.2-40.3c23.3-0.1,46.6-0.1,70,0c23.1,0.1,40.4,17.4,40.5,40.5
30
+ C801.8,151.3,801.7,175.4,801.7,199.6z"/>
31
+ </g>
32
+ </svg>
assets/images/simple-calendar-icon-strokes.svg ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg version="1.1" id="Shapes_xA0_Image_1_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
4
+ x="0px" y="0px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve">
5
+ <path fill="none" stroke="#000000" stroke-miterlimit="10" d="M513.4,1013.3c-163.8,0-327.7,0-491.5,0c-9.7,0-8.8,0.9-8.8-8.9
6
+ c0-243.7,0.2-487.3-0.3-731c-0.1-39.6,27.7-76.4,69.6-82.3c3-0.4,6-0.5,9-0.5c35,0,70,0.1,105-0.1c5.1,0,6.5,1.4,6.5,6.5
7
+ c-0.2,26.8,0.1,53.7-0.2,80.5c-0.2,15.2,3.9,28.2,15.9,38.2c2.7,2.2,5,4.8,7.4,7.4c8,8.6,18.1,12.4,29.6,12.4
8
+ c30.7,0.2,61.3,0.2,92,0c11.2-0.1,21.2-3.9,29.1-12.2c3.4-3.6,7-7.2,10.6-10.6c9-8.4,12.7-18.9,12.7-31c0.1-28.2,0-56.3,0-84.5
9
+ c0-6.6,0-6.6,6.6-6.6c71.5,0,143,0,214.5,0c6.6,0,6.6,0,6.6,6.7c0,26.8,0.2,53.7-0.1,80.5c-0.2,15,5.1,26.8,15.4,38
10
+ c13.6,14.7,28.9,20.8,48.7,20.1c25.6-1,51.3-0.4,77-0.2c13.9,0.1,24.8-5.1,35.4-14.4c15.9-14,22.1-30.4,21.1-51.3
11
+ c-1.1-24.3-0.1-48.7-0.4-73c0-5.1,1.4-6.5,6.5-6.5c34.7,0.2,69.3,0,104,0.1c42.3,0.2,78.3,36,78.3,78.3c0.1,246,0,492,0,738
12
+ c0,6.4,0,6.4-7.3,6.4C842,1013.3,677.7,1013.3,513.4,1013.3z M439.5,678.9c1.6-1,2.2-1.5,3-1.8c11.8-5.8,21.5-14.2,29-24.8
13
+ c24.3-34.1,24.3-70.9,8.6-107.8c-13.3-31.3-39.3-49.3-71-59.4c-27.6-8.8-56-9.4-84.5-5.6c-20.3,2.7-39.2,9.6-55.5,22.2
14
+ c-35.5,27.2-48.4,65.1-50.4,108c-0.2,3.7,2.1,3.9,4.8,3.9c22.2,0,44.3-0.1,66.5,0c3.8,0,5.4-1.1,5.3-5.1c-0.1-7.7,1-15.3,3-22.7
15
+ c5.4-20,16.5-34.7,37.8-39.7c8.1-1.9,16.3-2.1,24.4-1.3c21.4,2.1,36.3,13.1,43.5,33.7c3.5,10.1,3.9,20.4,2.9,31
16
+ c-2.2,23.4-16.2,39.7-39.1,44.6c-13.2,2.8-26.6,4.1-40.2,4c-3.7,0-5,1.2-4.9,4.9c0.1,16.5,0.2,33,0,49.5c0,4,1.5,5.2,5.3,5.1
17
+ c12.9-0.3,25.6,0.9,38.3,3c26.3,4.2,43.5,18.8,49.2,42c3.3,13.5,3,27.1-0.2,40.6c-2.3,9.7-6.8,18.4-13.8,25.7
18
+ c-15.4,16.1-34.5,21.6-56,18.9c-27.5-3.4-43.5-18-50.5-44.8c-1.9-7.3-3.1-14.7-2.8-22.2c0.2-4-1.1-5.6-5.4-5.6
19
+ c-23.7,0.2-47.3,0.2-71,0c-4,0-5.4,1.2-4.9,5.2c1.4,13.2,2.7,26.5,5.9,39.5c11.1,45.2,38.2,75.6,83.4,88.2
20
+ c36.7,10.3,73.8,10.1,110.5,0.1c24.5-6.6,44.7-20.1,61-39.8c24.5-29.7,34.4-63.9,31.4-101.9c-2.2-27.4-12.5-51.3-33.6-69.8
21
+ C460.7,689.3,451.9,682,439.5,678.9z M755,692.5c0-68.6-0.1-137.3,0.1-205.9c0-5.1-1.4-6.5-6.5-6.4c-18.3,0.3-36.7,0.4-55,0
22
+ c-5.7-0.1-7.2,1.9-8.2,7c-5.9,32.1-24.4,53-56.3,61.1c-17.5,4.5-35.6,5-53.5,5.8c-3.9,0.2-5.2,1.4-5.2,5.3c0.2,15.3,0.2,30.7,0,46
23
+ c-0.1,4.3,1.6,5.4,5.6,5.3c14.2-0.2,28.3-0.1,42.5-0.1c15.5,0,31,0.1,46.5-0.1c4.1,0,5.9,1.3,5.5,5.4c-0.1,1.7,0,3.3,0,5
24
+ c0,92.1,0,184.3,0,276.4c0,7.2,0,7.2,7,7.2c23.5,0,47,0,70.5,0c7,0,7,0,7-7.2C755,829.1,755,760.8,755,692.5z"/>
25
+ <path fill="none" stroke="#000000" stroke-miterlimit="10" d="M376.7,199.2c0,24.2,0,48.3,0,72.5c-0.1,23.1-17.4,40.5-40.4,40.6
26
+ c-23.3,0.1-46.6,0.1-70,0c-22.8-0.1-40.3-17.4-40.3-40.1c-0.1-48.5-0.1-96.9,0-145.4c0-22.4,17.5-40,39.9-40.1
27
+ c23.7-0.1,47.3-0.1,71,0c22.4,0.1,39.8,17.7,39.8,40.1C376.8,150.9,376.7,175.1,376.7,199.2z"/>
28
+ <path fill="none" stroke="#000000" stroke-miterlimit="10" d="M801.7,199.6c0,24,0,48,0,72c0,23.4-17.3,40.7-40.7,40.8
29
+ c-23.2,0.1-46.3,0.1-69.5,0c-23.1-0.1-40.5-17.4-40.5-40.5c-0.1-48.3-0.1-96.6,0-144.9c0-22.7,17.5-40.2,40.2-40.3
30
+ c23.3-0.1,46.6-0.1,70,0c23.1,0.1,40.4,17.4,40.5,40.5C801.8,151.3,801.7,175.4,801.7,199.6z"/>
31
+ </svg>
assets/images/welcome/calendar-settings-appearance.png ADDED
Binary file
assets/images/welcome/google-calendar-pro-list-view-annotated.png ADDED
Binary file
assets/images/welcome/grid-view-custom-colors.png ADDED
Binary file
assets/images/welcome/grid-view-widget-dark-theme.png ADDED
Binary file
assets/images/welcome/icon-185x185.png ADDED
Binary file
assets/images/welcome/list-view-widget.png ADDED
Binary file
assets/js/admin-add-calendar.js ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( window, undefined ) {
2
+ 'use strict';
3
+
4
+ jQuery( function( $ ) {
5
+
6
+ /* ===================== *
7
+ * Add Calendar Meta Box *
8
+ * ===================== */
9
+
10
+ var ajaxEnhance = false,
11
+ widgets = $( '#widgets-left' );
12
+
13
+ if ( widgets.length ) {
14
+
15
+ // See: http://wordpress.stackexchange.com/a/37707/48502
16
+ $( document ).ajaxComplete( function( event, XMLHttpRequest, ajaxOptions ) {
17
+
18
+ // Determine which ajax request is this (we are after "save-widget").
19
+ var request = {},
20
+ pairs = ajaxOptions.data.split( '&' ),
21
+ i,
22
+ split,
23
+ widget;
24
+
25
+ for ( i in pairs ) {
26
+ split = pairs[i].split('=');
27
+ request[ decodeURIComponent( split[0] ) ] = decodeURIComponent( split[1] );
28
+ }
29
+
30
+ if ( request.action && ( request.action === 'save-widget' ) ) {
31
+
32
+ widget = $( 'input.widget-id[value="' + request['widget-id'] + '"]' ).parents( '.widget' );
33
+
34
+ // Trigger manual save, if this was the save request
35
+ // and if we didn't get the form html response.
36
+ if ( !XMLHttpRequest.responseText ) {
37
+ wpWidgets.save( widget, 0, 1, 0 );
38
+ } else {
39
+ // We got a response, this could be either our request above,
40
+ // or a correct widget-save call, so fire an event on which we can hook our code.
41
+ $( document ).trigger( 'saved_widget', widget );
42
+ }
43
+
44
+ }
45
+
46
+ } );
47
+
48
+ // Bind our Select2 function to the saved_widget event.
49
+ $( document ).bind( 'saved_widget', function( event, widget ) {
50
+ ajaxEnhance = true;
51
+ enhanceDropDown();
52
+ } );
53
+
54
+ }
55
+
56
+ if ( ! ajaxEnhance ) {
57
+ enhanceDropDown();
58
+ }
59
+
60
+ /**
61
+ * Select2.
62
+ */
63
+ function enhanceDropDown() {
64
+ $( '.simcal-field-select-enhanced' ).each( function( e, i ) {
65
+
66
+ var field = $( i ),
67
+ noResults = field.data( 'noresults' ),
68
+ allowClear = field.data( 'allowclear' );
69
+
70
+ field.select2({
71
+ allowClear : allowClear != 'undefined' ? allowClear : false,
72
+ placeholder : {
73
+ id : '',
74
+ placeholder: ''
75
+ },
76
+ dir : simcal_admin.text_dir != 'undefined' ? simcal_admin.text_dir : 'ltr',
77
+ tokenSeparators: [','],
78
+ width : '100%',
79
+ language : {
80
+ noResults: function() {
81
+ return noResults != 'undefined' ? noResults : '';
82
+ }
83
+ }
84
+ } );
85
+ } );
86
+ }
87
+
88
+ /* ========================= *
89
+ * Add Calendar Media Button *
90
+ * ========================= */
91
+
92
+ // Very Ugly ThickBox hack: https://core.trac.wordpress.org/ticket/17249
93
+ $( '#simcal-insert-shortcode-button' ).on( 'click', function() {
94
+ // ThickBox creates a div which is not immediately available.
95
+ setTimeout( function() {
96
+ var thickBox = document.getElementById( 'TB_window');
97
+ if ( thickBox != 'undefined' ) {
98
+ thickBox.classList.add( 'simcal-insert-shortcode-modal' );
99
+ }
100
+ var thickBoxTitle = document.getElementById( 'TB_title' );
101
+ if ( thickBoxTitle != 'undefined' ) {
102
+ thickBoxTitle.classList.add( 'simcal-insert-shortcode-modal-title' );
103
+ }
104
+ }, 120 );
105
+ } );
106
+
107
+ // Add shortcode in WordPress post editor.
108
+ $( '#simcal-insert-shortcode' ).on( 'click', function( e ) {
109
+
110
+ e.preventDefault();
111
+
112
+ var feedId = $( '#simcal-choose-calendar' ).val();
113
+
114
+ wp.media.editor.insert( '[calendar id="' + feedId + '"] ' );
115
+
116
+ // Close Thickbox.
117
+ tb_remove();
118
+ } );
119
+
120
+ } );
121
+
122
+ } )( this );
assets/js/admin-add-calendar.min.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /*! Simple Calendar - 3.0.0
2
+ * https://simplecalendar.io
3
+ * Copyright (c) Moonstone Media 2015
4
+ * Licensed GPLv2+ */
5
+
6
+ !function(a,b){"use strict";jQuery(function(a){function b(){a(".simcal-field-select-enhanced").each(function(b,c){var d=a(c),e=d.data("noresults"),f=d.data("allowclear");d.select2({allowClear:"undefined"!=f?f:!1,placeholder:{id:"",placeholder:""},dir:"undefined"!=simcal_admin.text_dir?simcal_admin.text_dir:"ltr",tokenSeparators:[","],width:"100%",language:{noResults:function(){return"undefined"!=e?e:""}}})})}var c=!1,d=a("#widgets-left");d.length&&(a(document).ajaxComplete(function(b,c,d){var e,f,g,h={},i=d.data.split("&");for(e in i)f=i[e].split("="),h[decodeURIComponent(f[0])]=decodeURIComponent(f[1]);h.action&&"save-widget"===h.action&&(g=a('input.widget-id[value="'+h["widget-id"]+'"]').parents(".widget"),c.responseText?a(document).trigger("saved_widget",g):wpWidgets.save(g,0,1,0))}),a(document).bind("saved_widget",function(a,d){c=!0,b()})),c||b(),a("#simcal-insert-shortcode-button").on("click",function(){setTimeout(function(){var a=document.getElementById("TB_window");"undefined"!=a&&a.classList.add("simcal-insert-shortcode-modal");var b=document.getElementById("TB_title");"undefined"!=b&&b.classList.add("simcal-insert-shortcode-modal-title")},120)}),a("#simcal-insert-shortcode").on("click",function(b){b.preventDefault();var c=a("#simcal-choose-calendar").val();wp.media.editor.insert('[calendar id="'+c+'"] '),tb_remove()})})}(this);
assets/js/admin.js ADDED
@@ -0,0 +1,535 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( window, undefined ) {
2
+ 'use strict';
3
+
4
+ jQuery( function( $ ) {
5
+
6
+ /* ======== *
7
+ * Tooltips *
8
+ * ======== */
9
+
10
+ // Initialize Tooltips (tiptip.js).
11
+ $( '.simcal-help-tip' ).tipTip( {
12
+ attribute: 'data-tip',
13
+ delay: 200,
14
+ fadeIn: 50,
15
+ fadeOut: 50
16
+ } );
17
+
18
+ // Tooltips to ease shortcode copying.
19
+ $( '.simcal-shortcode-tip' ).tipTip( {
20
+ activation: 'click',
21
+ defaultPosition: 'top',
22
+ delay: 200,
23
+ fadeIn: 50,
24
+ fadeOut: 50
25
+ } );
26
+
27
+ /* ========== *
28
+ * Meta Boxes *
29
+ * ========== */
30
+
31
+ var // Calendar Settings Meta Box
32
+ calendarSettings = $( '#simcal-calendar-settings' ),
33
+ simCalBoxHandle = calendarSettings.find( '.simcal-box-handle' ),
34
+ boxHandle = calendarSettings.find( '.hndle' );
35
+
36
+ // Move the into the Meta Box header handle.
37
+ $( simCalBoxHandle ).appendTo( boxHandle );
38
+ $( function() {
39
+ // Prevent inputs in meta box headings opening/closing contents.
40
+ $( boxHandle ).unbind( 'click.postboxes' );
41
+ calendarSettings.on( 'click', 'h3.hndle', function( event ) {
42
+ // If the user clicks on some form input inside the h3 the box should not be toggled.
43
+ if ( $( event.target ).filter( 'input, option, label, select' ).length ) {
44
+ return;
45
+ }
46
+ calendarSettings.toggleClass( 'closed' );
47
+ });
48
+
49
+ });
50
+
51
+ // Tabbed Panels in Settings Meta Box.
52
+ $( document.body ).on( 'simcal-init-tabbed-panels', function() {
53
+ $( '.simcal-tabs' ).show();
54
+ $( '.simcal-tabs a' ).click( function() {
55
+ var panel_wrap = $( this ).closest( 'div.simcal-panels-wrap' );
56
+ $( 'ul.simcal-tabs li', panel_wrap ).removeClass( 'active' );
57
+ $( this ).parent().addClass( 'active' );
58
+ $( 'div.simcal-panel', panel_wrap ).hide();
59
+ $( $( this ).attr( 'href' ) ).show();
60
+ return false;
61
+ } );
62
+ $( 'div.simcal-panels-wrap' ).each( function() {
63
+ $( this ).find( 'ul.simcal-tabs > li' ).eq( 0 ).find( 'a' ).click();
64
+ } );
65
+ } ).trigger( 'simcal-init-tabbed-panels' );
66
+
67
+ // Swap feed type tabs and panels according to selection.
68
+ $( '#_feed_type' ).on( 'change', function() {
69
+
70
+ var selected = $( this ).find( 'option:selected'),
71
+ feed = selected.val(),
72
+ ul = $( '.simcal-tabs' ),
73
+ tabs = ul.find( '> .simcal-feed-type' ),
74
+ tab = ul.find( '> .simcal-feed-type-' + feed),
75
+ a = ul.find( '> li:first-child > a' );
76
+
77
+ tabs.each( function() {
78
+ $( this ).hide();
79
+ } );
80
+ tab.show();
81
+ a.trigger( 'click' );
82
+
83
+ } ).trigger( 'change' );
84
+
85
+ // Brings back the meta box after all the manipulations above.
86
+ calendarSettings.show();
87
+
88
+ // Toggle default calendar settings.
89
+ var defCalViews = $( '#_calendar_view_default-calendar' ),
90
+ defCalSettings = $( '#default-calendar-settings' ),
91
+ gridSettings = defCalSettings.find( '.simcal-default-calendar-grid' ),
92
+ listSettings = defCalSettings.find( '.simcal-default-calendar-list' ),
93
+ groupedListSettings = defCalSettings.find( '.simcal-default-calendar-list-grouped' );
94
+
95
+ defCalViews.on( 'change', function() {
96
+
97
+ var selView = $( this ).val();
98
+
99
+ if ( 'grid' == selView ) {
100
+ listSettings.hide();
101
+ groupedListSettings.hide();
102
+ gridSettings.show();
103
+ } else if ( 'list' == selView ) {
104
+ gridSettings.hide();
105
+ groupedListSettings.hide();
106
+ listSettings.show();
107
+ } else if ( 'list-grouped' == selView ) {
108
+ gridSettings.hide();
109
+ listSettings.hide();
110
+ groupedListSettings.show();
111
+ }
112
+
113
+ } ).trigger( 'change' );
114
+
115
+ /* ============ *
116
+ * Input Fields *
117
+ * ============ */
118
+
119
+ // WordPress color picker.
120
+ $( '.simcal-field-color-picker' ).wpColorPicker();
121
+
122
+ // Select2 enhanced select.
123
+ $( '.simcal-field-select-enhanced' ).each( function( e, i ) {
124
+
125
+ var field = $( i ),
126
+ noResults = field.data( 'noresults' ),
127
+ allowClear = field.data( 'allowclear' );
128
+
129
+ field.select2( {
130
+ allowClear: allowClear != 'undefined' ? allowClear : false,
131
+ placeholder: {
132
+ id: '',
133
+ placeholder: ''
134
+ },
135
+ dir: simcal_admin.text_dir != 'undefined' ? simcal_admin.text_dir : 'ltr',
136
+ tokenSeparators: [','],
137
+ width: '100%',
138
+ language: {
139
+ noResults: function() {
140
+ return noResults != 'undefined' ? noResults : '';
141
+ }
142
+ }
143
+ } );
144
+ } );
145
+
146
+ // jQuery Date Picker.
147
+ var fieldDatePicker = $( '.simcal-field-date-picker' );
148
+ fieldDatePicker.each( function( e, i ) {
149
+
150
+ var input = $( i ).find( 'input' ),
151
+ args = {
152
+ autoSize: true,
153
+ changeMonth: true,
154
+ changeYear: true,
155
+ dateFormat: 'yy-mm-dd',
156
+ firstDay: 1,
157
+ prevText: '<i class="simcal-icon-left"></i>',
158
+ nextText: '<i class="simcal-icon-right"></i>',
159
+ yearRange: '1900:2050',
160
+ beforeShow: function( input, instance ) {
161
+ $( '#ui-datepicker-div' ).addClass( 'simcal-date-picker' );
162
+ }
163
+ };
164
+
165
+ $( input ).datepicker( args );
166
+ $( input ).datepicker( 'option', $.datepicker.regional[ simcal_admin.locale ] );
167
+
168
+ } );
169
+
170
+ // Datetime formatter field.
171
+ var fieldDateTime = $( '.simcal-field-datetime-format' );
172
+ fieldDateTime.sortable( {
173
+ items: '> div',
174
+ stop: function() {
175
+ formatDateTime( $( this ) );
176
+ }
177
+ } );
178
+ fieldDateTime.each( function( e, i ) {
179
+
180
+ var select = $( i ).find( '> div select' );
181
+
182
+ select.each( function( e, i ) {
183
+ $( i ).on( 'change', function() {
184
+ formatDateTime( $( this ).closest( 'div.simcal-field-datetime-format' ) );
185
+ });
186
+ });
187
+
188
+ formatDateTime( i );
189
+ } );
190
+ // Helper function for datetime formatter field.
191
+ function formatDateTime( field ) {
192
+
193
+ var input = $( field ).find( 'input' ),
194
+ select = $( field ).find( '> div select' ),
195
+ code = $( field ).find( 'code' ),
196
+ format = '',
197
+ preview = '';
198
+
199
+ select.each( function( i, e ) {
200
+
201
+ var value = $( e ).val(),
202
+ selected = $( e ).find( '> option:selected' );
203
+
204
+ if ( value.length ) {
205
+ if ( selected.data( 'trim' ) ) {
206
+ format = format.trim() + $( e ).val();
207
+ preview = preview.trim() + selected.data( 'preview' );
208
+ } else {
209
+ format += $( e ).val() + ' ';
210
+ preview += selected.data( 'preview' ) + ' ';
211
+ }
212
+ }
213
+
214
+ });
215
+
216
+ input.val( format );
217
+ code.text( preview );
218
+ }
219
+ // If PHP datetime formatter is used, this will live preview the user input.
220
+ $( '.simcal-field-datetime-format-php' ).each( function( e, i ) {
221
+
222
+ var input = $( i ).find( 'input' ),
223
+ preview = $( i ).find( 'code' );
224
+
225
+ $( input ).on( 'keyup', function() {
226
+
227
+ var data = {
228
+ action: 'simcal_date_i18n_input_preview',
229
+ value: input.val()
230
+ };
231
+ $.post( simcal_admin.ajax_url, data, function( response ) {
232
+ $( preview ).text( response.data );
233
+ } );
234
+
235
+ } );
236
+
237
+ } );
238
+
239
+ /* =============== *
240
+ * Input Fields UI *
241
+ * =============== */
242
+
243
+ // Enforce min or max value on number inputs
244
+ $( 'input[type="number"].simcal-field' ).each( function( e, i ) {
245
+
246
+ var field = $( i ),
247
+ min = field.attr( 'min' ),
248
+ max = field.attr( 'max' );
249
+
250
+ field.on( 'change', function() {
251
+
252
+ var value = $( this ).val();
253
+
254
+ if ( min && ( value < min ) ) {
255
+ $( this ).val( min );
256
+ }
257
+
258
+ if ( max && ( value > max ) ) {
259
+ $( this ).val( max );
260
+ }
261
+ } );
262
+
263
+ } );
264
+
265
+ // Show or hide a field when an option is selected.
266
+ $( '.simcal-field-switch-other' ).on( 'change', function() {
267
+
268
+ var options = $( this ).find( 'option' );
269
+
270
+ options.each( function( e, option ) {
271
+
272
+ var show = $( option ).data( 'show-field' ),
273
+ showMany = $( option ).data( 'show-fields'),
274
+ hide = $( option ).data( 'hide-field' ),
275
+ hideMany = $( option ).data( 'hide-fields' );
276
+
277
+ var fieldShow = show ? $( '#' + show ) : '',
278
+ fieldHide = hide ? $( '#' + hide ) : '';
279
+
280
+ if ( $( option ).is( ':selected' ) ) {
281
+ if ( fieldShow ) {
282
+ fieldShow.show();
283
+ }
284
+ if ( fieldHide ) {
285
+ fieldHide.hide();
286
+ }
287
+ if ( showMany ) {
288
+ var s = hideMany.split( ',' );
289
+ $( s ).each( function( e, field ) {
290
+ $( '#' + field ).hide();
291
+ } );
292
+ }
293
+ if ( hideMany ) {
294
+ var h = hideMany.split( ',' );
295
+ $( h ).each( function( e, field ) {
296
+ $( '#' + field ).hide();
297
+ } );
298
+ }
299
+ }
300
+ } );
301
+
302
+ } ).trigger( 'change' );
303
+
304
+ // Show another field based on the selection of a field.
305
+ $( '.simcal-field-show-other' ).on( 'change', function() {
306
+
307
+ var options = $( this ).find( 'option' );
308
+
309
+ options.each( function( e, option ) {
310
+
311
+ var id = $( option ).data( 'show-field' ),
312
+ field = id.length ? $( '#' + id ) : '',
313
+ next = id.length ? field.next() : '';
314
+
315
+ if ( field.length ) {
316
+ if ( $( option ).is( ':selected' ) ) {
317
+ field.show();
318
+ if ( next.hasClass( 'select2' ) ) {
319
+ next.show();
320
+ }
321
+ } else {
322
+ field.hide();
323
+ if ( next.hasClass( 'select2' ) ) {
324
+ next.hide();
325
+ }
326
+ }
327
+ }
328
+
329
+ } );
330
+
331
+ } ).trigger( 'change' );
332
+
333
+ // Show the next field when a particular option is chosen in a field.
334
+ $( '.simcal-field-show-next' ).on( 'change', function() {
335
+
336
+ var value,
337
+ trigger,
338
+ el,
339
+ next;
340
+
341
+ if ( $( this ).is( ':checkbox' ) ) {
342
+
343
+ el = $( this ).parent().next();
344
+
345
+ if ( $( this ).is( ':checked' ) ) {
346
+ el.show();
347
+ } else {
348
+ el.hide();
349
+ }
350
+
351
+ } else {
352
+
353
+ value = $( this ).val();
354
+ trigger = $( this ).data( 'show-next-if-value' );
355
+ el = $( this ).nextUntil().not( 'i' );
356
+ next = el.length ? el.next() : '';
357
+
358
+ if (value == trigger) {
359
+ el.show();
360
+ if ( next.hasClass( 'select2' ) ) {
361
+ next.show();
362
+ }
363
+ } else {
364
+ el.hide();
365
+ if ( next.hasClass( 'select2' ) ) {
366
+ next.hide();
367
+ }
368
+ }
369
+
370
+ }
371
+
372
+ } ).trigger( 'change' );
373
+
374
+ /* ==== *
375
+ * Misc *
376
+ * ==== */
377
+
378
+ // Clear cache buttons.
379
+ $( '#simcal-clear-cache' ).on( 'click', function( e ) {
380
+
381
+ e.preventDefault();
382
+
383
+ var spinner = $( this ).find( 'i' );
384
+
385
+ $.ajax( {
386
+ url : simcal_admin.ajax_url,
387
+ method : 'POST',
388
+ data : {
389
+ action: 'simcal_clear_cache',
390
+ id : $( this ).data( 'id' )
391
+ },
392
+ beforeSend: function() {
393
+ spinner.fadeToggle();
394
+ },
395
+ success : function() {
396
+ spinner.fadeToggle();
397
+ },
398
+ error : function( response ) {
399
+ console.log( response );
400
+ }
401
+ } );
402
+
403
+ } );
404
+
405
+ // Newsletter signup
406
+ $( '#simcal-drip-signup' ).on( 'click', function( e ) {
407
+
408
+ e.preventDefault();
409
+
410
+ var nlMetaBox = $( '#simcal-drip' ),
411
+ signupDiv = nlMetaBox.find( '.signup' ),
412
+ thankYou = nlMetaBox.find( '.thank-you' ),
413
+ nlForm = $( '#simcal-drip-form' ),
414
+ name = nlMetaBox.find( '#simcal-drip-field-first_name' ),
415
+ nameReal = nlForm.find( '#simcal-drip-real-field-first_name' ),
416
+ email = nlMetaBox.find( '#simcal-drip-field-email' ),
417
+ emailReal = nlForm.find( '#simcal-drip-real-field-email' );
418
+
419
+ nameReal.val( name.val() );
420
+ emailReal.val( email.val() );
421
+
422
+ signupDiv.hide();
423
+ thankYou.show();
424
+
425
+ nlForm.submit();
426
+
427
+ } );
428
+
429
+ /* ========================= *
430
+ * Add-on License Management *
431
+ * ========================= */
432
+
433
+ $( '.simcal-addon-manage-license' ).on( 'click', function( e ) {
434
+
435
+ e.preventDefault();
436
+
437
+ var manage_license_action = '',
438
+ button = $( this ),
439
+ buttons = button.closest( '.simcal-addon-manage-license-buttons' ),
440
+ field = button.closest( '.simcal-addon-manage-license-field' ).find( '> input' ),
441
+ error = buttons.find( '.error' ),
442
+ spinner = button.find( 'i' );
443
+
444
+ if ( $( this ).hasClass( 'activate' ) ) {
445
+ manage_license_action = 'activate_license';
446
+ } else if ( $( this ).hasClass( 'deactivate' ) ) {
447
+ manage_license_action = 'deactivate_license';
448
+ } else {
449
+ return;
450
+ }
451
+
452
+ $.ajax( {
453
+ url : simcal_admin.ajax_url,
454
+ method : 'POST',
455
+ data : {
456
+ action : 'simcal_manage_add_on_license',
457
+ add_on : $( this ).data('add-on'),
458
+ license_key : field.val(),
459
+ license_action: manage_license_action,
460
+ nonce : $( '#simcal_license_manager' ).val()
461
+ },
462
+ beforeSend: function() {
463
+ spinner.fadeToggle();
464
+ },
465
+ success : function( response ) {
466
+ spinner.fadeToggle();
467
+ if ( 'activate_license' == manage_license_action ) {
468
+ if ( 'valid' == response.data ) {
469
+ button.hide();
470
+ field.attr( 'disabled', 'disabled' );
471
+ $( buttons ).find( '.label' ).show();
472
+ $( buttons ).find( '.deactivate' ).show();
473
+ error.hide();
474
+ } else {
475
+ error.show().text( response.data );
476
+ }
477
+ } else {
478
+ if ( 'deactivated' == response.data ) {
479
+ button.hide();
480
+ field.removeAttr( 'disabled' );
481
+ $( buttons ).find( '.label' ).hide();
482
+ $( buttons ).find( '.activate' ).show();
483
+ error.hide();
484
+ } else {
485
+ error.show().text( response.data );
486
+ }
487
+ }
488
+ },
489
+ error : function( response ) {
490
+ console.log( response );
491
+ spinner.fadeToggle();
492
+ }
493
+ } );
494
+
495
+ } );
496
+
497
+ $( '#simcal-reset-licenses' ).on( 'click', function( e ) {
498
+
499
+ e.preventDefault();
500
+
501
+ var spinner = $( this ).find( 'i' ),
502
+ dialog = $( this ).data( 'dialog' ),
503
+ reply = confirm( dialog );
504
+
505
+ if ( true !== reply ) {
506
+ return;
507
+ }
508
+
509
+ $.ajax({
510
+ url : simcal_admin.ajax_url,
511
+ method : 'POST',
512
+ data : {
513
+ action: 'simcal_reset_add_ons_licenses',
514
+ nonce : $( '#simcal_license_manager' ).val()
515
+ },
516
+ beforeSend: function() {
517
+ spinner.toggle();
518
+ },
519
+ success: function ( response ) {
520
+ if ( 'success' == response.data ) {
521
+ location.reload();
522
+ } else {
523
+ console.log( response );
524
+ }
525
+ },
526
+ error : function ( response ) {
527
+ console.log(response);
528
+ }
529
+ } );
530
+
531
+ } );
532
+
533
+ } );
534
+
535
+ } )( this );
assets/js/admin.min.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /*! Simple Calendar - 3.0.0
2
+ * https://simplecalendar.io
3
+ * Copyright (c) Moonstone Media 2015
4
+ * Licensed GPLv2+ */
5
+
6
+ !function(a,b){"use strict";jQuery(function(a){function b(b){var c=a(b).find("input"),d=a(b).find("> div select"),e=a(b).find("code"),f="",g="";d.each(function(b,c){var d=a(c).val(),e=a(c).find("> option:selected");d.length&&(e.data("trim")?(f=f.trim()+a(c).val(),g=g.trim()+e.data("preview")):(f+=a(c).val()+" ",g+=e.data("preview")+" "))}),c.val(f),e.text(g)}a(".simcal-help-tip").tipTip({attribute:"data-tip",delay:200,fadeIn:50,fadeOut:50}),a(".simcal-shortcode-tip").tipTip({activation:"click",defaultPosition:"top",delay:200,fadeIn:50,fadeOut:50});var c=a("#simcal-calendar-settings"),d=c.find(".simcal-box-handle"),e=c.find(".hndle");a(d).appendTo(e),a(function(){a(e).unbind("click.postboxes"),c.on("click","h3.hndle",function(b){a(b.target).filter("input, option, label, select").length||c.toggleClass("closed")})}),a(document.body).on("simcal-init-tabbed-panels",function(){a(".simcal-tabs").show(),a(".simcal-tabs a").click(function(){var b=a(this).closest("div.simcal-panels-wrap");return a("ul.simcal-tabs li",b).removeClass("active"),a(this).parent().addClass("active"),a("div.simcal-panel",b).hide(),a(a(this).attr("href")).show(),!1}),a("div.simcal-panels-wrap").each(function(){a(this).find("ul.simcal-tabs > li").eq(0).find("a").click()})}).trigger("simcal-init-tabbed-panels"),a("#_feed_type").on("change",function(){var b=a(this).find("option:selected"),c=b.val(),d=a(".simcal-tabs"),e=d.find("> .simcal-feed-type"),f=d.find("> .simcal-feed-type-"+c),g=d.find("> li:first-child > a");e.each(function(){a(this).hide()}),f.show(),g.trigger("click")}).trigger("change"),c.show();var f=a("#_calendar_view_default-calendar"),g=a("#default-calendar-settings"),h=g.find(".simcal-default-calendar-grid"),i=g.find(".simcal-default-calendar-list"),j=g.find(".simcal-default-calendar-list-grouped");f.on("change",function(){var b=a(this).val();"grid"==b?(i.hide(),j.hide(),h.show()):"list"==b?(h.hide(),j.hide(),i.show()):"list-grouped"==b&&(h.hide(),i.hide(),j.show())}).trigger("change"),a(".simcal-field-color-picker").wpColorPicker(),a(".simcal-field-select-enhanced").each(function(b,c){var d=a(c),e=d.data("noresults"),f=d.data("allowclear");d.select2({allowClear:"undefined"!=f?f:!1,placeholder:{id:"",placeholder:""},dir:"undefined"!=simcal_admin.text_dir?simcal_admin.text_dir:"ltr",tokenSeparators:[","],width:"100%",language:{noResults:function(){return"undefined"!=e?e:""}}})});var k=a(".simcal-field-date-picker");k.each(function(b,c){var d=a(c).find("input"),e={autoSize:!0,changeMonth:!0,changeYear:!0,dateFormat:"yy-mm-dd",firstDay:1,prevText:'<i class="simcal-icon-left"></i>',nextText:'<i class="simcal-icon-right"></i>',yearRange:"1900:2050",beforeShow:function(b,c){a("#ui-datepicker-div").addClass("simcal-date-picker")}};a(d).datepicker(e),a(d).datepicker("option",a.datepicker.regional[simcal_admin.locale])});var l=a(".simcal-field-datetime-format");l.sortable({items:"> div",stop:function(){b(a(this))}}),l.each(function(c,d){var e=a(d).find("> div select");e.each(function(c,d){a(d).on("change",function(){b(a(this).closest("div.simcal-field-datetime-format"))})}),b(d)}),a(".simcal-field-datetime-format-php").each(function(b,c){var d=a(c).find("input"),e=a(c).find("code");a(d).on("keyup",function(){var b={action:"simcal_date_i18n_input_preview",value:d.val()};a.post(simcal_admin.ajax_url,b,function(b){a(e).text(b.data)})})}),a('input[type="number"].simcal-field').each(function(b,c){var d=a(c),e=d.attr("min"),f=d.attr("max");d.on("change",function(){var b=a(this).val();e&&e>b&&a(this).val(e),f&&b>f&&a(this).val(f)})}),a(".simcal-field-switch-other").on("change",function(){var b=a(this).find("option");b.each(function(b,c){var d=a(c).data("show-field"),e=a(c).data("show-fields"),f=a(c).data("hide-field"),g=a(c).data("hide-fields"),h=d?a("#"+d):"",i=f?a("#"+f):"";if(a(c).is(":selected")){if(h&&h.show(),i&&i.hide(),e){var j=g.split(",");a(j).each(function(b,c){a("#"+c).hide()})}if(g){var k=g.split(",");a(k).each(function(b,c){a("#"+c).hide()})}}})}).trigger("change"),a(".simcal-field-show-other").on("change",function(){var b=a(this).find("option");b.each(function(b,c){var d=a(c).data("show-field"),e=d.length?a("#"+d):"",f=d.length?e.next():"";e.length&&(a(c).is(":selected")?(e.show(),f.hasClass("select2")&&f.show()):(e.hide(),f.hasClass("select2")&&f.hide()))})}).trigger("change"),a(".simcal-field-show-next").on("change",function(){var b,c,d,e;a(this).is(":checkbox")?(d=a(this).parent().next(),a(this).is(":checked")?d.show():d.hide()):(b=a(this).val(),c=a(this).data("show-next-if-value"),d=a(this).nextUntil().not("i"),e=d.length?d.next():"",b==c?(d.show(),e.hasClass("select2")&&e.show()):(d.hide(),e.hasClass("select2")&&e.hide()))}).trigger("change"),a("#simcal-clear-cache").on("click",function(b){b.preventDefault();var c=a(this).find("i");a.ajax({url:simcal_admin.ajax_url,method:"POST",data:{action:"simcal_clear_cache",id:a(this).data("id")},beforeSend:function(){c.fadeToggle()},success:function(){c.fadeToggle()},error:function(a){console.log(a)}})}),a("#simcal-drip-signup").on("click",function(b){b.preventDefault();var c=a("#simcal-drip"),d=c.find(".signup"),e=c.find(".thank-you"),f=a("#simcal-drip-form"),g=c.find("#simcal-drip-field-first_name"),h=f.find("#simcal-drip-real-field-first_name"),i=c.find("#simcal-drip-field-email"),j=f.find("#simcal-drip-real-field-email");console.log(g.val()),console.log(i.val()),h.val(g.val()),j.val(i.val()),d.hide(),e.show(),f.submit()}),a(".simcal-addon-manage-license").on("click",function(b){b.preventDefault();var c="",d=a(this),e=d.closest(".simcal-addon-manage-license-buttons"),f=d.closest(".simcal-addon-manage-license-field").find("> input"),g=e.find(".error"),h=d.find("i");if(a(this).hasClass("activate"))c="activate_license";else{if(!a(this).hasClass("deactivate"))return;c="deactivate_license"}a.ajax({url:simcal_admin.ajax_url,method:"POST",data:{action:"simcal_manage_add_on_license",add_on:a(this).data("add-on"),license_key:f.val(),license_action:c,nonce:a("#simcal_license_manager").val()},beforeSend:function(){h.fadeToggle()},success:function(b){h.fadeToggle(),"activate_license"==c?"valid"==b.data?(d.hide(),f.attr("disabled","disabled"),a(e).find(".label").show(),a(e).find(".deactivate").show(),g.hide()):g.show().text(b.data):"deactivated"==b.data?(d.hide(),f.removeAttr("disabled"),a(e).find(".label").hide(),a(e).find(".activate").show(),g.hide()):g.show().text(b.data)},error:function(a){console.log(a),h.fadeToggle()}})}),a("#simcal-reset-licenses").on("click",function(b){b.preventDefault();var c=a(this).data("dialog"),d=confirm(c);!0===d&&a.ajax({url:simcal_admin.ajax_url,method:"POST",data:{action:"simcal_reset_add_ons_licenses",nonce:a("#simcal_license_manager").val()},success:function(a){"success"==a.data?location.reload():console.log(a)},error:function(a){console.log(a)}})})})}(this);
assets/js/default-calendar.js ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( window, undefined ) {
2
+ 'use strict';
3
+
4
+ jQuery( function( $ ) {
5
+
6
+ // Browse calendar pages.
7
+ $( '.simcal-default-calendar' ).each( function( e, i ) {
8
+
9
+ var calendar = $( i ),
10
+ id = calendar.data( 'calendar-id' ),
11
+ offset = calendar.data( 'offset' ),
12
+ start = calendar.data( 'events-first' ),
13
+ end = calendar.data( 'calendar-end' ),
14
+ nav = calendar.find( '.simcal-calendar-head' ),
15
+ buttons = nav.find( '.simcal-nav-button' ),
16
+ spinner = calendar.find( '.simcal-ajax-loader' ),
17
+ current = nav.find( '.simcal-current' ),
18
+ currentTime = current.data( 'calendar-current' ),
19
+ currentMonth = current.find( 'span.simcal-current-month' ),
20
+ currentYear = current.find( 'span.simcal-current-year' ),
21
+ currentDate = new Date( currentTime * 1000 ),
22
+ date,
23
+ action;
24
+
25
+ if ( calendar.hasClass( 'simcal-default-calendar-grid' ) ) {
26
+ action = 'simcal_default_calendar_draw_grid';
27
+ // Always use the first of the month in grid.
28
+ date = new Date( currentDate.getFullYear(), currentDate.getMonth(), 1 );
29
+ toggleGridNavButtons( buttons, date.getTime() / 1000, start, end );
30
+ } else {
31
+ action = 'simcal_default_calendar_draw_list';
32
+ toggleListNavButtons( buttons, calendar, start, end, false );
33
+ toggleListHeading( calendar );
34
+ }
35
+
36
+ // Navigate the calendar.
37
+ buttons.on( 'click', function() {
38
+
39
+ var direction = $( this ).hasClass( 'simcal-next' ) ? 'next' : 'prev';
40
+
41
+ if ( action == 'simcal_default_calendar_draw_grid' ) {
42
+
43
+ // Monthly grid calendars.
44
+
45
+ var body = calendar.find( '.simcal-month' ),
46
+ newDate,
47
+ month,
48
+ year;
49
+
50
+ if ( 'prev' == direction ) {
51
+ // Beginning of the previous month.
52
+ newDate = new Date( date.setMonth( date.getMonth() - 1, 1 ) );
53
+ } else {
54
+ // Last day of next month.
55
+ newDate = new Date( date.setMonth( date.getMonth() + 2, 1 ) );
56
+ newDate.setDate( 0 );
57
+ newDate.setHours( 23 );
58
+ newDate.setMinutes( 59 );
59
+ newDate.setSeconds( 59 );
60
+ }
61
+
62
+ month = newDate.getMonth();
63
+ year = newDate.getFullYear();
64
+
65
+ $.ajax({
66
+ url : simcal_default_calendar.ajax_url,
67
+ method : 'POST',
68
+ dataType : 'json',
69
+ cache : false,
70
+ data : {
71
+ action: action,
72
+ month : month + 1, // month count in PHP goes 1-12 vs 0-11 in JavaScript
73
+ year : year,
74
+ id : id
75
+ },
76
+ beforeSend: function() {
77
+ spinner.fadeToggle();
78
+ },
79
+ success : function( response ) {
80
+
81
+ currentMonth.text( simcal_default_calendar.months.full[month] );
82
+ currentYear.text( year );
83
+ current.attr( 'data-calendar-current', ( newDate.getTime() / 1000 ) + offset + 1 );
84
+
85
+ toggleGridNavButtons( buttons, newDate.getTime() / 1000, start, end );
86
+
87
+ spinner.fadeToggle();
88
+
89
+ date = newDate;
90
+
91
+ body.replaceWith( response.data );
92
+
93
+ calendarBubbles( calendar, list );
94
+ expandEventsToggle();
95
+ },
96
+ error : function( response ) {
97
+ console.log(response );
98
+ }
99
+ } );
100
+
101
+ } else {
102
+
103
+ // List calendars.
104
+
105
+ var list = calendar.find( '.simcal-events-list-container' ),
106
+ prev = list.data( 'prev' ),
107
+ next = list.data( 'next' ),
108
+ timestamp = direction == 'prev' ? prev : next;
109
+
110
+ $.ajax( {
111
+ url : simcal_default_calendar.ajax_url,
112
+ method : 'POST',
113
+ dataType : 'json',
114
+ cache : false,
115
+ data : {
116
+ action: action,
117
+ ts : timestamp,
118
+ id : id
119
+ },
120
+ beforeSend: function() {
121
+ spinner.fadeToggle();
122
+ },
123
+ success : function( response ) {
124
+
125
+ list.replaceWith( response.data );
126
+ current.attr( 'data-calendar-current', timestamp );
127
+
128
+ toggleListHeading( calendar );
129
+ toggleListNavButtons( buttons, calendar, start, end, direction );
130
+
131
+ spinner.fadeToggle();
132
+ expandEventsToggle();
133
+ },
134
+ error : function( response ) {
135
+ console.log( response );
136
+ }
137
+ });
138
+
139
+ }
140
+
141
+ } );
142
+ } );
143
+
144
+ /**
145
+ * Enable or disable grid calendar navigation buttons.
146
+ *
147
+ * @param buttons Previous and Next buttons elements.
148
+ * @param time Current time.
149
+ * @param min Lower bound timestamp.
150
+ * @param max Upper bound timestamp.
151
+ */
152
+ function toggleGridNavButtons( buttons, time, min, max ) {
153
+
154
+ buttons.each( function( e, i ) {
155
+
156
+ var button = $( i),
157
+ month = new Date( time * 1000 );
158
+
159
+ if ( button.hasClass( 'simcal-prev' ) ) {
160
+
161
+ month = new Date( month.setMonth( month.getMonth(), 1 ) );
162
+ month.setDate( 0 );
163
+
164
+ if ( month.getTime() / 1000 <= min ) {
165
+ button.attr( 'disabled', 'disabled' );
166
+ } else {
167
+ button.removeAttr( 'disabled' );
168
+ }
169
+
170
+ } else {
171
+
172
+ month = new Date( month.setMonth( month.getMonth() + 1, 1 ) );
173
+ month.setDate( 0 );
174
+ month.setHours( 23 );
175
+ month.setMinutes( 59 );
176
+ month.setSeconds( 59 );
177
+
178
+ if ( month.getTime() / 1000 >= max ) {
179
+ button.attr( 'disabled', 'disabled' );
180
+ } else {
181
+ button.removeAttr( 'disabled' );
182
+ }
183
+ }
184
+
185
+ } );
186
+ }
187
+
188
+ /**
189
+ * Enable or disable grid calendar navigation buttons.
190
+ *
191
+ * @param buttons Previous and Next button elements.
192
+ * @param calendar Current calendar.
193
+ * @param start Lower bound timestamp.
194
+ * @param end Upper bound timestamp.
195
+ * @param direction Direction intent.
196
+ */
197
+ function toggleListNavButtons( buttons, calendar, start, end, direction ) {
198
+
199
+ var list = calendar.find( '.simcal-events-list-container' ),
200
+ prev = list.data( 'prev' ),
201
+ next = list.data( 'next' );
202
+
203
+ buttons.each( function( e, b ) {
204
+
205
+ var button = $( b );
206
+
207
+ if ( direction ) {
208
+
209
+ if ( direction == 'prev' && button.hasClass( 'simcal-prev' ) ) {
210
+ if ( prev <= start ) {
211
+ button.attr( 'disabled', 'disabled' );
212
+ }
213
+ } else if ( button.hasClass( 'simcal-prev' ) ) {
214
+ button.removeAttr( 'disabled' );
215
+ }
216
+
217
+ if ( direction == 'next' && button.hasClass( 'simcal-next' ) ) {
218
+ if ( next >= end ) {
219
+ button.attr( 'disabled', 'disabled' );
220
+ }
221
+ } else if( $(button).hasClass( 'simcal-next' ) ) {
222
+ button.removeAttr( 'disabled' );
223
+ }
224
+
225
+ } else {
226
+
227
+ if ( prev <= start && button.hasClass( 'simcal-prev' ) ) {
228
+ button.attr( 'disabled', 'disabled' );
229
+ }
230
+
231
+ if ( next >= end && button.hasClass( 'simcal-next' ) ) {
232
+ button.attr( 'disabled', 'disabled' );
233
+ }
234
+
235
+ }
236
+ } );
237
+ }
238
+
239
+ /**
240
+ * Replace the list heading with current page.
241
+ *
242
+ * @param calendar Current calendar.
243
+ */
244
+ function toggleListHeading( calendar ) {
245
+
246
+ var current = $( calendar ).find( '.simcal-current' ),
247
+ heading = $( calendar ).find( '.simcal-events-list-container' ),
248
+ small = heading.data( 'heading-small' ),
249
+ large = heading.data( 'heading-large' );
250
+
251
+ if ( calendar.width() < 400 ) {
252
+ current.html( '<h3>' + small + '</h3>' );
253
+ } else {
254
+ current.html( '<h3>' + large + '</h3>' );
255
+ }
256
+ }
257
+
258
+ var gridCalendars = $( '.simcal-default-calendar-grid' );
259
+
260
+ /**
261
+ * Default calendar grid event bubbles.
262
+ *
263
+ * Initializes tooltips for events in grid.
264
+ * Adjusts UI for mobile or desktop.
265
+ *
266
+ * @param calendar The calendar element.
267
+ */
268
+ function calendarBubbles( calendar ) {
269
+
270
+ var table = $( calendar ).find( '> table' ),
271
+ thead = table.find( 'thead' ),
272
+ weekDayNames = thead.find( 'th.simcal-week-day' ),
273
+ cells = table.find( 'td.simcal-day > div' ),
274
+ eventsList = table.find( 'ul.simcal-events' ),
275
+ eventTitles = eventsList.find( '> li > .simcal-event-title' ),
276
+ eventsToggle = table.find( '.simcal-events-toggle' ),
277
+ eventsDots = table.find( 'span.simcal-events-dots' ),
278
+ events = table.find( '.simcal-tooltip-content' ),
279
+ hiddenEvents = table.find( '.simcal-event-toggled' ),
280
+ bubbleTrigger = table.data( 'event-bubble-trigger' ),
281
+ width = cells.first().width();
282
+
283
+ if ( width < 60 ) {
284
+
285
+ weekDayNames.each( function( e, w ) {
286
+ $( w ).text( $( w ).data( 'screen-small' ) );
287
+ } );
288
+
289
+ // Hide list of events titles and show dots.
290
+ eventsList.hide();
291
+ eventTitles.hide();
292
+ if ( eventsToggle != 'undefined' ) {
293
+ eventsToggle.hide();
294
+ if ( hiddenEvents != 'undefined' ) {
295
+ hiddenEvents.show();
296
+ }
297
+ }
298
+ eventsDots.show();
299
+
300
+ // Force click/tap on mobile.
301
+ bubbleTrigger = 'click';
302
+ // Adapts cells to be more squareish on mobile.
303
+ var minH = ( width - 10 ) + 'px';
304
+ cells.css( 'min-height', minH );
305
+ table.find( 'span.simcal-events-dots:not(:empty)' ).css( 'min-height', minH );
306
+
307
+ } else {
308
+
309
+ if ( width <= 240 ) {
310
+ weekDayNames.each( function( e, w ) {
311
+ $( w ).text( $( w ).data( 'screen-medium' ) );
312
+ });
313
+ } else {
314
+ weekDayNames.each( function( e, w ) {
315
+ $( w ).text( $( w ).data( 'screen-large' ) );
316
+ } );
317
+ }
318
+
319
+ // Hide dots and show list of events titles and toggle.
320
+ eventsList.show();
321
+ eventTitles.show();
322
+ if ( eventsToggle != 'undefined' ) {
323
+ eventsToggle.show();
324
+ if ( hiddenEvents != 'undefined' ) {
325
+ hiddenEvents.hide();
326
+ }
327
+ }
328
+ eventsDots.hide();
329
+
330
+ // Cells default min-height value.
331
+ cells.css( 'min-height', ( width ) + 'px' );
332
+ }
333
+
334
+ // Create bubbles for each cell.
335
+ cells.each( function( e, cell ) {
336
+
337
+ var cellDots = $( cell ).find( 'span.simcal-events-dots' ),
338
+ tooltips = $( cell ).find( '.simcal-tooltip' ),
339
+ eventBubbles,
340
+ content,
341
+ last;
342
+
343
+ // Mobile mode.
344
+ if ( width < 60 ) {
345
+ events.show();
346
+ // Use a single bubble from dots as a whole.
347
+ eventBubbles = cellDots;
348
+ } else {
349
+ events.hide();
350
+ // Create a bubble for each event in list.
351
+ eventBubbles = tooltips;
352
+ }
353
+
354
+ eventBubbles.each( function( e, i ) {
355
+ $( i ).qtip( {
356
+ content : width < 60 ? $( cell ).find( 'ul.simcal-events' ) : $( i ).find( '> .simcal-tooltip-content' ),
357
+ position: {
358
+ my : 'top center',
359
+ at : 'bottom center',
360
+ target : $(i),
361
+ viewport: width < 60 ? $( window ) : true,
362
+ adjust : {
363
+ method: 'shift',
364
+ scroll: false
365
+ }
366
+ },
367
+ style : {
368
+ def : false,
369
+ classes: 'simcal-default-calendar simcal-event-bubble'
370
+ },
371
+ show : {
372
+ solo : true,
373
+ effect: false,
374
+ event : bubbleTrigger == 'hover' ? 'mouseenter' : 'click'
375
+ },
376
+ hide : {
377
+ fixed : true,
378
+ effect: false,
379
+ event : bubbleTrigger == 'click' ? 'unfocus' : 'mouseleave',
380
+ delay: 100
381
+ },
382
+ events : {
383
+ show: function( event, current ) {
384
+ // Hide when another tooltip opens:
385
+ if ( last && last.id ) {
386
+ if ( last.id != current.id ) {
387
+ last.hide();
388
+ }
389
+ }
390
+ last = current;
391
+ }
392
+ }
393
+ } );
394
+
395
+ } );
396
+
397
+ } );
398
+
399
+ }
400
+
401
+ // Event bubbles and calendar UI triggers.
402
+ gridCalendars.each( function( e, calendar ) {
403
+ calendarBubbles( calendar );
404
+ $( calendar ).on( 'change', function() {
405
+ calendarBubbles( this );
406
+ } );
407
+ } );
408
+ // Viewport changes might require triggering calendar mobile mode.
409
+ window.onresize = function() {
410
+ gridCalendars.each( function( e, calendar ) {
411
+ calendarBubbles( calendar );
412
+ } );
413
+ };
414
+
415
+ /**
416
+ * Toggle to expand events.
417
+ */
418
+ function expandEventsToggle() {
419
+ $( '.simcal-events-toggle' ).each( function( e, button ) {
420
+
421
+ var list = $( button ).prev( '.simcal-events' ),
422
+ toggled = list.find( '.simcal-event-toggled' ),
423
+ arrow = $( button ).find( 'i' );
424
+
425
+ $( button ).on( 'click', function() {
426
+ arrow.toggleClass( 'simcal-icon-rotate-180' );
427
+ toggled.slideToggle();
428
+ });
429
+
430
+ });
431
+ }
432
+ expandEventsToggle();
433
+
434
+ } );
435
+
436
+ } )( this );
assets/js/default-calendar.min.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /*! Simple Calendar - 3.0.0
2
+ * https://simplecalendar.io
3
+ * Copyright (c) Moonstone Media 2015
4
+ * Licensed GPLv2+ */
5
+
6
+ !function(a,b){"use strict";jQuery(function(b){function c(a,c,d,e){a.each(function(a,f){var g=b(f),h=new Date(1e3*c);g.hasClass("simcal-prev")?(h=new Date(h.setMonth(h.getMonth(),1)),h.setDate(0),h.getTime()/1e3<=d?g.attr("disabled","disabled"):g.removeAttr("disabled")):(h=new Date(h.setMonth(h.getMonth()+1,1)),h.setDate(0),h.setHours(23),h.setMinutes(59),h.setSeconds(59),h.getTime()/1e3>=e?g.attr("disabled","disabled"):g.removeAttr("disabled"))})}function d(a,c,d,e,f){var g=c.find(".simcal-events-list-container"),h=g.data("prev"),i=g.data("next");a.each(function(a,c){var g=b(c);f?("prev"==f&&g.hasClass("simcal-prev")?d>=h&&g.attr("disabled","disabled"):g.hasClass("simcal-prev")&&g.removeAttr("disabled"),"next"==f&&g.hasClass("simcal-next")?i>=e&&g.attr("disabled","disabled"):b(g).hasClass("simcal-next")&&g.removeAttr("disabled")):(d>=h&&g.hasClass("simcal-prev")&&g.attr("disabled","disabled"),i>=e&&g.hasClass("simcal-next")&&g.attr("disabled","disabled"))})}function e(a){var c=b(a).find(".simcal-current"),d=b(a).find(".simcal-events-list-container"),e=d.data("heading-small"),f=d.data("heading-large");a.width()<400?c.html("<h3>"+e+"</h3>"):c.html("<h3>"+f+"</h3>")}function f(c){var d=b(c).find("> table"),e=d.find("thead"),f=e.find("th.simcal-week-day"),g=d.find("td.simcal-day > div"),h=d.find("ul.simcal-events"),i=h.find("> li > .simcal-event-title"),j=d.find(".simcal-events-toggle"),k=d.find("span.simcal-events-dots"),l=d.find(".simcal-tooltip-content"),m=d.find(".simcal-event-toggled"),n=d.data("event-bubble-trigger"),o=g.first().width();if(60>o){f.each(function(a,c){b(c).text(b(c).data("screen-small"))}),h.hide(),i.hide(),"undefined"!=j&&(j.hide(),"undefined"!=m&&m.show()),k.show(),n="click";var p=o-10+"px";g.css("min-height",p),d.find("span.simcal-events-dots:not(:empty)").css("min-height",p)}else 240>=o?f.each(function(a,c){b(c).text(b(c).data("screen-medium"))}):f.each(function(a,c){b(c).text(b(c).data("screen-large"))}),h.show(),i.show(),"undefined"!=j&&(j.show(),"undefined"!=m&&m.hide()),k.hide(),g.css("min-height",o+"px");g.each(function(c,d){var e,f,g=b(d).find("span.simcal-events-dots"),h=b(d).find(".simcal-tooltip");60>o?(l.show(),e=g):(l.hide(),e=h),e.each(function(c,e){b(e).qtip({content:60>o?b(d).find("ul.simcal-events"):b(e).find("> .simcal-tooltip-content"),position:{my:"top center",at:"bottom center",target:b(e),viewport:60>o?b(a):!0,adjust:{method:"shift",scroll:!1}},style:{def:!1,classes:"simcal-default-calendar simcal-event-bubble"},show:{solo:!0,effect:!1,event:"hover"==n?"mouseenter":"click"},hide:{fixed:!0,effect:!1,event:"click"==n?"unfocus":"mouseleave",delay:100},events:{show:function(a,b){f&&f.id&&f.id!=b.id&&f.hide(),f=b}}})})})}function g(){b(".simcal-events-toggle").each(function(a,c){var d=b(c).prev(".simcal-events"),e=d.find(".simcal-event-toggled"),f=b(c).find("i");b(c).on("click",function(){f.toggleClass("simcal-icon-rotate-180"),e.slideToggle()})})}b(".simcal-default-calendar").each(function(a,h){var i,j,k=b(h),l=k.data("calendar-id"),m=k.data("offset"),n=k.data("events-first"),o=k.data("calendar-end"),p=k.find(".simcal-calendar-head"),q=p.find(".simcal-nav-button"),r=k.find(".simcal-ajax-loader"),s=p.find(".simcal-current"),t=s.data("calendar-current"),u=s.find("span.simcal-current-month"),v=s.find("span.simcal-current-year"),w=new Date(1e3*t);k.hasClass("simcal-default-calendar-grid")?(j="simcal_default_calendar_draw_grid",i=new Date(w.getFullYear(),w.getMonth(),1),c(q,i.getTime()/1e3,n,o)):(j="simcal_default_calendar_draw_list",d(q,k,n,o,!1),e(k)),q.on("click",function(){var a=b(this).hasClass("simcal-next")?"next":"prev";if("simcal_default_calendar_draw_grid"==j){var h,p,t,w=k.find(".simcal-month");"prev"==a?h=new Date(i.setMonth(i.getMonth()-1,1)):(h=new Date(i.setMonth(i.getMonth()+2,1)),h.setDate(0),h.setHours(23),h.setMinutes(59),h.setSeconds(59)),p=h.getMonth(),t=h.getFullYear(),b.ajax({url:simcal_default_calendar.ajax_url,method:"POST",dataType:"json",cache:!1,data:{action:j,month:p+1,year:t,id:l},beforeSend:function(){r.fadeToggle()},success:function(a){u.text(simcal_default_calendar.months.full[p]),v.text(t),s.attr("data-calendar-current",h.getTime()/1e3+m+1),c(q,h.getTime()/1e3,n,o),r.fadeToggle(),i=h,w.replaceWith(a.data),f(k,x),g()},error:function(a){console.log(a)}})}else{var x=k.find(".simcal-events-list-container"),y=x.data("prev"),z=x.data("next"),A="prev"==a?y:z;b.ajax({url:simcal_default_calendar.ajax_url,method:"POST",dataType:"json",cache:!1,data:{action:j,ts:A,id:l},beforeSend:function(){r.fadeToggle()},success:function(b){x.replaceWith(b.data),s.attr("data-calendar-current",A),e(k),d(q,k,n,o,a),r.fadeToggle(),g()},error:function(a){console.log(a)}})}})});var h=b(".simcal-default-calendar-grid");h.each(function(a,c){f(c),b(c).on("change",function(){f(this)})}),a.onresize=function(){h.each(function(a,b){f(b)})},g()})}(this);
js/imagesloaded.pkgd.js → assets/js/vendor/imagesloaded.js RENAMED
File without changes
js/imagesloaded.pkgd.min.js → assets/js/vendor/imagesloaded.min.js RENAMED
File without changes
js/jquery.qtip.js → assets/js/vendor/qtip.js RENAMED
@@ -6,8 +6,8 @@
6
  * Released under the MIT licenses
7
  * http://jquery.org/license
8
  *
9
- * Date: Sat Sep 6 2014 09:55 EDT-0400
10
- * Plugins: tips viewport
11
  * Styles: core basic css3
12
  */
13
  /*global window: false, jQuery: false, console: false, define: false */
@@ -1961,7 +1961,7 @@ QTIP.defaults = {
1961
  },
1962
  show: {
1963
  target: FALSE,
1964
- event: BROWSER.iOS ? 'click' : 'mouseenter', // was just : 'mouseenter'
1965
  effect: TRUE,
1966
  delay: 90,
1967
  solo: FALSE,
@@ -1970,7 +1970,7 @@ QTIP.defaults = {
1970
  },
1971
  hide: {
1972
  target: FALSE,
1973
- event: BROWSER.iOS ? 'click' : 'mouseleave', // was just : 'mouseleave'
1974
  effect: TRUE,
1975
  delay: 0,
1976
  fixed: FALSE,
@@ -2620,6 +2620,338 @@ $.extend(TRUE, QTIP.defaults, {
2620
  }
2621
  }
2622
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2623
  ;PLUGINS.viewport = function(api, position, posOptions, targetWidth, targetHeight, elemWidth, elemHeight)
2624
  {
2625
  var target = posOptions.target,
@@ -2727,5 +3059,393 @@ $.extend(TRUE, QTIP.defaults, {
2727
 
2728
  return adjusted;
2729
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2730
  ;}));
2731
  }( window, document ));
6
  * Released under the MIT licenses
7
  * http://jquery.org/license
8
  *
9
+ * Date: Sun Sep 7 2014 12:09 GMT+0100+0100
10
+ * Plugins: tips modal viewport svg imagemap ie6
11
  * Styles: core basic css3
12
  */
13
  /*global window: false, jQuery: false, console: false, define: false */
1961
  },
1962
  show: {
1963
  target: FALSE,
1964
+ event: 'mouseenter',
1965
  effect: TRUE,
1966
  delay: 90,
1967
  solo: FALSE,
1970
  },
1971
  hide: {
1972
  target: FALSE,
1973
+ event: 'mouseleave',
1974
  effect: TRUE,
1975
  delay: 0,
1976
  fixed: FALSE,
2620
  }
2621
  }
2622
  });
2623
+ ;var MODAL, OVERLAY,
2624
+ MODALCLASS = 'qtip-modal',
2625
+ MODALSELECTOR = '.'+MODALCLASS;
2626
+
2627
+ OVERLAY = function()
2628
+ {
2629
+ var self = this,
2630
+ focusableElems = {},
2631
+ current, onLast,
2632
+ prevState, elem;
2633
+
2634
+ // Modified code from jQuery UI 1.10.0 source
2635
+ // http://code.jquery.com/ui/1.10.0/jquery-ui.js
2636
+ function focusable(element) {
2637
+ // Use the defined focusable checker when possible
2638
+ if($.expr[':'].focusable) { return $.expr[':'].focusable; }
2639
+
2640
+ var isTabIndexNotNaN = !isNaN($.attr(element, 'tabindex')),
2641
+ nodeName = element.nodeName && element.nodeName.toLowerCase(),
2642
+ map, mapName, img;
2643
+
2644
+ if('area' === nodeName) {
2645
+ map = element.parentNode;
2646
+ mapName = map.name;
2647
+ if(!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') {
2648
+ return false;
2649
+ }
2650
+ img = $('img[usemap=#' + mapName + ']')[0];
2651
+ return !!img && img.is(':visible');
2652
+ }
2653
+ return (/input|select|textarea|button|object/.test( nodeName ) ?
2654
+ !element.disabled :
2655
+ 'a' === nodeName ?
2656
+ element.href || isTabIndexNotNaN :
2657
+ isTabIndexNotNaN
2658
+ );
2659
+ }
2660
+
2661
+ // Focus inputs using cached focusable elements (see update())
2662
+ function focusInputs(blurElems) {
2663
+ // Blurring body element in IE causes window.open windows to unfocus!
2664
+ if(focusableElems.length < 1 && blurElems.length) { blurElems.not('body').blur(); }
2665
+
2666
+ // Focus the inputs
2667
+ else { focusableElems.first().focus(); }
2668
+ }
2669
+
2670
+ // Steal focus from elements outside tooltip
2671
+ function stealFocus(event) {
2672
+ if(!elem.is(':visible')) { return; }
2673
+
2674
+ var target = $(event.target),
2675
+ tooltip = current.tooltip,
2676
+ container = target.closest(SELECTOR),
2677
+ targetOnTop;
2678
+
2679
+ // Determine if input container target is above this
2680
+ targetOnTop = container.length < 1 ? FALSE :
2681
+ (parseInt(container[0].style.zIndex, 10) > parseInt(tooltip[0].style.zIndex, 10));
2682
+
2683
+ // If we're showing a modal, but focus has landed on an input below
2684
+ // this modal, divert focus to the first visible input in this modal
2685
+ // or if we can't find one... the tooltip itself
2686
+ if(!targetOnTop && target.closest(SELECTOR)[0] !== tooltip[0]) {
2687
+ focusInputs(target);
2688
+ }
2689
+
2690
+ // Detect when we leave the last focusable element...
2691
+ onLast = event.target === focusableElems[focusableElems.length - 1];
2692
+ }
2693
+
2694
+ $.extend(self, {
2695
+ init: function() {
2696
+ // Create document overlay
2697
+ elem = self.elem = $('<div />', {
2698
+ id: 'qtip-overlay',
2699
+ html: '<div></div>',
2700
+ mousedown: function() { return FALSE; }
2701
+ })
2702
+ .hide();
2703
+
2704
+ // Make sure we can't focus anything outside the tooltip
2705
+ $(document.body).bind('focusin'+MODALSELECTOR, stealFocus);
2706
+
2707
+ // Apply keyboard "Escape key" close handler
2708
+ $(document).bind('keydown'+MODALSELECTOR, function(event) {
2709
+ if(current && current.options.show.modal.escape && event.keyCode === 27) {
2710
+ current.hide(event);
2711
+ }
2712
+ });
2713
+
2714
+ // Apply click handler for blur option
2715
+ elem.bind('click'+MODALSELECTOR, function(event) {
2716
+ if(current && current.options.show.modal.blur) {
2717
+ current.hide(event);
2718
+ }
2719
+ });
2720
+
2721
+ return self;
2722
+ },
2723
+
2724
+ update: function(api) {
2725
+ // Update current API reference
2726
+ current = api;
2727
+
2728
+ // Update focusable elements if enabled
2729
+ if(api.options.show.modal.stealfocus !== FALSE) {
2730
+ focusableElems = api.tooltip.find('*').filter(function() {
2731
+ return focusable(this);
2732
+ });
2733
+ }
2734
+ else { focusableElems = []; }
2735
+ },
2736
+
2737
+ toggle: function(api, state, duration) {
2738
+ var docBody = $(document.body),
2739
+ tooltip = api.tooltip,
2740
+ options = api.options.show.modal,
2741
+ effect = options.effect,
2742
+ type = state ? 'show': 'hide',
2743
+ visible = elem.is(':visible'),
2744
+ visibleModals = $(MODALSELECTOR).filter(':visible:not(:animated)').not(tooltip),
2745
+ zindex;
2746
+
2747
+ // Set active tooltip API reference
2748
+ self.update(api);
2749
+
2750
+ // If the modal can steal the focus...
2751
+ // Blur the current item and focus anything in the modal we an
2752
+ if(state && options.stealfocus !== FALSE) {
2753
+ focusInputs( $(':focus') );
2754
+ }
2755
+
2756
+ // Toggle backdrop cursor style on show
2757
+ elem.toggleClass('blurs', options.blur);
2758
+
2759
+ // Append to body on show
2760
+ if(state) {
2761
+ elem.appendTo(document.body);
2762
+ }
2763
+
2764
+ // Prevent modal from conflicting with show.solo, and don't hide backdrop is other modals are visible
2765
+ if((elem.is(':animated') && visible === state && prevState !== FALSE) || (!state && visibleModals.length)) {
2766
+ return self;
2767
+ }
2768
+
2769
+ // Stop all animations
2770
+ elem.stop(TRUE, FALSE);
2771
+
2772
+ // Use custom function if provided
2773
+ if($.isFunction(effect)) {
2774
+ effect.call(elem, state);
2775
+ }
2776
+
2777
+ // If no effect type is supplied, use a simple toggle
2778
+ else if(effect === FALSE) {
2779
+ elem[ type ]();
2780
+ }
2781
+
2782
+ // Use basic fade function
2783
+ else {
2784
+ elem.fadeTo( parseInt(duration, 10) || 90, state ? 1 : 0, function() {
2785
+ if(!state) { elem.hide(); }
2786
+ });
2787
+ }
2788
+
2789
+ // Reset position and detach from body on hide
2790
+ if(!state) {
2791
+ elem.queue(function(next) {
2792
+ elem.css({ left: '', top: '' });
2793
+ if(!$(MODALSELECTOR).length) { elem.detach(); }
2794
+ next();
2795
+ });
2796
+ }
2797
+
2798
+ // Cache the state
2799
+ prevState = state;
2800
+
2801
+ // If the tooltip is destroyed, set reference to null
2802
+ if(current.destroyed) { current = NULL; }
2803
+
2804
+ return self;
2805
+ }
2806
+ });
2807
+
2808
+ self.init();
2809
+ };
2810
+ OVERLAY = new OVERLAY();
2811
+
2812
+ function Modal(api, options) {
2813
+ this.options = options;
2814
+ this._ns = '-modal';
2815
+
2816
+ this.init( (this.qtip = api) );
2817
+ }
2818
+
2819
+ $.extend(Modal.prototype, {
2820
+ init: function(qtip) {
2821
+ var tooltip = qtip.tooltip;
2822
+
2823
+ // If modal is disabled... return
2824
+ if(!this.options.on) { return this; }
2825
+
2826
+ // Set overlay reference
2827
+ qtip.elements.overlay = OVERLAY.elem;
2828
+
2829
+ // Add unique attribute so we can grab modal tooltips easily via a SELECTOR, and set z-index
2830
+ tooltip.addClass(MODALCLASS).css('z-index', QTIP.modal_zindex + $(MODALSELECTOR).length);
2831
+
2832
+ // Apply our show/hide/focus modal events
2833
+ qtip._bind(tooltip, ['tooltipshow', 'tooltiphide'], function(event, api, duration) {
2834
+ var oEvent = event.originalEvent;
2835
+
2836
+ // Make sure mouseout doesn't trigger a hide when showing the modal and mousing onto backdrop
2837
+ if(event.target === tooltip[0]) {
2838
+ if(oEvent && event.type === 'tooltiphide' && /mouse(leave|enter)/.test(oEvent.type) && $(oEvent.relatedTarget).closest(OVERLAY.elem[0]).length) {
2839
+ try { event.preventDefault(); } catch(e) {}
2840
+ }
2841
+ else if(!oEvent || (oEvent && oEvent.type !== 'tooltipsolo')) {
2842
+ this.toggle(event, event.type === 'tooltipshow', duration);
2843
+ }
2844
+ }
2845
+ }, this._ns, this);
2846
+
2847
+ // Adjust modal z-index on tooltip focus
2848
+ qtip._bind(tooltip, 'tooltipfocus', function(event, api) {
2849
+ // If focus was cancelled before it reached us, don't do anything
2850
+ if(event.isDefaultPrevented() || event.target !== tooltip[0]) { return; }
2851
+
2852
+ var qtips = $(MODALSELECTOR),
2853
+
2854
+ // Keep the modal's lower than other, regular qtips
2855
+ newIndex = QTIP.modal_zindex + qtips.length,
2856
+ curIndex = parseInt(tooltip[0].style.zIndex, 10);
2857
+
2858
+ // Set overlay z-index
2859
+ OVERLAY.elem[0].style.zIndex = newIndex - 1;
2860
+
2861
+ // Reduce modal z-index's and keep them properly ordered
2862
+ qtips.each(function() {
2863
+ if(this.style.zIndex > curIndex) {
2864
+ this.style.zIndex -= 1;
2865
+ }
2866
+ });
2867
+
2868
+ // Fire blur event for focused tooltip
2869
+ qtips.filter('.' + CLASS_FOCUS).qtip('blur', event.originalEvent);
2870
+
2871
+ // Set the new z-index
2872
+ tooltip.addClass(CLASS_FOCUS)[0].style.zIndex = newIndex;
2873
+
2874
+ // Set current
2875
+ OVERLAY.update(api);
2876
+
2877
+ // Prevent default handling
2878
+ try { event.preventDefault(); } catch(e) {}
2879
+ }, this._ns, this);
2880
+
2881
+ // Focus any other visible modals when this one hides
2882
+ qtip._bind(tooltip, 'tooltiphide', function(event) {
2883
+ if(event.target === tooltip[0]) {
2884
+ $(MODALSELECTOR).filter(':visible').not(tooltip).last().qtip('focus', event);
2885
+ }
2886
+ }, this._ns, this);
2887
+ },
2888
+
2889
+ toggle: function(event, state, duration) {
2890
+ // Make sure default event hasn't been prevented
2891
+ if(event && event.isDefaultPrevented()) { return this; }
2892
+
2893
+ // Toggle it
2894
+ OVERLAY.toggle(this.qtip, !!state, duration);
2895
+ },
2896
+
2897
+ destroy: function() {
2898
+ // Remove modal class
2899
+ this.qtip.tooltip.removeClass(MODALCLASS);
2900
+
2901
+ // Remove bound events
2902
+ this.qtip._unbind(this.qtip.tooltip, this._ns);
2903
+
2904
+ // Delete element reference
2905
+ OVERLAY.toggle(this.qtip, FALSE);
2906
+ delete this.qtip.elements.overlay;
2907
+ }
2908
+ });
2909
+
2910
+
2911
+ MODAL = PLUGINS.modal = function(api) {
2912
+ return new Modal(api, api.options.show.modal);
2913
+ };
2914
+
2915
+ // Setup sanitiztion rules
2916
+ MODAL.sanitize = function(opts) {
2917
+ if(opts.show) {
2918
+ if(typeof opts.show.modal !== 'object') { opts.show.modal = { on: !!opts.show.modal }; }
2919
+ else if(typeof opts.show.modal.on === 'undefined') { opts.show.modal.on = TRUE; }
2920
+ }
2921
+ };
2922
+
2923
+ // Base z-index for all modal tooltips (use qTip core z-index as a base)
2924
+ QTIP.modal_zindex = QTIP.zindex - 200;
2925
+
2926
+ // Plugin needs to be initialized on render
2927
+ MODAL.initialize = 'render';
2928
+
2929
+ // Setup option set checks
2930
+ CHECKS.modal = {
2931
+ '^show.modal.(on|blur)$': function() {
2932
+ // Initialise
2933
+ this.destroy();
2934
+ this.init();
2935
+
2936
+ // Show the modal if not visible already and tooltip is visible
2937
+ this.qtip.elems.overlay.toggle(
2938
+ this.qtip.tooltip[0].offsetWidth > 0
2939
+ );
2940
+ }
2941
+ };
2942
+
2943
+ // Extend original api defaults
2944
+ $.extend(TRUE, QTIP.defaults, {
2945
+ show: {
2946
+ modal: {
2947
+ on: FALSE,
2948
+ effect: TRUE,
2949
+ blur: TRUE,
2950
+ stealfocus: TRUE,
2951
+ escape: TRUE
2952
+ }
2953
+ }
2954
+ });
2955
  ;PLUGINS.viewport = function(api, position, posOptions, targetWidth, targetHeight, elemWidth, elemHeight)
2956
  {
2957
  var target = posOptions.target,
3059
 
3060
  return adjusted;
3061
  };
3062
+ ;PLUGINS.polys = {
3063
+ // POLY area coordinate calculator
3064
+ // Special thanks to Ed Cradock for helping out with this.
3065
+ // Uses a binary search algorithm to find suitable coordinates.
3066
+ polygon: function(baseCoords, corner) {
3067
+ var result = {
3068
+ width: 0, height: 0,
3069
+ position: {
3070
+ top: 1e10, right: 0,
3071
+ bottom: 0, left: 1e10
3072
+ },
3073
+ adjustable: FALSE
3074
+ },
3075
+ i = 0, next,
3076
+ coords = [],
3077
+ compareX = 1, compareY = 1,
3078
+ realX = 0, realY = 0,
3079
+ newWidth, newHeight;
3080
+
3081
+ // First pass, sanitize coords and determine outer edges
3082
+ i = baseCoords.length; while(i--) {
3083
+ next = [ parseInt(baseCoords[--i], 10), parseInt(baseCoords[i+1], 10) ];
3084
+
3085
+ if(next[0] > result.position.right){ result.position.right = next[0]; }
3086
+ if(next[0] < result.position.left){ result.position.left = next[0]; }
3087
+ if(next[1] > result.position.bottom){ result.position.bottom = next[1]; }
3088
+ if(next[1] < result.position.top){ result.position.top = next[1]; }
3089
+
3090
+ coords.push(next);
3091
+ }
3092
+
3093
+ // Calculate height and width from outer edges
3094
+ newWidth = result.width = Math.abs(result.position.right - result.position.left);
3095
+ newHeight = result.height = Math.abs(result.position.bottom - result.position.top);
3096
+
3097
+ // If it's the center corner...
3098
+ if(corner.abbrev() === 'c') {
3099
+ result.position = {
3100
+ left: result.position.left + (result.width / 2),
3101
+ top: result.position.top + (result.height / 2)
3102
+ };
3103
+ }
3104
+ else {
3105
+ // Second pass, use a binary search algorithm to locate most suitable coordinate
3106
+ while(newWidth > 0 && newHeight > 0 && compareX > 0 && compareY > 0)
3107
+ {
3108
+ newWidth = Math.floor(newWidth / 2);
3109
+ newHeight = Math.floor(newHeight / 2);
3110
+
3111
+ if(corner.x === LEFT){ compareX = newWidth; }
3112
+ else if(corner.x === RIGHT){ compareX = result.width - newWidth; }
3113
+ else{ compareX += Math.floor(newWidth / 2); }
3114
+
3115
+ if(corner.y === TOP){ compareY = newHeight; }
3116
+ else if(corner.y === BOTTOM){ compareY = result.height - newHeight; }
3117
+ else{ compareY += Math.floor(newHeight / 2); }
3118
+
3119
+ i = coords.length; while(i--)
3120
+ {
3121
+ if(coords.length < 2){ break; }
3122
+
3123
+ realX = coords[i][0] - result.position.left;
3124
+ realY = coords[i][1] - result.position.top;
3125
+
3126
+ if((corner.x === LEFT && realX >= compareX) ||
3127
+ (corner.x === RIGHT && realX <= compareX) ||
3128
+ (corner.x === CENTER && (realX < compareX || realX > (result.width - compareX))) ||
3129
+ (corner.y === TOP && realY >= compareY) ||
3130
+ (corner.y === BOTTOM && realY <= compareY) ||
3131
+ (corner.y === CENTER && (realY < compareY || realY > (result.height - compareY)))) {
3132
+ coords.splice(i, 1);
3133
+ }
3134
+ }
3135
+ }
3136
+ result.position = { left: coords[0][0], top: coords[0][1] };
3137
+ }
3138
+
3139
+ return result;
3140
+ },
3141
+
3142
+ rect: function(ax, ay, bx, by) {
3143
+ return {
3144
+ width: Math.abs(bx - ax),
3145
+ height: Math.abs(by - ay),
3146
+ position: {
3147
+ left: Math.min(ax, bx),
3148
+ top: Math.min(ay, by)
3149
+ }
3150
+ };
3151
+ },
3152
+
3153
+ _angles: {
3154
+ tc: 3 / 2, tr: 7 / 4, tl: 5 / 4,
3155
+ bc: 1 / 2, br: 1 / 4, bl: 3 / 4,
3156
+ rc: 2, lc: 1, c: 0
3157
+ },
3158
+ ellipse: function(cx, cy, rx, ry, corner) {
3159
+ var c = PLUGINS.polys._angles[ corner.abbrev() ],
3160
+ rxc = c === 0 ? 0 : rx * Math.cos( c * Math.PI ),
3161
+ rys = ry * Math.sin( c * Math.PI );
3162
+
3163
+ return {
3164
+ width: (rx * 2) - Math.abs(rxc),
3165
+ height: (ry * 2) - Math.abs(rys),
3166
+ position: {
3167
+ left: cx + rxc,
3168
+ top: cy + rys
3169
+ },
3170
+ adjustable: FALSE
3171
+ };
3172
+ },
3173
+ circle: function(cx, cy, r, corner) {
3174
+ return PLUGINS.polys.ellipse(cx, cy, r, r, corner);
3175
+ }
3176
+ };
3177
+ ;PLUGINS.svg = function(api, svg, corner)
3178
+ {
3179
+ var doc = $(document),
3180
+ elem = svg[0],
3181
+ root = $(elem.ownerSVGElement),
3182
+ ownerDocument = elem.ownerDocument,
3183
+ strokeWidth2 = (parseInt(svg.css('stroke-width'), 10) || 0) / 2,
3184
+ frameOffset, mtx, transformed, viewBox,
3185
+ len, next, i, points,
3186
+ result, position, dimensions;
3187
+
3188
+ // Ascend the parentNode chain until we find an element with getBBox()
3189
+ while(!elem.getBBox) { elem = elem.parentNode; }
3190
+ if(!elem.getBBox || !elem.parentNode) { return FALSE; }
3191
+
3192
+ // Determine which shape calculation to use
3193
+ switch(elem.nodeName) {
3194
+ case 'ellipse':
3195
+ case 'circle':
3196
+ result = PLUGINS.polys.ellipse(
3197
+ elem.cx.baseVal.value,
3198
+ elem.cy.baseVal.value,
3199
+ (elem.rx || elem.r).baseVal.value + strokeWidth2,
3200
+ (elem.ry || elem.r).baseVal.value + strokeWidth2,
3201
+ corner
3202
+ );
3203
+ break;
3204
+
3205
+ case 'line':
3206
+ case 'polygon':
3207
+ case 'polyline':
3208
+ // Determine points object (line has none, so mimic using array)
3209
+ points = elem.points || [
3210
+ { x: elem.x1.baseVal.value, y: elem.y1.baseVal.value },
3211
+ { x: elem.x2.baseVal.value, y: elem.y2.baseVal.value }
3212
+ ];
3213
+
3214
+ for(result = [], i = -1, len = points.numberOfItems || points.length; ++i < len;) {
3215
+ next = points.getItem ? points.getItem(i) : points[i];
3216
+ result.push.apply(result, [next.x, next.y]);
3217
+ }
3218
+
3219
+ result = PLUGINS.polys.polygon(result, corner);
3220
+ break;
3221
+
3222
+ // Unknown shape or rectangle? Use bounding box
3223
+ default:
3224
+ result = elem.getBBox();
3225
+ result = {
3226
+ width: result.width,
3227
+ height: result.height,
3228
+ position: {
3229
+ left: result.x,
3230
+ top: result.y
3231
+ }
3232
+ };
3233
+ break;
3234
+ }
3235
+
3236
+ // Shortcut assignments
3237
+ position = result.position;
3238
+ root = root[0];
3239
+
3240
+ // Convert position into a pixel value
3241
+ if(root.createSVGPoint) {
3242
+ mtx = elem.getScreenCTM();
3243
+ points = root.createSVGPoint();
3244
+
3245
+ points.x = position.left;
3246
+ points.y = position.top;
3247
+ transformed = points.matrixTransform( mtx );
3248
+ position.left = transformed.x;
3249
+ position.top = transformed.y;
3250
+ }
3251
+
3252
+ // Check the element is not in a child document, and if so, adjust for frame elements offset
3253
+ if(ownerDocument !== document && api.position.target !== 'mouse') {
3254
+ frameOffset = $((ownerDocument.defaultView || ownerDocument.parentWindow).frameElement).offset();
3255
+ if(frameOffset) {
3256
+ position.left += frameOffset.left;
3257
+ position.top += frameOffset.top;
3258
+ }
3259
+ }
3260
+
3261
+ // Adjust by scroll offset of owner document
3262
+ ownerDocument = $(ownerDocument);
3263
+ position.left += ownerDocument.scrollLeft();
3264
+ position.top += ownerDocument.scrollTop();
3265
+
3266
+ return result;
3267
+ };
3268
+ ;PLUGINS.imagemap = function(api, area, corner, adjustMethod)
3269
+ {
3270
+ if(!area.jquery) { area = $(area); }
3271
+
3272
+ var shape = (area.attr('shape') || 'rect').toLowerCase().replace('poly', 'polygon'),
3273
+ image = $('img[usemap="#'+area.parent('map').attr('name')+'"]'),
3274
+ coordsString = $.trim(area.attr('coords')),
3275
+ coordsArray = coordsString.replace(/,$/, '').split(','),
3276
+ imageOffset, coords, i, next, result, len;
3277
+
3278
+ // If we can't find the image using the map...
3279
+ if(!image.length) { return FALSE; }
3280
+
3281
+ // Pass coordinates string if polygon
3282
+ if(shape === 'polygon') {
3283
+ result = PLUGINS.polys.polygon(coordsArray, corner);
3284
+ }
3285
+
3286
+ // Otherwise parse the coordinates and pass them as arguments
3287
+ else if(PLUGINS.polys[shape]) {
3288
+ for(i = -1, len = coordsArray.length, coords = []; ++i < len;) {
3289
+ coords.push( parseInt(coordsArray[i], 10) );
3290
+ }
3291
+
3292
+ result = PLUGINS.polys[shape].apply(
3293
+ this, coords.concat(corner)
3294
+ );
3295
+ }
3296
+
3297
+ // If no shapre calculation method was found, return false
3298
+ else { return FALSE; }
3299
+
3300
+ // Make sure we account for padding and borders on the image
3301
+ imageOffset = image.offset();
3302
+ imageOffset.left += Math.ceil((image.outerWidth(FALSE) - image.width()) / 2);
3303
+ imageOffset.top += Math.ceil((image.outerHeight(FALSE) - image.height()) / 2);
3304
+
3305
+ // Add image position to offset coordinates
3306
+ result.position.left += imageOffset.left;
3307
+ result.position.top += imageOffset.top;
3308
+
3309
+ return result;
3310
+ };
3311
+ ;var IE6,
3312
+
3313
+ /*
3314
+ * BGIFrame adaption (http://plugins.jquery.com/project/bgiframe)
3315
+ * Special thanks to Brandon Aaron
3316
+ */
3317
+ BGIFRAME = '<iframe class="qtip-bgiframe" frameborder="0" tabindex="-1" src="javascript:\'\';" ' +
3318
+ ' style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=0); ' +
3319
+ '-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";"></iframe>';
3320
+
3321
+ function Ie6(api, qtip) {
3322
+ this._ns = 'ie6';
3323
+ this.init( (this.qtip = api) );
3324
+ }
3325
+
3326
+ $.extend(Ie6.prototype, {
3327
+ _scroll : function() {
3328
+ var overlay = this.qtip.elements.overlay;
3329
+ overlay && (overlay[0].style.top = $(window).scrollTop() + 'px');
3330
+ },
3331
+
3332
+ init: function(qtip) {
3333
+ var tooltip = qtip.tooltip,
3334
+ scroll;
3335
+
3336
+ // Create the BGIFrame element if needed
3337
+ if($('select, object').length < 1) {
3338
+ this.bgiframe = qtip.elements.bgiframe = $(BGIFRAME).appendTo(tooltip);
3339
+
3340
+ // Update BGIFrame on tooltip move
3341
+ qtip._bind(tooltip, 'tooltipmove', this.adjustBGIFrame, this._ns, this);
3342
+ }
3343
+
3344
+ // redraw() container for width/height calculations
3345
+ this.redrawContainer = $('<div/>', { id: NAMESPACE+'-rcontainer' })
3346
+ .appendTo(document.body);
3347
+
3348
+ // Fixup modal plugin if present too
3349
+ if( qtip.elements.overlay && qtip.elements.overlay.addClass('qtipmodal-ie6fix') ) {
3350
+ qtip._bind(window, ['scroll', 'resize'], this._scroll, this._ns, this);
3351
+ qtip._bind(tooltip, ['tooltipshow'], this._scroll, this._ns, this);
3352
+ }
3353
+
3354
+ // Set dimensions
3355
+ this.redraw();
3356
+ },
3357
+
3358
+ adjustBGIFrame: function() {
3359
+ var tooltip = this.qtip.tooltip,
3360
+ dimensions = {
3361
+ height: tooltip.outerHeight(FALSE),
3362
+ width: tooltip.outerWidth(FALSE)
3363
+ },
3364
+ plugin = this.qtip.plugins.tip,
3365
+ tip = this.qtip.elements.tip,
3366
+ tipAdjust, offset;
3367
+
3368
+ // Adjust border offset
3369
+ offset = parseInt(tooltip.css('borderLeftWidth'), 10) || 0;
3370
+ offset = { left: -offset, top: -offset };
3371
+
3372
+ // Adjust for tips plugin
3373
+ if(plugin && tip) {
3374
+ tipAdjust = (plugin.corner.precedance === 'x') ? [WIDTH, LEFT] : [HEIGHT, TOP];
3375
+ offset[ tipAdjust[1] ] -= tip[ tipAdjust[0] ]();
3376
+ }
3377
+
3378
+ // Update bgiframe
3379
+ this.bgiframe.css(offset).css(dimensions);
3380
+ },
3381
+
3382
+ // Max/min width simulator function
3383
+ redraw: function() {
3384
+ if(this.qtip.rendered < 1 || this.drawing) { return this; }
3385
+
3386
+ var tooltip = this.qtip.tooltip,
3387
+ style = this.qtip.options.style,
3388
+ container = this.qtip.options.position.container,
3389
+ perc, width, max, min;
3390
+
3391
+ // Set drawing flag
3392
+ this.qtip.drawing = 1;
3393
+
3394
+ // If tooltip has a set height/width, just set it... like a boss!
3395
+ if(style.height) { tooltip.css(HEIGHT, style.height); }
3396
+ if(style.width) { tooltip.css(WIDTH, style.width); }
3397
+
3398
+ // Simulate max/min width if not set width present...
3399
+ else {
3400
+ // Reset width and add fluid class
3401
+ tooltip.css(WIDTH, '').appendTo(this.redrawContainer);
3402
+
3403
+ // Grab our tooltip width (add 1 if odd so we don't get wrapping problems.. huzzah!)
3404
+ width = tooltip.width();
3405
+ if(width % 2 < 1) { width += 1; }
3406
+
3407
+ // Grab our max/min properties
3408
+ max = tooltip.css('maxWidth') || '';
3409
+ min = tooltip.css('minWidth') || '';
3410
+
3411
+ // Parse into proper pixel values
3412
+ perc = (max + min).indexOf('%') > -1 ? container.width() / 100 : 0;
3413
+ max = ((max.indexOf('%') > -1 ? perc : 1) * parseInt(max, 10)) || width;
3414
+ min = ((min.indexOf('%') > -1 ? perc : 1) * parseInt(min, 10)) || 0;
3415
+
3416
+ // Determine new dimension size based on max/min/current values
3417
+ width = max + min ? Math.min(Math.max(width, min), max) : width;
3418
+
3419
+ // Set the newly calculated width and remvoe fluid class
3420
+ tooltip.css(WIDTH, Math.round(width)).appendTo(container);
3421
+ }
3422
+
3423
+ // Set drawing flag
3424
+ this.drawing = 0;
3425
+
3426
+ return this;
3427
+ },
3428
+
3429
+ destroy: function() {
3430
+ // Remove iframe
3431
+ this.bgiframe && this.bgiframe.remove();
3432
+
3433
+ // Remove bound events
3434
+ this.qtip._unbind([window, this.qtip.tooltip], this._ns);
3435
+ }
3436
+ });
3437
+
3438
+ IE6 = PLUGINS.ie6 = function(api) {
3439
+ // Proceed only if the browser is IE6
3440
+ return BROWSER.ie === 6 ? new Ie6(api) : FALSE;
3441
+ };
3442
+
3443
+ IE6.initialize = 'render';
3444
+
3445
+ CHECKS.ie6 = {
3446
+ '^content|style$': function() {
3447
+ this.redraw();
3448
+ }
3449
+ };
3450
  ;}));
3451
  }( window, document ));
assets/js/vendor/qtip.min.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ /* qTip2 v2.2.1 | Plugins: tips modal viewport svg imagemap ie6 | Styles: core basic css3 | qtip2.com | Licensed MIT | Sun Sep 07 2014 00:09:27 */
2
+
3
+ !function(a,b,c){!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):jQuery&&!jQuery.fn.qtip&&a(jQuery)}(function(d){"use strict";function e(a,b,c,e){this.id=c,this.target=a,this.tooltip=F,this.elements={target:a},this._id=S+"-"+c,this.timers={img:{}},this.options=b,this.plugins={},this.cache={event:{},target:d(),disabled:E,attr:e,onTooltip:E,lastClass:""},this.rendered=this.destroyed=this.disabled=this.waiting=this.hiddenDuringWait=this.positioning=this.triggering=E}function f(a){return a===F||"object"!==d.type(a)}function g(a){return!(d.isFunction(a)||a&&a.attr||a.length||"object"===d.type(a)&&(a.jquery||a.then))}function h(a){var b,c,e,h;return f(a)?E:(f(a.metadata)&&(a.metadata={type:a.metadata}),"content"in a&&(b=a.content,f(b)||b.jquery||b.done?b=a.content={text:c=g(b)?E:b}:c=b.text,"ajax"in b&&(e=b.ajax,h=e&&e.once!==E,delete b.ajax,b.text=function(a,b){var f=c||d(this).attr(b.options.content.attr)||"Loading...",g=d.ajax(d.extend({},e,{context:b})).then(e.success,F,e.error).then(function(a){return a&&h&&b.set("content.text",a),a},function(a,c,d){b.destroyed||0===a.status||b.set("content.text",c+": "+d)});return h?f:(b.set("content.text",f),g)}),"title"in b&&(d.isPlainObject(b.title)&&(b.button=b.title.button,b.title=b.title.text),g(b.title||E)&&(b.title=E))),"position"in a&&f(a.position)&&(a.position={my:a.position,at:a.position}),"show"in a&&f(a.show)&&(a.show=a.show.jquery?{target:a.show}:a.show===D?{ready:D}:{event:a.show}),"hide"in a&&f(a.hide)&&(a.hide=a.hide.jquery?{target:a.hide}:{event:a.hide}),"style"in a&&f(a.style)&&(a.style={classes:a.style}),d.each(R,function(){this.sanitize&&this.sanitize(a)}),a)}function i(a,b){for(var c,d=0,e=a,f=b.split(".");e=e[f[d++]];)d<f.length&&(c=e);return[c||a,f.pop()]}function j(a,b){var c,d,e;for(c in this.checks)for(d in this.checks[c])(e=new RegExp(d,"i").exec(a))&&(b.push(e),("builtin"===c||this.plugins[c])&&this.checks[c][d].apply(this.plugins[c]||this,b))}function k(a){return V.concat("").join(a?"-"+a+" ":" ")}function l(a,b){return b>0?setTimeout(d.proxy(a,this),b):void a.call(this)}function m(a){this.tooltip.hasClass(ab)||(clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this.timers.show=l.call(this,function(){this.toggle(D,a)},this.options.show.delay))}function n(a){if(!this.tooltip.hasClass(ab)&&!this.destroyed){var b=d(a.relatedTarget),c=b.closest(W)[0]===this.tooltip[0],e=b[0]===this.options.show.target[0];if(clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this!==b[0]&&"mouse"===this.options.position.target&&c||this.options.hide.fixed&&/mouse(out|leave|move)/.test(a.type)&&(c||e))try{a.preventDefault(),a.stopImmediatePropagation()}catch(f){}else this.timers.hide=l.call(this,function(){this.toggle(E,a)},this.options.hide.delay,this)}}function o(a){!this.tooltip.hasClass(ab)&&this.options.hide.inactive&&(clearTimeout(this.timers.inactive),this.timers.inactive=l.call(this,function(){this.hide(a)},this.options.hide.inactive))}function p(a){this.rendered&&this.tooltip[0].offsetWidth>0&&this.reposition(a)}function q(a,c,e){d(b.body).delegate(a,(c.split?c:c.join("."+S+" "))+"."+S,function(){var a=y.api[d.attr(this,U)];a&&!a.disabled&&e.apply(a,arguments)})}function r(a,c,f){var g,i,j,k,l,m=d(b.body),n=a[0]===b?m:a,o=a.metadata?a.metadata(f.metadata):F,p="html5"===f.metadata.type&&o?o[f.metadata.name]:F,q=a.data(f.metadata.name||"qtipopts");try{q="string"==typeof q?d.parseJSON(q):q}catch(r){}if(k=d.extend(D,{},y.defaults,f,"object"==typeof q?h(q):F,h(p||o)),i=k.position,k.id=c,"boolean"==typeof k.content.text){if(j=a.attr(k.content.attr),k.content.attr===E||!j)return E;k.content.text=j}if(i.container.length||(i.container=m),i.target===E&&(i.target=n),k.show.target===E&&(k.show.target=n),k.show.solo===D&&(k.show.solo=i.container.closest("body")),k.hide.target===E&&(k.hide.target=n),k.position.viewport===D&&(k.position.viewport=i.container),i.container=i.container.eq(0),i.at=new A(i.at,D),i.my=new A(i.my),a.data(S))if(k.overwrite)a.qtip("destroy",!0);else if(k.overwrite===E)return E;return a.attr(T,c),k.suppress&&(l=a.attr("title"))&&a.removeAttr("title").attr(cb,l).attr("title",""),g=new e(a,k,c,!!j),a.data(S,g),g}function s(a){return a.charAt(0).toUpperCase()+a.slice(1)}function t(a,b){var d,e,f=b.charAt(0).toUpperCase()+b.slice(1),g=(b+" "+rb.join(f+" ")+f).split(" "),h=0;if(qb[b])return a.css(qb[b]);for(;d=g[h++];)if((e=a.css(d))!==c)return qb[b]=d,e}function u(a,b){return Math.ceil(parseFloat(t(a,b)))}function v(a,b){this._ns="tip",this.options=b,this.offset=b.offset,this.size=[b.width,b.height],this.init(this.qtip=a)}function w(a,b){this.options=b,this._ns="-modal",this.init(this.qtip=a)}function x(a){this._ns="ie6",this.init(this.qtip=a)}var y,z,A,B,C,D=!0,E=!1,F=null,G="x",H="y",I="width",J="height",K="top",L="left",M="bottom",N="right",O="center",P="flipinvert",Q="shift",R={},S="qtip",T="data-hasqtip",U="data-qtip-id",V=["ui-widget","ui-tooltip"],W="."+S,X="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),Y=S+"-fixed",Z=S+"-default",$=S+"-focus",_=S+"-hover",ab=S+"-disabled",bb="_replacedByqTip",cb="oldtitle",db={ie:function(){for(var a=4,c=b.createElement("div");(c.innerHTML="<!--[if gt IE "+a+"]><i></i><![endif]-->")&&c.getElementsByTagName("i")[0];a+=1);return a>4?a:0/0}(),iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||E};z=e.prototype,z._when=function(a){return d.when.apply(d,a)},z.render=function(a){if(this.rendered||this.destroyed)return this;var b,c=this,e=this.options,f=this.cache,g=this.elements,h=e.content.text,i=e.content.title,j=e.content.button,k=e.position,l=("."+this._id+" ",[]);return d.attr(this.target[0],"aria-describedby",this._id),f.posClass=this._createPosClass((this.position={my:k.my,at:k.at}).my),this.tooltip=g.tooltip=b=d("<div/>",{id:this._id,"class":[S,Z,e.style.classes,f.posClass].join(" "),width:e.style.width||"",height:e.style.height||"",tracking:"mouse"===k.target&&k.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":E,"aria-describedby":this._id+"-content","aria-hidden":D}).toggleClass(ab,this.disabled).attr(U,this.id).data(S,this).appendTo(k.container).append(g.content=d("<div />",{"class":S+"-content",id:this._id+"-content","aria-atomic":D})),this.rendered=-1,this.positioning=D,i&&(this._createTitle(),d.isFunction(i)||l.push(this._updateTitle(i,E))),j&&this._createButton(),d.isFunction(h)||l.push(this._updateContent(h,E)),this.rendered=D,this._setWidget(),d.each(R,function(a){var b;"render"===this.initialize&&(b=this(c))&&(c.plugins[a]=b)}),this._unassignEvents(),this._assignEvents(),this._when(l).then(function(){c._trigger("render"),c.positioning=E,c.hiddenDuringWait||!e.show.ready&&!a||c.toggle(D,f.event,E),c.hiddenDuringWait=E}),y.api[this.id]=this,this},z.destroy=function(a){function b(){if(!this.destroyed){this.destroyed=D;var a,b=this.target,c=b.attr(cb);this.rendered&&this.tooltip.stop(1,0).find("*").remove().end().remove(),d.each(this.plugins,function(){this.destroy&&this.destroy()});for(a in this.timers)clearTimeout(this.timers[a]);b.removeData(S).removeAttr(U).removeAttr(T).removeAttr("aria-describedby"),this.options.suppress&&c&&b.attr("title",c).removeAttr(cb),this._unassignEvents(),this.options=this.elements=this.cache=this.timers=this.plugins=this.mouse=F,delete y.api[this.id]}}return this.destroyed?this.target:(a===D&&"hide"!==this.triggering||!this.rendered?b.call(this):(this.tooltip.one("tooltiphidden",d.proxy(b,this)),!this.triggering&&this.hide()),this.target)},B=z.checks={builtin:{"^id$":function(a,b,c,e){var f=c===D?y.nextid:c,g=S+"-"+f;f!==E&&f.length>0&&!d("#"+g).length?(this._id=g,this.rendered&&(this.tooltip[0].id=this._id,this.elements.content[0].id=this._id+"-content",this.elements.title[0].id=this._id+"-title")):a[b]=e},"^prerender":function(a,b,c){c&&!this.rendered&&this.render(this.options.show.ready)},"^content.text$":function(a,b,c){this._updateContent(c)},"^content.attr$":function(a,b,c,d){this.options.content.text===this.target.attr(d)&&this._updateContent(this.target.attr(c))},"^content.title$":function(a,b,c){return c?(c&&!this.elements.title&&this._createTitle(),void this._updateTitle(c)):this._removeTitle()},"^content.button$":function(a,b,c){this._updateButton(c)},"^content.title.(text|button)$":function(a,b,c){this.set("content."+b,c)},"^position.(my|at)$":function(a,b,c){"string"==typeof c&&(this.position[b]=a[b]=new A(c,"at"===b))},"^position.container$":function(a,b,c){this.rendered&&this.tooltip.appendTo(c)},"^show.ready$":function(a,b,c){c&&(!this.rendered&&this.render(D)||this.toggle(D))},"^style.classes$":function(a,b,c,d){this.rendered&&this.tooltip.removeClass(d).addClass(c)},"^style.(width|height)":function(a,b,c){this.rendered&&this.tooltip.css(b,c)},"^style.widget|content.title":function(){this.rendered&&this._setWidget()},"^style.def":function(a,b,c){this.rendered&&this.tooltip.toggleClass(Z,!!c)},"^events.(render|show|move|hide|focus|blur)$":function(a,b,c){this.rendered&&this.tooltip[(d.isFunction(c)?"":"un")+"bind"]("tooltip"+b,c)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){if(this.rendered){var a=this.options.position;this.tooltip.attr("tracking","mouse"===a.target&&a.adjust.mouse),this._unassignEvents(),this._assignEvents()}}}},z.get=function(a){if(this.destroyed)return this;var b=i(this.options,a.toLowerCase()),c=b[0][b[1]];return c.precedance?c.string():c};var eb=/^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i,fb=/^prerender|show\.ready/i;z.set=function(a,b){if(this.destroyed)return this;{var c,e=this.rendered,f=E,g=this.options;this.checks}return"string"==typeof a?(c=a,a={},a[c]=b):a=d.extend({},a),d.each(a,function(b,c){if(e&&fb.test(b))return void delete a[b];var h,j=i(g,b.toLowerCase());h=j[0][j[1]],j[0][j[1]]=c&&c.nodeType?d(c):c,f=eb.test(b)||f,a[b]=[j[0],j[1],c,h]}),h(g),this.positioning=D,d.each(a,d.proxy(j,this)),this.positioning=E,this.rendered&&this.tooltip[0].offsetWidth>0&&f&&this.reposition("mouse"===g.position.target?F:this.cache.event),this},z._update=function(a,b){var c=this,e=this.cache;return this.rendered&&a?(d.isFunction(a)&&(a=a.call(this.elements.target,e.event,this)||""),d.isFunction(a.then)?(e.waiting=D,a.then(function(a){return e.waiting=E,c._update(a,b)},F,function(a){return c._update(a,b)})):a===E||!a&&""!==a?E:(a.jquery&&a.length>0?b.empty().append(a.css({display:"block",visibility:"visible"})):b.html(a),this._waitForContent(b).then(function(a){c.rendered&&c.tooltip[0].offsetWidth>0&&c.reposition(e.event,!a.length)}))):E},z._waitForContent=function(a){var b=this.cache;return b.waiting=D,(d.fn.imagesLoaded?a.imagesLoaded():d.Deferred().resolve([])).done(function(){b.waiting=E}).promise()},z._updateContent=function(a,b){this._update(a,this.elements.content,b)},z._updateTitle=function(a,b){this._update(a,this.elements.title,b)===E&&this._removeTitle(E)},z._createTitle=function(){var a=this.elements,b=this._id+"-title";a.titlebar&&this._removeTitle(),a.titlebar=d("<div />",{"class":S+"-titlebar "+(this.options.style.widget?k("header"):"")}).append(a.title=d("<div />",{id:b,"class":S+"-title","aria-atomic":D})).insertBefore(a.content).delegate(".qtip-close","mousedown keydown mouseup keyup mouseout",function(a){d(this).toggleClass("ui-state-active ui-state-focus","down"===a.type.substr(-4))}).delegate(".qtip-close","mouseover mouseout",function(a){d(this).toggleClass("ui-state-hover","mouseover"===a.type)}),this.options.content.button&&this._createButton()},z._removeTitle=function(a){var b=this.elements;b.title&&(b.titlebar.remove(),b.titlebar=b.title=b.button=F,a!==E&&this.reposition())},z._createPosClass=function(a){return S+"-pos-"+(a||this.options.position.my).abbrev()},z.reposition=function(c,e){if(!this.rendered||this.positioning||this.destroyed)return this;this.positioning=D;var f,g,h,i,j=this.cache,k=this.tooltip,l=this.options.position,m=l.target,n=l.my,o=l.at,p=l.viewport,q=l.container,r=l.adjust,s=r.method.split(" "),t=k.outerWidth(E),u=k.outerHeight(E),v=0,w=0,x=k.css("position"),y={left:0,top:0},z=k[0].offsetWidth>0,A=c&&"scroll"===c.type,B=d(a),C=q[0].ownerDocument,F=this.mouse;if(d.isArray(m)&&2===m.length)o={x:L,y:K},y={left:m[0],top:m[1]};else if("mouse"===m)o={x:L,y:K},(!r.mouse||this.options.hide.distance)&&j.origin&&j.origin.pageX?c=j.origin:!c||c&&("resize"===c.type||"scroll"===c.type)?c=j.event:F&&F.pageX&&(c=F),"static"!==x&&(y=q.offset()),C.body.offsetWidth!==(a.innerWidth||C.documentElement.clientWidth)&&(g=d(b.body).offset()),y={left:c.pageX-y.left+(g&&g.left||0),top:c.pageY-y.top+(g&&g.top||0)},r.mouse&&A&&F&&(y.left-=(F.scrollX||0)-B.scrollLeft(),y.top-=(F.scrollY||0)-B.scrollTop());else{if("event"===m?c&&c.target&&"scroll"!==c.type&&"resize"!==c.type?j.target=d(c.target):c.target||(j.target=this.elements.target):"event"!==m&&(j.target=d(m.jquery?m:this.elements.target)),m=j.target,m=d(m).eq(0),0===m.length)return this;m[0]===b||m[0]===a?(v=db.iOS?a.innerWidth:m.width(),w=db.iOS?a.innerHeight:m.height(),m[0]===a&&(y={top:(p||m).scrollTop(),left:(p||m).scrollLeft()})):R.imagemap&&m.is("area")?f=R.imagemap(this,m,o,R.viewport?s:E):R.svg&&m&&m[0].ownerSVGElement?f=R.svg(this,m,o,R.viewport?s:E):(v=m.outerWidth(E),w=m.outerHeight(E),y=m.offset()),f&&(v=f.width,w=f.height,g=f.offset,y=f.position),y=this.reposition.offset(m,y,q),(db.iOS>3.1&&db.iOS<4.1||db.iOS>=4.3&&db.iOS<4.33||!db.iOS&&"fixed"===x)&&(y.left-=B.scrollLeft(),y.top-=B.scrollTop()),(!f||f&&f.adjustable!==E)&&(y.left+=o.x===N?v:o.x===O?v/2:0,y.top+=o.y===M?w:o.y===O?w/2:0)}return y.left+=r.x+(n.x===N?-t:n.x===O?-t/2:0),y.top+=r.y+(n.y===M?-u:n.y===O?-u/2:0),R.viewport?(h=y.adjusted=R.viewport(this,y,l,v,w,t,u),g&&h.left&&(y.left+=g.left),g&&h.top&&(y.top+=g.top),h.my&&(this.position.my=h.my)):y.adjusted={left:0,top:0},j.posClass!==(i=this._createPosClass(this.position.my))&&k.removeClass(j.posClass).addClass(j.posClass=i),this._trigger("move",[y,p.elem||p],c)?(delete y.adjusted,e===E||!z||isNaN(y.left)||isNaN(y.top)||"mouse"===m||!d.isFunction(l.effect)?k.css(y):d.isFunction(l.effect)&&(l.effect.call(k,this,d.extend({},y)),k.queue(function(a){d(this).css({opacity:"",height:""}),db.ie&&this.style.removeAttribute("filter"),a()})),this.positioning=E,this):this},z.reposition.offset=function(a,c,e){function f(a,b){c.left+=b*a.scrollLeft(),c.top+=b*a.scrollTop()}if(!e[0])return c;var g,h,i,j,k=d(a[0].ownerDocument),l=!!db.ie&&"CSS1Compat"!==b.compatMode,m=e[0];do"static"!==(h=d.css(m,"position"))&&("fixed"===h?(i=m.getBoundingClientRect(),f(k,-1)):(i=d(m).position(),i.left+=parseFloat(d.css(m,"borderLeftWidth"))||0,i.top+=parseFloat(d.css(m,"borderTopWidth"))||0),c.left-=i.left+(parseFloat(d.css(m,"marginLeft"))||0),c.top-=i.top+(parseFloat(d.css(m,"marginTop"))||0),g||"hidden"===(j=d.css(m,"overflow"))||"visible"===j||(g=d(m)));while(m=m.offsetParent);return g&&(g[0]!==k[0]||l)&&f(g,1),c};var gb=(A=z.reposition.Corner=function(a,b){a=(""+a).replace(/([A-Z])/," $1").replace(/middle/gi,O).toLowerCase(),this.x=(a.match(/left|right/i)||a.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(a.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase(),this.forceY=!!b;var c=a.charAt(0);this.precedance="t"===c||"b"===c?H:G}).prototype;gb.invert=function(a,b){this[a]=this[a]===L?N:this[a]===N?L:b||this[a]},gb.string=function(a){var b=this.x,c=this.y,d=b!==c?"center"===b||"center"!==c&&(this.precedance===H||this.forceY)?[c,b]:[b,c]:[b];return a!==!1?d.join(" "):d},gb.abbrev=function(){var a=this.string(!1);return a[0].charAt(0)+(a[1]&&a[1].charAt(0)||"")},gb.clone=function(){return new A(this.string(),this.forceY)},z.toggle=function(a,c){var e=this.cache,f=this.options,g=this.tooltip;if(c){if(/over|enter/.test(c.type)&&e.event&&/out|leave/.test(e.event.type)&&f.show.target.add(c.target).length===f.show.target.length&&g.has(c.relatedTarget).length)return this;e.event=d.event.fix(c)}if(this.waiting&&!a&&(this.hiddenDuringWait=D),!this.rendered)return a?this.render(1):this;if(this.destroyed||this.disabled)return this;var h,i,j,k=a?"show":"hide",l=this.options[k],m=(this.options[a?"hide":"show"],this.options.position),n=this.options.content,o=this.tooltip.css("width"),p=this.tooltip.is(":visible"),q=a||1===l.target.length,r=!c||l.target.length<2||e.target[0]===c.target;return(typeof a).search("boolean|number")&&(a=!p),h=!g.is(":animated")&&p===a&&r,i=h?F:!!this._trigger(k,[90]),this.destroyed?this:(i!==E&&a&&this.focus(c),!i||h?this:(d.attr(g[0],"aria-hidden",!a),a?(this.mouse&&(e.origin=d.event.fix(this.mouse)),d.isFunction(n.text)&&this._updateContent(n.text,E),d.isFunction(n.title)&&this._updateTitle(n.title,E),!C&&"mouse"===m.target&&m.adjust.mouse&&(d(b).bind("mousemove."+S,this._storeMouse),C=D),o||g.css("width",g.outerWidth(E)),this.reposition(c,arguments[2]),o||g.css("width",""),l.solo&&("string"==typeof l.solo?d(l.solo):d(W,l.solo)).not(g).not(l.target).qtip("hide",d.Event("tooltipsolo"))):(clearTimeout(this.timers.show),delete e.origin,C&&!d(W+'[tracking="true"]:visible',l.solo).not(g).length&&(d(b).unbind("mousemove."+S),C=E),this.blur(c)),j=d.proxy(function(){a?(db.ie&&g[0].style.removeAttribute("filter"),g.css("overflow",""),"string"==typeof l.autofocus&&d(this.options.show.autofocus,g).focus(),this.options.show.target.trigger("qtip-"+this.id+"-inactive")):g.css({display:"",visibility:"",opacity:"",left:"",top:""}),this._trigger(a?"visible":"hidden")},this),l.effect===E||q===E?(g[k](),j()):d.isFunction(l.effect)?(g.stop(1,1),l.effect.call(g,this),g.queue("fx",function(a){j(),a()})):g.fadeTo(90,a?1:0,j),a&&l.target.trigger("qtip-"+this.id+"-inactive"),this))},z.show=function(a){return this.toggle(D,a)},z.hide=function(a){return this.toggle(E,a)},z.focus=function(a){if(!this.rendered||this.destroyed)return this;var b=d(W),c=this.tooltip,e=parseInt(c[0].style.zIndex,10),f=y.zindex+b.length;return c.hasClass($)||this._trigger("focus",[f],a)&&(e!==f&&(b.each(function(){this.style.zIndex>e&&(this.style.zIndex=this.style.zIndex-1)}),b.filter("."+$).qtip("blur",a)),c.addClass($)[0].style.zIndex=f),this},z.blur=function(a){return!this.rendered||this.destroyed?this:(this.tooltip.removeClass($),this._trigger("blur",[this.tooltip.css("zIndex")],a),this)},z.disable=function(a){return this.destroyed?this:("toggle"===a?a=!(this.rendered?this.tooltip.hasClass(ab):this.disabled):"boolean"!=typeof a&&(a=D),this.rendered&&this.tooltip.toggleClass(ab,a).attr("aria-disabled",a),this.disabled=!!a,this)},z.enable=function(){return this.disable(E)},z._createButton=function(){var a=this,b=this.elements,c=b.tooltip,e=this.options.content.button,f="string"==typeof e,g=f?e:"Close tooltip";b.button&&b.button.remove(),b.button=e.jquery?e:d("<a />",{"class":"qtip-close "+(this.options.style.widget?"":S+"-icon"),title:g,"aria-label":g}).prepend(d("<span />",{"class":"ui-icon ui-icon-close",html:"&times;"})),b.button.appendTo(b.titlebar||c).attr("role","button").click(function(b){return c.hasClass(ab)||a.hide(b),E})},z._updateButton=function(a){if(!this.rendered)return E;var b=this.elements.button;a?this._createButton():b.remove()},z._setWidget=function(){var a=this.options.style.widget,b=this.elements,c=b.tooltip,d=c.hasClass(ab);c.removeClass(ab),ab=a?"ui-state-disabled":"qtip-disabled",c.toggleClass(ab,d),c.toggleClass("ui-helper-reset "+k(),a).toggleClass(Z,this.options.style.def&&!a),b.content&&b.content.toggleClass(k("content"),a),b.titlebar&&b.titlebar.toggleClass(k("header"),a),b.button&&b.button.toggleClass(S+"-icon",!a)},z._storeMouse=function(a){return(this.mouse=d.event.fix(a)).type="mousemove",this},z._bind=function(a,b,c,e,f){if(a&&c&&b.length){var g="."+this._id+(e?"-"+e:"");return d(a).bind((b.split?b:b.join(g+" "))+g,d.proxy(c,f||this)),this}},z._unbind=function(a,b){return a&&d(a).unbind("."+this._id+(b?"-"+b:"")),this},z._trigger=function(a,b,c){var e=d.Event("tooltip"+a);return e.originalEvent=c&&d.extend({},c)||this.cache.event||F,this.triggering=a,this.tooltip.trigger(e,[this].concat(b||[])),this.triggering=E,!e.isDefaultPrevented()},z._bindEvents=function(a,b,c,e,f,g){var h=c.filter(e).add(e.filter(c)),i=[];h.length&&(d.each(b,function(b,c){var e=d.inArray(c,a);e>-1&&i.push(a.splice(e,1)[0])}),i.length&&(this._bind(h,i,function(a){var b=this.rendered?this.tooltip[0].offsetWidth>0:!1;(b?g:f).call(this,a)}),c=c.not(h),e=e.not(h))),this._bind(c,a,f),this._bind(e,b,g)},z._assignInitialEvents=function(a){function b(a){return this.disabled||this.destroyed?E:(this.cache.event=a&&d.event.fix(a),this.cache.target=a&&d(a.target),clearTimeout(this.timers.show),void(this.timers.show=l.call(this,function(){this.render("object"==typeof a||c.show.ready)},c.prerender?0:c.show.delay)))}var c=this.options,e=c.show.target,f=c.hide.target,g=c.show.event?d.trim(""+c.show.event).split(" "):[],h=c.hide.event?d.trim(""+c.hide.event).split(" "):[];this._bind(this.elements.target,["remove","removeqtip"],function(){this.destroy(!0)},"destroy"),/mouse(over|enter)/i.test(c.show.event)&&!/mouse(out|leave)/i.test(c.hide.event)&&h.push("mouseleave"),this._bind(e,"mousemove",function(a){this._storeMouse(a),this.cache.onTarget=D}),this._bindEvents(g,h,e,f,b,function(){return this.timers?void clearTimeout(this.timers.show):E}),(c.show.ready||c.prerender)&&b.call(this,a)},z._assignEvents=function(){var c=this,e=this.options,f=e.position,g=this.tooltip,h=e.show.target,i=e.hide.target,j=f.container,k=f.viewport,l=d(b),q=(d(b.body),d(a)),r=e.show.event?d.trim(""+e.show.event).split(" "):[],s=e.hide.event?d.trim(""+e.hide.event).split(" "):[];d.each(e.events,function(a,b){c._bind(g,"toggle"===a?["tooltipshow","tooltiphide"]:["tooltip"+a],b,null,g)}),/mouse(out|leave)/i.test(e.hide.event)&&"window"===e.hide.leave&&this._bind(l,["mouseout","blur"],function(a){/select|option/.test(a.target.nodeName)||a.relatedTarget||this.hide(a)}),e.hide.fixed?i=i.add(g.addClass(Y)):/mouse(over|enter)/i.test(e.show.event)&&this._bind(i,"mouseleave",function(){clearTimeout(this.timers.show)}),(""+e.hide.event).indexOf("unfocus")>-1&&this._bind(j.closest("html"),["mousedown","touchstart"],function(a){var b=d(a.target),c=this.rendered&&!this.tooltip.hasClass(ab)&&this.tooltip[0].offsetWidth>0,e=b.parents(W).filter(this.tooltip[0]).length>0;b[0]===this.target[0]||b[0]===this.tooltip[0]||e||this.target.has(b[0]).length||!c||this.hide(a)}),"number"==typeof e.hide.inactive&&(this._bind(h,"qtip-"+this.id+"-inactive",o,"inactive"),this._bind(i.add(g),y.inactiveEvents,o)),this._bindEvents(r,s,h,i,m,n),this._bind(h.add(g),"mousemove",function(a){if("number"==typeof e.hide.distance){var b=this.cache.origin||{},c=this.options.hide.distance,d=Math.abs;(d(a.pageX-b.pageX)>=c||d(a.pageY-b.pageY)>=c)&&this.hide(a)}this._storeMouse(a)}),"mouse"===f.target&&f.adjust.mouse&&(e.hide.event&&this._bind(h,["mouseenter","mouseleave"],function(a){return this.cache?void(this.cache.onTarget="mouseenter"===a.type):E}),this._bind(l,"mousemove",function(a){this.rendered&&this.cache.onTarget&&!this.tooltip.hasClass(ab)&&this.tooltip[0].offsetWidth>0&&this.reposition(a)})),(f.adjust.resize||k.length)&&this._bind(d.event.special.resize?k:q,"resize",p),f.adjust.scroll&&this._bind(q.add(f.container),"scroll",p)},z._unassignEvents=function(){var c=this.options,e=c.show.target,f=c.hide.target,g=d.grep([this.elements.target[0],this.rendered&&this.tooltip[0],c.position.container[0],c.position.viewport[0],c.position.container.closest("html")[0],a,b],function(a){return"object"==typeof a});e&&e.toArray&&(g=g.concat(e.toArray())),f&&f.toArray&&(g=g.concat(f.toArray())),this._unbind(g)._unbind(g,"destroy")._unbind(g,"inactive")},d(function(){q(W,["mouseenter","mouseleave"],function(a){var b="mouseenter"===a.type,c=d(a.currentTarget),e=d(a.relatedTarget||a.target),f=this.options;b?(this.focus(a),c.hasClass(Y)&&!c.hasClass(ab)&&clearTimeout(this.timers.hide)):"mouse"===f.position.target&&f.position.adjust.mouse&&f.hide.event&&f.show.target&&!e.closest(f.show.target[0]).length&&this.hide(a),c.toggleClass(_,b)}),q("["+U+"]",X,o)}),y=d.fn.qtip=function(a,b,e){var f=(""+a).toLowerCase(),g=F,i=d.makeArray(arguments).slice(1),j=i[i.length-1],k=this[0]?d.data(this[0],S):F;return!arguments.length&&k||"api"===f?k:"string"==typeof a?(this.each(function(){var a=d.data(this,S);if(!a)return D;if(j&&j.timeStamp&&(a.cache.event=j),!b||"option"!==f&&"options"!==f)a[f]&&a[f].apply(a,i);else{if(e===c&&!d.isPlainObject(b))return g=a.get(b),E;a.set(b,e)}}),g!==F?g:this):"object"!=typeof a&&arguments.length?void 0:(k=h(d.extend(D,{},a)),this.each(function(a){var b,c;return c=d.isArray(k.id)?k.id[a]:k.id,c=!c||c===E||c.length<1||y.api[c]?y.nextid++:c,b=r(d(this),c,k),b===E?D:(y.api[c]=b,d.each(R,function(){"initialize"===this.initialize&&this(b)}),void b._assignInitialEvents(j))}))},d.qtip=e,y.api={},d.each({attr:function(a,b){if(this.length){var c=this[0],e="title",f=d.data(c,"qtip");if(a===e&&f&&"object"==typeof f&&f.options.suppress)return arguments.length<2?d.attr(c,cb):(f&&f.options.content.attr===e&&f.cache.attr&&f.set("content.text",b),this.attr(cb,b))}return d.fn["attr"+bb].apply(this,arguments)},clone:function(a){var b=(d([]),d.fn["clone"+bb].apply(this,arguments));return a||b.filter("["+cb+"]").attr("title",function(){return d.attr(this,cb)}).removeAttr(cb),b}},function(a,b){if(!b||d.fn[a+bb])return D;var c=d.fn[a+bb]=d.fn[a];d.fn[a]=function(){return b.apply(this,arguments)||c.apply(this,arguments)}}),d.ui||(d["cleanData"+bb]=d.cleanData,d.cleanData=function(a){for(var b,c=0;(b=d(a[c])).length;c++)if(b.attr(T))try{b.triggerHandler("removeqtip")}catch(e){}d["cleanData"+bb].apply(this,arguments)}),y.version="2.2.1",y.nextid=0,y.inactiveEvents=X,y.zindex=15e3,y.defaults={prerender:E,id:E,overwrite:D,suppress:D,content:{text:D,attr:"title",title:E,button:E},position:{my:"top left",at:"bottom right",target:E,container:E,viewport:E,adjust:{x:0,y:0,mouse:D,scroll:D,resize:D,method:"flipinvert flipinvert"},effect:function(a,b){d(this).animate(b,{duration:200,queue:E})}},show:{target:E,event:"mouseenter",effect:D,delay:90,solo:E,ready:E,autofocus:E},hide:{target:E,event:"mouseleave",effect:D,delay:0,fixed:E,inactive:E,leave:"window",distance:E},style:{classes:"",widget:E,width:E,height:E,def:D},events:{render:F,move:F,show:F,hide:F,toggle:F,visible:F,hidden:F,focus:F,blur:F}};var hb,ib="margin",jb="border",kb="color",lb="background-color",mb="transparent",nb=" !important",ob=!!b.createElement("canvas").getContext,pb=/rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,qb={},rb=["Webkit","O","Moz","ms"];if(ob)var sb=a.devicePixelRatio||1,tb=function(){var a=b.createElement("canvas").getContext("2d");return a.backingStorePixelRatio||a.webkitBackingStorePixelRatio||a.mozBackingStorePixelRatio||a.msBackingStorePixelRatio||a.oBackingStorePixelRatio||1}(),ub=sb/tb;else var vb=function(a,b,c){return"<qtipvml:"+a+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(b||"")+' style="behavior: url(#default#VML); '+(c||"")+'" />'};d.extend(v.prototype,{init:function(a){var b,c;c=this.element=a.elements.tip=d("<div />",{"class":S+"-tip"}).prependTo(a.tooltip),ob?(b=d("<canvas />").appendTo(this.element)[0].getContext("2d"),b.lineJoin="miter",b.miterLimit=1e5,b.save()):(b=vb("shape",'coordorigin="0,0"',"position:absolute;"),this.element.html(b+b),a._bind(d("*",c).add(c),["click","mousedown"],function(a){a.stopPropagation()},this._ns)),a._bind(a.tooltip,"tooltipmove",this.reposition,this._ns,this),this.create()},_swapDimensions:function(){this.size[0]=this.options.height,this.size[1]=this.options.width},_resetDimensions:function(){this.size[0]=this.options.width,this.size[1]=this.options.height},_useTitle:function(a){var b=this.qtip.elements.titlebar;return b&&(a.y===K||a.y===O&&this.element.position().top+this.size[1]/2+this.options.offset<b.outerHeight(D))},_parseCorner:function(a){var b=this.qtip.options.position.my;return a===E||b===E?a=E:a===D?a=new A(b.string()):a.string||(a=new A(a),a.fixed=D),a},_parseWidth:function(a,b,c){var d=this.qtip.elements,e=jb+s(b)+"Width";return(c?u(c,e):u(d.content,e)||u(this._useTitle(a)&&d.titlebar||d.content,e)||u(d.tooltip,e))||0},_parseRadius:function(a){var b=this.qtip.elements,c=jb+s(a.y)+s(a.x)+"Radius";return db.ie<9?0:u(this._useTitle(a)&&b.titlebar||b.content,c)||u(b.tooltip,c)||0},_invalidColour:function(a,b,c){var d=a.css(b);return!d||c&&d===a.css(c)||pb.test(d)?E:d},_parseColours:function(a){var b=this.qtip.elements,c=this.element.css("cssText",""),e=jb+s(a[a.precedance])+s(kb),f=this._useTitle(a)&&b.titlebar||b.content,g=this._invalidColour,h=[];return h[0]=g(c,lb)||g(f,lb)||g(b.content,lb)||g(b.tooltip,lb)||c.css(lb),h[1]=g(c,e,kb)||g(f,e,kb)||g(b.content,e,kb)||g(b.tooltip,e,kb)||b.tooltip.css(e),d("*",c).add(c).css("cssText",lb+":"+mb+nb+";"+jb+":0"+nb+";"),h},_calculateSize:function(a){var b,c,d,e=a.precedance===H,f=this.options.width,g=this.options.height,h="c"===a.abbrev(),i=(e?f:g)*(h?.5:1),j=Math.pow,k=Math.round,l=Math.sqrt(j(i,2)+j(g,2)),m=[this.border/i*l,this.border/g*l];return m[2]=Math.sqrt(j(m[0],2)-j(this.border,2)),m[3]=Math.sqrt(j(m[1],2)-j(this.border,2)),b=l+m[2]+m[3]+(h?0:m[0]),c=b/l,d=[k(c*f),k(c*g)],e?d:d.reverse()},_calculateTip:function(a,b,c){c=c||1,b=b||this.size;var d=b[0]*c,e=b[1]*c,f=Math.ceil(d/2),g=Math.ceil(e/2),h={br:[0,0,d,e,d,0],bl:[0,0,d,0,0,e],tr:[0,e,d,0,d,e],tl:[0,0,0,e,d,e],tc:[0,e,f,0,d,e],bc:[0,0,d,0,f,e],rc:[0,0,d,g,0,e],lc:[d,0,d,e,0,g]};return h.lt=h.br,h.rt=h.bl,h.lb=h.tr,h.rb=h.tl,h[a.abbrev()]},_drawCoords:function(a,b){a.beginPath(),a.moveTo(b[0],b[1]),a.lineTo(b[2],b[3]),a.lineTo(b[4],b[5]),a.closePath()},create:function(){var a=this.corner=(ob||db.ie)&&this._parseCorner(this.options.corner);return(this.enabled=!!this.corner&&"c"!==this.corner.abbrev())&&(this.qtip.cache.corner=a.clone(),this.update()),this.element.toggle(this.enabled),this.corner},update:function(b,c){if(!this.enabled)return this;var e,f,g,h,i,j,k,l,m=this.qtip.elements,n=this.element,o=n.children(),p=this.options,q=this.size,r=p.mimic,s=Math.round;b||(b=this.qtip.cache.corner||this.corner),r===E?r=b:(r=new A(r),r.precedance=b.precedance,"inherit"===r.x?r.x=b.x:"inherit"===r.y?r.y=b.y:r.x===r.y&&(r[b.precedance]=b[b.precedance])),f=r.precedance,b.precedance===G?this._swapDimensions():this._resetDimensions(),e=this.color=this._parseColours(b),e[1]!==mb?(l=this.border=this._parseWidth(b,b[b.precedance]),p.border&&1>l&&!pb.test(e[1])&&(e[0]=e[1]),this.border=l=p.border!==D?p.border:l):this.border=l=0,k=this.size=this._calculateSize(b),n.css({width:k[0],height:k[1],lineHeight:k[1]+"px"}),j=b.precedance===H?[s(r.x===L?l:r.x===N?k[0]-q[0]-l:(k[0]-q[0])/2),s(r.y===K?k[1]-q[1]:0)]:[s(r.x===L?k[0]-q[0]:0),s(r.y===K?l:r.y===M?k[1]-q[1]-l:(k[1]-q[1])/2)],ob?(g=o[0].getContext("2d"),g.restore(),g.save(),g.clearRect(0,0,6e3,6e3),h=this._calculateTip(r,q,ub),i=this._calculateTip(r,this.size,ub),o.attr(I,k[0]*ub).attr(J,k[1]*ub),o.css(I,k[0]).css(J,k[1]),this._drawCoords(g,i),g.fillStyle=e[1],g.fill(),g.translate(j[0]*ub,j[1]*ub),this._drawCoords(g,h),g.fillStyle=e[0],g.fill()):(h=this._calculateTip(r),h="m"+h[0]+","+h[1]+" l"+h[2]+","+h[3]+" "+h[4]+","+h[5]+" xe",j[2]=l&&/^(r|b)/i.test(b.string())?8===db.ie?2:1:0,o.css({coordsize:k[0]+l+" "+(k[1]+l),antialias:""+(r.string().indexOf(O)>-1),left:j[0]-j[2]*Number(f===G),top:j[1]-j[2]*Number(f===H),width:k[0]+l,height:k[1]+l}).each(function(a){var b=d(this);b[b.prop?"prop":"attr"]({coordsize:k[0]+l+" "+(k[1]+l),path:h,fillcolor:e[0],filled:!!a,stroked:!a}).toggle(!(!l&&!a)),!a&&b.html(vb("stroke",'weight="'+2*l+'px" color="'+e[1]+'" miterlimit="1000" joinstyle="miter"'))})),a.opera&&setTimeout(function(){m.tip.css({display:"inline-block",visibility:"visible"})},1),c!==E&&this.calculate(b,k)},calculate:function(a,b){if(!this.enabled)return E;var c,e,f=this,g=this.qtip.elements,h=this.element,i=this.options.offset,j=(g.tooltip.hasClass("ui-widget"),{});return a=a||this.corner,c=a.precedance,b=b||this._calculateSize(a),e=[a.x,a.y],c===G&&e.reverse(),d.each(e,function(d,e){var h,k,l;
4
+ e===O?(h=c===H?L:K,j[h]="50%",j[ib+"-"+h]=-Math.round(b[c===H?0:1]/2)+i):(h=f._parseWidth(a,e,g.tooltip),k=f._parseWidth(a,e,g.content),l=f._parseRadius(a),j[e]=Math.max(-f.border,d?k:i+(l>h?l:-h)))}),j[a[c]]-=b[c===G?0:1],h.css({margin:"",top:"",bottom:"",left:"",right:""}).css(j),j},reposition:function(a,b,d){function e(a,b,c,d,e){a===Q&&j.precedance===b&&k[d]&&j[c]!==O?j.precedance=j.precedance===G?H:G:a!==Q&&k[d]&&(j[b]=j[b]===O?k[d]>0?d:e:j[b]===d?e:d)}function f(a,b,e){j[a]===O?p[ib+"-"+b]=o[a]=g[ib+"-"+b]-k[b]:(h=g[e]!==c?[k[b],-g[b]]:[-k[b],g[b]],(o[a]=Math.max(h[0],h[1]))>h[0]&&(d[b]-=k[b],o[b]=E),p[g[e]!==c?e:b]=o[a])}if(this.enabled){var g,h,i=b.cache,j=this.corner.clone(),k=d.adjusted,l=b.options.position.adjust.method.split(" "),m=l[0],n=l[1]||l[0],o={left:E,top:E,x:0,y:0},p={};this.corner.fixed!==D&&(e(m,G,H,L,N),e(n,H,G,K,M),(j.string()!==i.corner.string()||i.cornerTop!==k.top||i.cornerLeft!==k.left)&&this.update(j,E)),g=this.calculate(j),g.right!==c&&(g.left=-g.right),g.bottom!==c&&(g.top=-g.bottom),g.user=this.offset,(o.left=m===Q&&!!k.left)&&f(G,L,N),(o.top=n===Q&&!!k.top)&&f(H,K,M),this.element.css(p).toggle(!(o.x&&o.y||j.x===O&&o.y||j.y===O&&o.x)),d.left-=g.left.charAt?g.user:m!==Q||o.top||!o.left&&!o.top?g.left+this.border:0,d.top-=g.top.charAt?g.user:n!==Q||o.left||!o.left&&!o.top?g.top+this.border:0,i.cornerLeft=k.left,i.cornerTop=k.top,i.corner=j.clone()}},destroy:function(){this.qtip._unbind(this.qtip.tooltip,this._ns),this.qtip.elements.tip&&this.qtip.elements.tip.find("*").remove().end().remove()}}),hb=R.tip=function(a){return new v(a,a.options.style.tip)},hb.initialize="render",hb.sanitize=function(a){if(a.style&&"tip"in a.style){var b=a.style.tip;"object"!=typeof b&&(b=a.style.tip={corner:b}),/string|boolean/i.test(typeof b.corner)||(b.corner=D)}},B.tip={"^position.my|style.tip.(corner|mimic|border)$":function(){this.create(),this.qtip.reposition()},"^style.tip.(height|width)$":function(a){this.size=[a.width,a.height],this.update(),this.qtip.reposition()},"^content.title|style.(classes|widget)$":function(){this.update()}},d.extend(D,y.defaults,{style:{tip:{corner:D,mimic:E,width:6,height:6,border:D,offset:0}}});var wb,xb,yb="qtip-modal",zb="."+yb;xb=function(){function a(a){if(d.expr[":"].focusable)return d.expr[":"].focusable;var b,c,e,f=!isNaN(d.attr(a,"tabindex")),g=a.nodeName&&a.nodeName.toLowerCase();return"area"===g?(b=a.parentNode,c=b.name,a.href&&c&&"map"===b.nodeName.toLowerCase()?(e=d("img[usemap=#"+c+"]")[0],!!e&&e.is(":visible")):!1):/input|select|textarea|button|object/.test(g)?!a.disabled:"a"===g?a.href||f:f}function c(a){k.length<1&&a.length?a.not("body").blur():k.first().focus()}function e(a){if(i.is(":visible")){var b,e=d(a.target),h=f.tooltip,j=e.closest(W);b=j.length<1?E:parseInt(j[0].style.zIndex,10)>parseInt(h[0].style.zIndex,10),b||e.closest(W)[0]===h[0]||c(e),g=a.target===k[k.length-1]}}var f,g,h,i,j=this,k={};d.extend(j,{init:function(){return i=j.elem=d("<div />",{id:"qtip-overlay",html:"<div></div>",mousedown:function(){return E}}).hide(),d(b.body).bind("focusin"+zb,e),d(b).bind("keydown"+zb,function(a){f&&f.options.show.modal.escape&&27===a.keyCode&&f.hide(a)}),i.bind("click"+zb,function(a){f&&f.options.show.modal.blur&&f.hide(a)}),j},update:function(b){f=b,k=b.options.show.modal.stealfocus!==E?b.tooltip.find("*").filter(function(){return a(this)}):[]},toggle:function(a,e,g){var k=(d(b.body),a.tooltip),l=a.options.show.modal,m=l.effect,n=e?"show":"hide",o=i.is(":visible"),p=d(zb).filter(":visible:not(:animated)").not(k);return j.update(a),e&&l.stealfocus!==E&&c(d(":focus")),i.toggleClass("blurs",l.blur),e&&i.appendTo(b.body),i.is(":animated")&&o===e&&h!==E||!e&&p.length?j:(i.stop(D,E),d.isFunction(m)?m.call(i,e):m===E?i[n]():i.fadeTo(parseInt(g,10)||90,e?1:0,function(){e||i.hide()}),e||i.queue(function(a){i.css({left:"",top:""}),d(zb).length||i.detach(),a()}),h=e,f.destroyed&&(f=F),j)}}),j.init()},xb=new xb,d.extend(w.prototype,{init:function(a){var b=a.tooltip;return this.options.on?(a.elements.overlay=xb.elem,b.addClass(yb).css("z-index",y.modal_zindex+d(zb).length),a._bind(b,["tooltipshow","tooltiphide"],function(a,c,e){var f=a.originalEvent;if(a.target===b[0])if(f&&"tooltiphide"===a.type&&/mouse(leave|enter)/.test(f.type)&&d(f.relatedTarget).closest(xb.elem[0]).length)try{a.preventDefault()}catch(g){}else(!f||f&&"tooltipsolo"!==f.type)&&this.toggle(a,"tooltipshow"===a.type,e)},this._ns,this),a._bind(b,"tooltipfocus",function(a,c){if(!a.isDefaultPrevented()&&a.target===b[0]){var e=d(zb),f=y.modal_zindex+e.length,g=parseInt(b[0].style.zIndex,10);xb.elem[0].style.zIndex=f-1,e.each(function(){this.style.zIndex>g&&(this.style.zIndex-=1)}),e.filter("."+$).qtip("blur",a.originalEvent),b.addClass($)[0].style.zIndex=f,xb.update(c);try{a.preventDefault()}catch(h){}}},this._ns,this),void a._bind(b,"tooltiphide",function(a){a.target===b[0]&&d(zb).filter(":visible").not(b).last().qtip("focus",a)},this._ns,this)):this},toggle:function(a,b,c){return a&&a.isDefaultPrevented()?this:void xb.toggle(this.qtip,!!b,c)},destroy:function(){this.qtip.tooltip.removeClass(yb),this.qtip._unbind(this.qtip.tooltip,this._ns),xb.toggle(this.qtip,E),delete this.qtip.elements.overlay}}),wb=R.modal=function(a){return new w(a,a.options.show.modal)},wb.sanitize=function(a){a.show&&("object"!=typeof a.show.modal?a.show.modal={on:!!a.show.modal}:"undefined"==typeof a.show.modal.on&&(a.show.modal.on=D))},y.modal_zindex=y.zindex-200,wb.initialize="render",B.modal={"^show.modal.(on|blur)$":function(){this.destroy(),this.init(),this.qtip.elems.overlay.toggle(this.qtip.tooltip[0].offsetWidth>0)}},d.extend(D,y.defaults,{show:{modal:{on:E,effect:D,blur:D,stealfocus:D,escape:D}}}),R.viewport=function(c,d,e,f,g,h,i){function j(a,b,c,e,f,g,h,i,j){var k=d[f],s=u[a],t=v[a],w=c===Q,x=s===f?j:s===g?-j:-j/2,y=t===f?i:t===g?-i:-i/2,z=q[f]+r[f]-(n?0:m[f]),A=z-k,B=k+j-(h===I?o:p)-z,C=x-(u.precedance===a||s===u[b]?y:0)-(t===O?i/2:0);return w?(C=(s===f?1:-1)*x,d[f]+=A>0?A:B>0?-B:0,d[f]=Math.max(-m[f]+r[f],k-C,Math.min(Math.max(-m[f]+r[f]+(h===I?o:p),k+C),d[f],"center"===s?k-x:1e9))):(e*=c===P?2:0,A>0&&(s!==f||B>0)?(d[f]-=C+e,l.invert(a,f)):B>0&&(s!==g||A>0)&&(d[f]-=(s===O?-C:C)+e,l.invert(a,g)),d[f]<q&&-d[f]>B&&(d[f]=k,l=u.clone())),d[f]-k}var k,l,m,n,o,p,q,r,s=e.target,t=c.elements.tooltip,u=e.my,v=e.at,w=e.adjust,x=w.method.split(" "),y=x[0],z=x[1]||x[0],A=e.viewport,B=e.container,C=(c.cache,{left:0,top:0});return A.jquery&&s[0]!==a&&s[0]!==b.body&&"none"!==w.method?(m=B.offset()||C,n="static"===B.css("position"),k="fixed"===t.css("position"),o=A[0]===a?A.width():A.outerWidth(E),p=A[0]===a?A.height():A.outerHeight(E),q={left:k?0:A.scrollLeft(),top:k?0:A.scrollTop()},r=A.offset()||C,("shift"!==y||"shift"!==z)&&(l=u.clone()),C={left:"none"!==y?j(G,H,y,w.x,L,N,I,f,h):0,top:"none"!==z?j(H,G,z,w.y,K,M,J,g,i):0,my:l}):C},R.polys={polygon:function(a,b){var c,d,e,f={width:0,height:0,position:{top:1e10,right:0,bottom:0,left:1e10},adjustable:E},g=0,h=[],i=1,j=1,k=0,l=0;for(g=a.length;g--;)c=[parseInt(a[--g],10),parseInt(a[g+1],10)],c[0]>f.position.right&&(f.position.right=c[0]),c[0]<f.position.left&&(f.position.left=c[0]),c[1]>f.position.bottom&&(f.position.bottom=c[1]),c[1]<f.position.top&&(f.position.top=c[1]),h.push(c);if(d=f.width=Math.abs(f.position.right-f.position.left),e=f.height=Math.abs(f.position.bottom-f.position.top),"c"===b.abbrev())f.position={left:f.position.left+f.width/2,top:f.position.top+f.height/2};else{for(;d>0&&e>0&&i>0&&j>0;)for(d=Math.floor(d/2),e=Math.floor(e/2),b.x===L?i=d:b.x===N?i=f.width-d:i+=Math.floor(d/2),b.y===K?j=e:b.y===M?j=f.height-e:j+=Math.floor(e/2),g=h.length;g--&&!(h.length<2);)k=h[g][0]-f.position.left,l=h[g][1]-f.position.top,(b.x===L&&k>=i||b.x===N&&i>=k||b.x===O&&(i>k||k>f.width-i)||b.y===K&&l>=j||b.y===M&&j>=l||b.y===O&&(j>l||l>f.height-j))&&h.splice(g,1);f.position={left:h[0][0],top:h[0][1]}}return f},rect:function(a,b,c,d){return{width:Math.abs(c-a),height:Math.abs(d-b),position:{left:Math.min(a,c),top:Math.min(b,d)}}},_angles:{tc:1.5,tr:7/4,tl:5/4,bc:.5,br:.25,bl:.75,rc:2,lc:1,c:0},ellipse:function(a,b,c,d,e){var f=R.polys._angles[e.abbrev()],g=0===f?0:c*Math.cos(f*Math.PI),h=d*Math.sin(f*Math.PI);return{width:2*c-Math.abs(g),height:2*d-Math.abs(h),position:{left:a+g,top:b+h},adjustable:E}},circle:function(a,b,c,d){return R.polys.ellipse(a,b,c,c,d)}},R.svg=function(a,c,e){for(var f,g,h,i,j,k,l,m,n,o=(d(b),c[0]),p=d(o.ownerSVGElement),q=o.ownerDocument,r=(parseInt(c.css("stroke-width"),10)||0)/2;!o.getBBox;)o=o.parentNode;if(!o.getBBox||!o.parentNode)return E;switch(o.nodeName){case"ellipse":case"circle":m=R.polys.ellipse(o.cx.baseVal.value,o.cy.baseVal.value,(o.rx||o.r).baseVal.value+r,(o.ry||o.r).baseVal.value+r,e);break;case"line":case"polygon":case"polyline":for(l=o.points||[{x:o.x1.baseVal.value,y:o.y1.baseVal.value},{x:o.x2.baseVal.value,y:o.y2.baseVal.value}],m=[],k=-1,i=l.numberOfItems||l.length;++k<i;)j=l.getItem?l.getItem(k):l[k],m.push.apply(m,[j.x,j.y]);m=R.polys.polygon(m,e);break;default:m=o.getBBox(),m={width:m.width,height:m.height,position:{left:m.x,top:m.y}}}return n=m.position,p=p[0],p.createSVGPoint&&(g=o.getScreenCTM(),l=p.createSVGPoint(),l.x=n.left,l.y=n.top,h=l.matrixTransform(g),n.left=h.x,n.top=h.y),q!==b&&"mouse"!==a.position.target&&(f=d((q.defaultView||q.parentWindow).frameElement).offset(),f&&(n.left+=f.left,n.top+=f.top)),q=d(q),n.left+=q.scrollLeft(),n.top+=q.scrollTop(),m},R.imagemap=function(a,b,c){b.jquery||(b=d(b));var e,f,g,h,i,j=(b.attr("shape")||"rect").toLowerCase().replace("poly","polygon"),k=d('img[usemap="#'+b.parent("map").attr("name")+'"]'),l=d.trim(b.attr("coords")),m=l.replace(/,$/,"").split(",");if(!k.length)return E;if("polygon"===j)h=R.polys.polygon(m,c);else{if(!R.polys[j])return E;for(g=-1,i=m.length,f=[];++g<i;)f.push(parseInt(m[g],10));h=R.polys[j].apply(this,f.concat(c))}return e=k.offset(),e.left+=Math.ceil((k.outerWidth(E)-k.width())/2),e.top+=Math.ceil((k.outerHeight(E)-k.height())/2),h.position.left+=e.left,h.position.top+=e.top,h};var Ab,Bb='<iframe class="qtip-bgiframe" frameborder="0" tabindex="-1" src="javascript:\'\';" style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=0); -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";"></iframe>';d.extend(x.prototype,{_scroll:function(){var b=this.qtip.elements.overlay;b&&(b[0].style.top=d(a).scrollTop()+"px")},init:function(c){var e=c.tooltip;d("select, object").length<1&&(this.bgiframe=c.elements.bgiframe=d(Bb).appendTo(e),c._bind(e,"tooltipmove",this.adjustBGIFrame,this._ns,this)),this.redrawContainer=d("<div/>",{id:S+"-rcontainer"}).appendTo(b.body),c.elements.overlay&&c.elements.overlay.addClass("qtipmodal-ie6fix")&&(c._bind(a,["scroll","resize"],this._scroll,this._ns,this),c._bind(e,["tooltipshow"],this._scroll,this._ns,this)),this.redraw()},adjustBGIFrame:function(){var a,b,c=this.qtip.tooltip,d={height:c.outerHeight(E),width:c.outerWidth(E)},e=this.qtip.plugins.tip,f=this.qtip.elements.tip;b=parseInt(c.css("borderLeftWidth"),10)||0,b={left:-b,top:-b},e&&f&&(a="x"===e.corner.precedance?[I,L]:[J,K],b[a[1]]-=f[a[0]]()),this.bgiframe.css(b).css(d)},redraw:function(){if(this.qtip.rendered<1||this.drawing)return this;var a,b,c,d,e=this.qtip.tooltip,f=this.qtip.options.style,g=this.qtip.options.position.container;return this.qtip.drawing=1,f.height&&e.css(J,f.height),f.width?e.css(I,f.width):(e.css(I,"").appendTo(this.redrawContainer),b=e.width(),1>b%2&&(b+=1),c=e.css("maxWidth")||"",d=e.css("minWidth")||"",a=(c+d).indexOf("%")>-1?g.width()/100:0,c=(c.indexOf("%")>-1?a:1)*parseInt(c,10)||b,d=(d.indexOf("%")>-1?a:1)*parseInt(d,10)||0,b=c+d?Math.min(Math.max(b,d),c):b,e.css(I,Math.round(b)).appendTo(g)),this.drawing=0,this},destroy:function(){this.bgiframe&&this.bgiframe.remove(),this.qtip._unbind([a,this.qtip.tooltip],this._ns)}}),Ab=R.ie6=function(a){return 6===db.ie?new x(a):E},Ab.initialize="render",B.ie6={"^content|style$":function(){this.redraw()}}})}(window,document);
5
+ //# sourceMappingURL=jquery.qtip.min.map
assets/js/vendor/select2.js ADDED
@@ -0,0 +1,5403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * Select2 4.0.0
3
+ * https://select2.github.io
4
+ *
5
+ * Released under the MIT license
6
+ * https://github.com/select2/select2/blob/master/LICENSE.md
7
+ */
8
+ (function (factory) {
9
+ if (typeof define === 'function' && define.amd) {
10
+ // AMD. Register as an anonymous module.
11
+ define(['jquery'], factory);
12
+ } else if (typeof exports === 'object') {
13
+ // Node/CommonJS
14
+ factory(require('jquery'));
15
+ } else {
16
+ // Browser globals
17
+ factory(jQuery);
18
+ }
19
+ }(function (jQuery) {
20
+ // This is needed so we can catch the AMD loader configuration and use it
21
+ // The inner file should be wrapped (by `banner.start.js`) in a function that
22
+ // returns the AMD loader references.
23
+ var S2 =
24
+ (function () {
25
+ // Restore the Select2 AMD loader so it can be used
26
+ // Needed mostly in the language files, where the loader is not inserted
27
+ if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
28
+ var S2 = jQuery.fn.select2.amd;
29
+ }
30
+ var S2;(function () { if (!S2 || !S2.requirejs) {
31
+ if (!S2) { S2 = {}; } else { require = S2; }
32
+ /**
33
+ * @license almond 0.2.9 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
34
+ * Available via the MIT or new BSD license.
35
+ * see: http://github.com/jrburke/almond for details
36
+ */
37
+ //Going sloppy to avoid 'use strict' string cost, but strict practices should
38
+ //be followed.
39
+ /*jslint sloppy: true */
40
+ /*global setTimeout: false */
41
+
42
+ var requirejs, require, define;
43
+ (function (undef) {
44
+ var main, req, makeMap, handlers,
45
+ defined = {},
46
+ waiting = {},
47
+ config = {},
48
+ defining = {},
49
+ hasOwn = Object.prototype.hasOwnProperty,
50
+ aps = [].slice,
51
+ jsSuffixRegExp = /\.js$/;
52
+
53
+ function hasProp(obj, prop) {
54
+ return hasOwn.call(obj, prop);
55
+ }
56
+
57
+ /**
58
+ * Given a relative module name, like ./something, normalize it to
59
+ * a real name that can be mapped to a path.
60
+ * @param {String} name the relative name
61
+ * @param {String} baseName a real name that the name arg is relative
62
+ * to.
63
+ * @returns {String} normalized name
64
+ */
65
+ function normalize(name, baseName) {
66
+ var nameParts, nameSegment, mapValue, foundMap, lastIndex,
67
+ foundI, foundStarMap, starI, i, j, part,
68
+ baseParts = baseName && baseName.split("/"),
69
+ map = config.map,
70
+ starMap = (map && map['*']) || {};
71
+
72
+ //Adjust any relative paths.
73
+ if (name && name.charAt(0) === ".") {
74
+ //If have a base name, try to normalize against it,
75
+ //otherwise, assume it is a top-level require that will
76
+ //be relative to baseUrl in the end.
77
+ if (baseName) {
78
+ //Convert baseName to array, and lop off the last part,
79
+ //so that . matches that "directory" and not name of the baseName's
80
+ //module. For instance, baseName of "one/two/three", maps to
81
+ //"one/two/three.js", but we want the directory, "one/two" for
82
+ //this normalization.
83
+ baseParts = baseParts.slice(0, baseParts.length - 1);
84
+ name = name.split('/');
85
+ lastIndex = name.length - 1;
86
+
87
+ // Node .js allowance:
88
+ if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
89
+ name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
90
+ }
91
+
92
+ name = baseParts.concat(name);
93
+
94
+ //start trimDots
95
+ for (i = 0; i < name.length; i += 1) {
96
+ part = name[i];
97
+ if (part === ".") {
98
+ name.splice(i, 1);
99
+ i -= 1;
100
+ } else if (part === "..") {
101
+ if (i === 1 && (name[2] === '..' || name[0] === '..')) {
102
+ //End of the line. Keep at least one non-dot
103
+ //path segment at the front so it can be mapped
104
+ //correctly to disk. Otherwise, there is likely
105
+ //no path mapping for a path starting with '..'.
106
+ //This can still fail, but catches the most reasonable
107
+ //uses of ..
108
+ break;
109
+ } else if (i > 0) {
110
+ name.splice(i - 1, 2);
111
+ i -= 2;
112
+ }
113
+ }
114
+ }
115
+ //end trimDots
116
+
117
+ name = name.join("/");
118
+ } else if (name.indexOf('./') === 0) {
119
+ // No baseName, so this is ID is resolved relative
120
+ // to baseUrl, pull off the leading dot.
121
+ name = name.substring(2);
122
+ }
123
+ }
124
+
125
+ //Apply map config if available.
126
+ if ((baseParts || starMap) && map) {
127
+ nameParts = name.split('/');
128
+
129
+ for (i = nameParts.length; i > 0; i -= 1) {
130
+ nameSegment = nameParts.slice(0, i).join("/");
131
+
132
+ if (baseParts) {
133
+ //Find the longest baseName segment match in the config.
134
+ //So, do joins on the biggest to smallest lengths of baseParts.
135
+ for (j = baseParts.length; j > 0; j -= 1) {
136
+ mapValue = map[baseParts.slice(0, j).join('/')];
137
+
138
+ //baseName segment has config, find if it has one for
139
+ //this name.
140
+ if (mapValue) {
141
+ mapValue = mapValue[nameSegment];
142
+ if (mapValue) {
143
+ //Match, update name to the new value.
144
+ foundMap = mapValue;
145
+ foundI = i;
146
+ break;
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ if (foundMap) {
153
+ break;
154
+ }
155
+
156
+ //Check for a star map match, but just hold on to it,
157
+ //if there is a shorter segment match later in a matching
158
+ //config, then favor over this star map.
159
+ if (!foundStarMap && starMap && starMap[nameSegment]) {
160
+ foundStarMap = starMap[nameSegment];
161
+ starI = i;
162
+ }
163
+ }
164
+
165
+ if (!foundMap && foundStarMap) {
166
+ foundMap = foundStarMap;
167
+ foundI = starI;
168
+ }
169
+
170
+ if (foundMap) {
171
+ nameParts.splice(0, foundI, foundMap);
172
+ name = nameParts.join('/');
173
+ }
174
+ }
175
+
176
+ return name;
177
+ }
178
+
179
+ function makeRequire(relName, forceSync) {
180
+ return function () {
181
+ //A version of a require function that passes a moduleName
182
+ //value for items that may need to
183
+ //look up paths relative to the moduleName
184
+ return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync]));
185
+ };
186
+ }
187
+
188
+ function makeNormalize(relName) {
189
+ return function (name) {
190
+ return normalize(name, relName);
191
+ };
192
+ }
193
+
194
+ function makeLoad(depName) {
195
+ return function (value) {
196
+ defined[depName] = value;
197
+ };
198
+ }
199
+
200
+ function callDep(name) {
201
+ if (hasProp(waiting, name)) {
202
+ var args = waiting[name];
203
+ delete waiting[name];
204
+ defining[name] = true;
205
+ main.apply(undef, args);
206
+ }
207
+
208
+ if (!hasProp(defined, name) && !hasProp(defining, name)) {
209
+ throw new Error('No ' + name);
210
+ }
211
+ return defined[name];
212
+ }
213
+
214
+ //Turns a plugin!resource to [plugin, resource]
215
+ //with the plugin being undefined if the name
216
+ //did not have a plugin prefix.
217
+ function splitPrefix(name) {
218
+ var prefix,
219
+ index = name ? name.indexOf('!') : -1;
220
+ if (index > -1) {
221
+ prefix = name.substring(0, index);
222
+ name = name.substring(index + 1, name.length);
223
+ }
224
+ return [prefix, name];
225
+ }
226
+
227
+ /**
228
+ * Makes a name map, normalizing the name, and using a plugin
229
+ * for normalization if necessary. Grabs a ref to plugin
230
+ * too, as an optimization.
231
+ */
232
+ makeMap = function (name, relName) {
233
+ var plugin,
234
+ parts = splitPrefix(name),
235
+ prefix = parts[0];
236
+
237
+ name = parts[1];
238
+
239
+ if (prefix) {
240
+ prefix = normalize(prefix, relName);
241
+ plugin = callDep(prefix);
242
+ }
243
+
244
+ //Normalize according
245
+ if (prefix) {
246
+ if (plugin && plugin.normalize) {
247
+ name = plugin.normalize(name, makeNormalize(relName));
248
+ } else {
249
+ name = normalize(name, relName);
250
+ }
251
+ } else {
252
+ name = normalize(name, relName);
253
+ parts = splitPrefix(name);
254
+ prefix = parts[0];
255
+ name = parts[1];
256
+ if (prefix) {
257
+ plugin = callDep(prefix);
258
+ }
259
+ }
260
+
261
+ //Using ridiculous property names for space reasons
262
+ return {
263
+ f: prefix ? prefix + '!' + name : name, //fullName
264
+ n: name,
265
+ pr: prefix,
266
+ p: plugin
267
+ };
268
+ };
269
+
270
+ function makeConfig(name) {
271
+ return function () {
272
+ return (config && config.config && config.config[name]) || {};
273
+ };
274
+ }
275
+
276
+ handlers = {
277
+ require: function (name) {
278
+ return makeRequire(name);
279
+ },
280
+ exports: function (name) {
281
+ var e = defined[name];
282
+ if (typeof e !== 'undefined') {
283
+ return e;
284
+ } else {
285
+ return (defined[name] = {});
286
+ }
287
+ },
288
+ module: function (name) {
289
+ return {
290
+ id: name,
291
+ uri: '',
292
+ exports: defined[name],
293
+ config: makeConfig(name)
294
+ };
295
+ }
296
+ };
297
+
298
+ main = function (name, deps, callback, relName) {
299
+ var cjsModule, depName, ret, map, i,
300
+ args = [],
301
+ callbackType = typeof callback,
302
+ usingExports;
303
+
304
+ //Use name if no relName
305
+ relName = relName || name;
306
+
307
+ //Call the callback to define the module, if necessary.
308
+ if (callbackType === 'undefined' || callbackType === 'function') {
309
+ //Pull out the defined dependencies and pass the ordered
310
+ //values to the callback.
311
+ //Default to [require, exports, module] if no deps
312
+ deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
313
+ for (i = 0; i < deps.length; i += 1) {
314
+ map = makeMap(deps[i], relName);
315
+ depName = map.f;
316
+
317
+ //Fast path CommonJS standard dependencies.
318
+ if (depName === "require") {
319
+ args[i] = handlers.require(name);
320
+ } else if (depName === "exports") {
321
+ //CommonJS module spec 1.1
322
+ args[i] = handlers.exports(name);
323
+ usingExports = true;
324
+ } else if (depName === "module") {
325
+ //CommonJS module spec 1.1
326
+ cjsModule = args[i] = handlers.module(name);
327
+ } else if (hasProp(defined, depName) ||
328
+ hasProp(waiting, depName) ||
329
+ hasProp(defining, depName)) {
330
+ args[i] = callDep(depName);
331
+ } else if (map.p) {
332
+ map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
333
+ args[i] = defined[depName];
334
+ } else {
335
+ throw new Error(name + ' missing ' + depName);
336
+ }
337
+ }
338
+
339
+ ret = callback ? callback.apply(defined[name], args) : undefined;
340
+
341
+ if (name) {
342
+ //If setting exports via "module" is in play,
343
+ //favor that over return value and exports. After that,
344
+ //favor a non-undefined return value over exports use.
345
+ if (cjsModule && cjsModule.exports !== undef &&
346
+ cjsModule.exports !== defined[name]) {
347
+ defined[name] = cjsModule.exports;
348
+ } else if (ret !== undef || !usingExports) {
349
+ //Use the return value from the function.
350
+ defined[name] = ret;
351
+ }
352
+ }
353
+ } else if (name) {
354
+ //May just be an object definition for the module. Only
355
+ //worry about defining if have a module name.
356
+ defined[name] = callback;
357
+ }
358
+ };
359
+
360
+ requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
361
+ if (typeof deps === "string") {
362
+ if (handlers[deps]) {
363
+ //callback in this case is really relName
364
+ return handlers[deps](callback);
365
+ }
366
+ //Just return the module wanted. In this scenario, the
367
+ //deps arg is the module name, and second arg (if passed)
368
+ //is just the relName.
369
+ //Normalize module name, if it contains . or ..
370
+ return callDep(makeMap(deps, callback).f);
371
+ } else if (!deps.splice) {
372
+ //deps is a config object, not an array.
373
+ config = deps;
374
+ if (config.deps) {
375
+ req(config.deps, config.callback);
376
+ }
377
+ if (!callback) {
378
+ return;
379
+ }
380
+
381
+ if (callback.splice) {
382
+ //callback is an array, which means it is a dependency list.
383
+ //Adjust args if there are dependencies
384
+ deps = callback;
385
+ callback = relName;
386
+ relName = null;
387
+ } else {
388
+ deps = undef;
389
+ }
390
+ }
391
+
392
+ //Support require(['a'])
393
+ callback = callback || function () {};
394
+
395
+ //If relName is a function, it is an errback handler,
396
+ //so remove it.
397
+ if (typeof relName === 'function') {
398
+ relName = forceSync;
399
+ forceSync = alt;
400
+ }
401
+
402
+ //Simulate async callback;
403
+ if (forceSync) {
404
+ main(undef, deps, callback, relName);
405
+ } else {
406
+ //Using a non-zero value because of concern for what old browsers
407
+ //do, and latest browsers "upgrade" to 4 if lower value is used:
408
+ //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
409
+ //If want a value immediately, use require('id') instead -- something
410
+ //that works in almond on the global level, but not guaranteed and
411
+ //unlikely to work in other AMD implementations.
412
+ setTimeout(function () {
413
+ main(undef, deps, callback, relName);
414
+ }, 4);
415
+ }
416
+
417
+ return req;
418
+ };
419
+
420
+ /**
421
+ * Just drops the config on the floor, but returns req in case
422
+ * the config return value is used.
423
+ */
424
+ req.config = function (cfg) {
425
+ return req(cfg);
426
+ };
427
+
428
+ /**
429
+ * Expose module registry for debugging and tooling
430
+ */
431
+ requirejs._defined = defined;
432
+
433
+ define = function (name, deps, callback) {
434
+
435
+ //This module may not have dependencies
436
+ if (!deps.splice) {
437
+ //deps is not an array, so probably means
438
+ //an object literal or factory function for
439
+ //the value. Adjust args.
440
+ callback = deps;
441
+ deps = [];
442
+ }
443
+
444
+ if (!hasProp(defined, name) && !hasProp(waiting, name)) {
445
+ waiting[name] = [name, deps, callback];
446
+ }
447
+ };
448
+
449
+ define.amd = {
450
+ jQuery: true
451
+ };
452
+ }());
453
+
454
+ S2.requirejs = requirejs;S2.require = require;S2.define = define;
455
+ }
456
+ }());
457
+ S2.define("almond", function(){});
458
+
459
+ /* global jQuery:false, $:false */
460
+ S2.define('jquery',[],function () {
461
+ var _$ = jQuery || $;
462
+
463
+ if (_$ == null && console && console.error) {
464
+ console.error(
465
+ 'Select2: An instance of jQuery or a jQuery-compatible library was not ' +
466
+ 'found. Make sure that you are including jQuery before Select2 on your ' +
467
+ 'web page.'
468
+ );
469
+ }
470
+
471
+ return _$;
472
+ });
473
+
474
+ S2.define('select2/utils',[
475
+ 'jquery'
476
+ ], function ($) {
477
+ var Utils = {};
478
+
479
+ Utils.Extend = function (ChildClass, SuperClass) {
480
+ var __hasProp = {}.hasOwnProperty;
481
+
482
+ function BaseConstructor () {
483
+ this.constructor = ChildClass;
484
+ }
485
+
486
+ for (var key in SuperClass) {
487
+ if (__hasProp.call(SuperClass, key)) {
488
+ ChildClass[key] = SuperClass[key];
489
+ }
490
+ }
491
+
492
+ BaseConstructor.prototype = SuperClass.prototype;
493
+ ChildClass.prototype = new BaseConstructor();
494
+ ChildClass.__super__ = SuperClass.prototype;
495
+
496
+ return ChildClass;
497
+ };
498
+
499
+ function getMethods (theClass) {
500
+ var proto = theClass.prototype;
501
+
502
+ var methods = [];
503
+
504
+ for (var methodName in proto) {
505
+ var m = proto[methodName];
506
+
507
+ if (typeof m !== 'function') {
508
+ continue;
509
+ }
510
+
511
+ if (methodName === 'constructor') {
512
+ continue;
513
+ }
514
+
515
+ methods.push(methodName);
516
+ }
517
+
518
+ return methods;
519
+ }
520
+
521
+ Utils.Decorate = function (SuperClass, DecoratorClass) {
522
+ var decoratedMethods = getMethods(DecoratorClass);
523
+ var superMethods = getMethods(SuperClass);
524
+
525
+ function DecoratedClass () {
526
+ var unshift = Array.prototype.unshift;
527
+
528
+ var argCount = DecoratorClass.prototype.constructor.length;
529
+
530
+ var calledConstructor = SuperClass.prototype.constructor;
531
+
532
+ if (argCount > 0) {
533
+ unshift.call(arguments, SuperClass.prototype.constructor);
534
+
535
+ calledConstructor = DecoratorClass.prototype.constructor;
536
+ }
537
+
538
+ calledConstructor.apply(this, arguments);
539
+ }
540
+
541
+ DecoratorClass.displayName = SuperClass.displayName;
542
+
543
+ function ctr () {
544
+ this.constructor = DecoratedClass;
545
+ }
546
+
547
+ DecoratedClass.prototype = new ctr();
548
+
549
+ for (var m = 0; m < superMethods.length; m++) {
550
+ var superMethod = superMethods[m];
551
+
552
+ DecoratedClass.prototype[superMethod] =
553
+ SuperClass.prototype[superMethod];
554
+ }
555
+
556
+ var calledMethod = function (methodName) {
557
+ // Stub out the original method if it's not decorating an actual method
558
+ var originalMethod = function () {};
559
+
560
+ if (methodName in DecoratedClass.prototype) {
561
+ originalMethod = DecoratedClass.prototype[methodName];
562
+ }
563
+
564
+ var decoratedMethod = DecoratorClass.prototype[methodName];
565
+
566
+ return function () {
567
+ var unshift = Array.prototype.unshift;
568
+
569
+ unshift.call(arguments, originalMethod);
570
+
571
+ return decoratedMethod.apply(this, arguments);
572
+ };
573
+ };
574
+
575
+ for (var d = 0; d < decoratedMethods.length; d++) {
576
+ var decoratedMethod = decoratedMethods[d];
577
+
578
+ DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod);
579
+ }
580
+
581
+ return DecoratedClass;
582
+ };
583
+
584
+ var Observable = function () {
585
+ this.listeners = {};
586
+ };
587
+
588
+ Observable.prototype.on = function (event, callback) {
589
+ this.listeners = this.listeners || {};
590
+
591
+ if (event in this.listeners) {
592
+ this.listeners[event].push(callback);
593
+ } else {
594
+ this.listeners[event] = [callback];
595
+ }
596
+ };
597
+
598
+ Observable.prototype.trigger = function (event) {
599
+ var slice = Array.prototype.slice;
600
+
601
+ this.listeners = this.listeners || {};
602
+
603
+ if (event in this.listeners) {
604
+ this.invoke(this.listeners[event], slice.call(arguments, 1));
605
+ }
606
+
607
+ if ('*' in this.listeners) {
608
+ this.invoke(this.listeners['*'], arguments);
609
+ }
610
+ };
611
+
612
+ Observable.prototype.invoke = function (listeners, params) {
613
+ for (var i = 0, len = listeners.length; i < len; i++) {
614
+ listeners[i].apply(this, params);
615
+ }
616
+ };
617
+
618
+ Utils.Observable = Observable;
619
+
620
+ Utils.generateChars = function (length) {
621
+ var chars = '';
622
+
623
+ for (var i = 0; i < length; i++) {
624
+ var randomChar = Math.floor(Math.random() * 36);
625
+ chars += randomChar.toString(36);
626
+ }
627
+
628
+ return chars;
629
+ };
630
+
631
+ Utils.bind = function (func, context) {
632
+ return function () {
633
+ func.apply(context, arguments);
634
+ };
635
+ };
636
+
637
+ Utils._convertData = function (data) {
638
+ for (var originalKey in data) {
639
+ var keys = originalKey.split('-');
640
+
641
+ var dataLevel = data;
642
+
643
+ if (keys.length === 1) {
644
+ continue;
645
+ }
646
+
647
+ for (var k = 0; k < keys.length; k++) {
648
+ var key = keys[k];
649
+
650
+ // Lowercase the first letter
651
+ // By default, dash-separated becomes camelCase
652
+ key = key.substring(0, 1).toLowerCase() + key.substring(1);
653
+
654
+ if (!(key in dataLevel)) {
655
+ dataLevel[key] = {};
656
+ }
657
+
658
+ if (k == keys.length - 1) {
659
+ dataLevel[key] = data[originalKey];
660
+ }
661
+
662
+ dataLevel = dataLevel[key];
663
+ }
664
+
665
+ delete data[originalKey];
666
+ }
667
+
668
+ return data;
669
+ };
670
+
671
+ Utils.hasScroll = function (index, el) {
672
+ // Adapted from the function created by @ShadowScripter
673
+ // and adapted by @BillBarry on the Stack Exchange Code Review website.
674
+ // The original code can be found at
675
+ // http://codereview.stackexchange.com/q/13338
676
+ // and was designed to be used with the Sizzle selector engine.
677
+
678
+ var $el = $(el);
679
+ var overflowX = el.style.overflowX;
680
+ var overflowY = el.style.overflowY;
681
+
682
+ //Check both x and y declarations
683
+ if (overflowX === overflowY &&
684
+ (overflowY === 'hidden' || overflowY === 'visible')) {
685
+ return false;
686
+ }
687
+
688
+ if (overflowX === 'scroll' || overflowY === 'scroll') {
689
+ return true;
690
+ }
691
+
692
+ return ($el.innerHeight() < el.scrollHeight ||
693
+ $el.innerWidth() < el.scrollWidth);
694
+ };
695
+
696
+ Utils.escapeMarkup = function (markup) {
697
+ var replaceMap = {
698
+ '\\': '&#92;',
699
+ '&': '&amp;',
700
+ '<': '&lt;',
701
+ '>': '&gt;',
702
+ '"': '&quot;',
703
+ '\'': '&#39;',
704
+ '/': '&#47;'
705
+ };
706
+
707
+ // Do not try to escape the markup if it's not a string
708
+ if (typeof markup !== 'string') {
709
+ return markup;
710
+ }
711
+
712
+ return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
713
+ return replaceMap[match];
714
+ });
715
+ };
716
+
717
+ // Append an array of jQuery nodes to a given element.
718
+ Utils.appendMany = function ($element, $nodes) {
719
+ // jQuery 1.7.x does not support $.fn.append() with an array
720
+ // Fall back to a jQuery object collection using $.fn.add()
721
+ if ($.fn.jquery.substr(0, 3) === '1.7') {
722
+ var $jqNodes = $();
723
+
724
+ $.map($nodes, function (node) {
725
+ $jqNodes = $jqNodes.add(node);
726
+ });
727
+
728
+ $nodes = $jqNodes;
729
+ }
730
+
731
+ $element.append($nodes);
732
+ };
733
+
734
+ return Utils;
735
+ });
736
+
737
+ S2.define('select2/results',[
738
+ 'jquery',
739
+ './utils'
740
+ ], function ($, Utils) {
741
+ function Results ($element, options, dataAdapter) {
742
+ this.$element = $element;
743
+ this.data = dataAdapter;
744
+ this.options = options;
745
+
746
+ Results.__super__.constructor.call(this);
747
+ }
748
+
749
+ Utils.Extend(Results, Utils.Observable);
750
+
751
+ Results.prototype.render = function () {
752
+ var $results = $(
753
+ '<ul class="select2-results__options" role="tree"></ul>'
754
+ );
755
+
756
+ if (this.options.get('multiple')) {
757
+ $results.attr('aria-multiselectable', 'true');
758
+ }
759
+
760
+ this.$results = $results;
761
+
762
+ return $results;
763
+ };
764
+
765
+ Results.prototype.clear = function () {
766
+ this.$results.empty();
767
+ };
768
+
769
+ Results.prototype.displayMessage = function (params) {
770
+ var escapeMarkup = this.options.get('escapeMarkup');
771
+
772
+ this.clear();
773
+ this.hideLoading();
774
+
775
+ var $message = $(
776
+ '<li role="treeitem" class="select2-results__option"></li>'
777
+ );
778
+
779
+ var message = this.options.get('translations').get(params.message);
780
+
781
+ $message.append(
782
+ escapeMarkup(
783
+ message(params.args)
784
+ )
785
+ );
786
+
787
+ this.$results.append($message);
788
+ };
789
+
790
+ Results.prototype.append = function (data) {
791
+ this.hideLoading();
792
+
793
+ var $options = [];
794
+
795
+ if (data.results == null || data.results.length === 0) {
796
+ if (this.$results.children().length === 0) {
797
+ this.trigger('results:message', {
798
+ message: 'noResults'
799
+ });
800
+ }
801
+
802
+ return;
803
+ }
804
+
805
+ data.results = this.sort(data.results);
806
+
807
+ for (var d = 0; d < data.results.length; d++) {
808
+ var item = data.results[d];
809
+
810
+ var $option = this.option(item);
811
+
812
+ $options.push($option);
813
+ }
814
+
815
+ this.$results.append($options);
816
+ };
817
+
818
+ Results.prototype.position = function ($results, $dropdown) {
819
+ var $resultsContainer = $dropdown.find('.select2-results');
820
+ $resultsContainer.append($results);
821
+ };
822
+
823
+ Results.prototype.sort = function (data) {
824
+ var sorter = this.options.get('sorter');
825
+
826
+ return sorter(data);
827
+ };
828
+
829
+ Results.prototype.setClasses = function () {
830
+ var self = this;
831
+
832
+ this.data.current(function (selected) {
833
+ var selectedIds = $.map(selected, function (s) {
834
+ return s.id.toString();
835
+ });
836
+
837
+ var $options = self.$results
838
+ .find('.select2-results__option[aria-selected]');
839
+
840
+ $options.each(function () {
841
+ var $option = $(this);
842
+
843
+ var item = $.data(this, 'data');
844
+
845
+ // id needs to be converted to a string when comparing
846
+ var id = '' + item.id;
847
+
848
+ if ((item.element != null && item.element.selected) ||
849
+ (item.element == null && $.inArray(id, selectedIds) > -1)) {
850
+ $option.attr('aria-selected', 'true');
851
+ } else {
852
+ $option.attr('aria-selected', 'false');
853
+ }
854
+ });
855
+
856
+ var $selected = $options.filter('[aria-selected=true]');
857
+
858
+ // Check if there are any selected options
859
+ if ($selected.length > 0) {
860
+ // If there are selected options, highlight the first
861
+ $selected.first().trigger('mouseenter');
862
+ } else {
863
+ // If there are no selected options, highlight the first option
864
+ // in the dropdown
865
+ $options.first().trigger('mouseenter');
866
+ }
867
+ });
868
+ };
869
+
870
+ Results.prototype.showLoading = function (params) {
871
+ this.hideLoading();
872
+
873
+ var loadingMore = this.options.get('translations').get('searching');
874
+
875
+ var loading = {
876
+ disabled: true,
877
+ loading: true,
878
+ text: loadingMore(params)
879
+ };
880
+ var $loading = this.option(loading);
881
+ $loading.className += ' loading-results';
882
+
883
+ this.$results.prepend($loading);
884
+ };
885
+
886
+ Results.prototype.hideLoading = function () {
887
+ this.$results.find('.loading-results').remove();
888
+ };
889
+
890
+ Results.prototype.option = function (data) {
891
+ var option = document.createElement('li');
892
+ option.className = 'select2-results__option';
893
+
894
+ var attrs = {
895
+ 'role': 'treeitem',
896
+ 'aria-selected': 'false'
897
+ };
898
+
899
+ if (data.disabled) {
900
+ delete attrs['aria-selected'];
901
+ attrs['aria-disabled'] = 'true';
902
+ }
903
+
904
+ if (data.id == null) {
905
+ delete attrs['aria-selected'];
906
+ }
907
+
908
+ if (data._resultId != null) {
909
+ option.id = data._resultId;
910
+ }
911
+
912
+ if (data.title) {
913
+ option.title = data.title;
914
+ }
915
+
916
+ if (data.children) {
917
+ attrs.role = 'group';
918
+ attrs['aria-label'] = data.text;
919
+ delete attrs['aria-selected'];
920
+ }
921
+
922
+ for (var attr in attrs) {
923
+ var val = attrs[attr];
924
+
925
+ option.setAttribute(attr, val);
926
+ }
927
+
928
+ if (data.children) {
929
+ var $option = $(option);
930
+
931
+ var label = document.createElement('strong');
932
+ label.className = 'select2-results__group';
933
+
934
+ var $label = $(label);
935
+ this.template(data, label);
936
+
937
+ var $children = [];
938
+
939
+ for (var c = 0; c < data.children.length; c++) {
940
+ var child = data.children[c];
941
+
942
+ var $child = this.option(child);
943
+
944
+ $children.push($child);
945
+ }
946
+
947
+ var $childrenContainer = $('<ul></ul>', {
948
+ 'class': 'select2-results__options select2-results__options--nested'
949
+ });
950
+
951
+ $childrenContainer.append($children);
952
+
953
+ $option.append(label);
954
+ $option.append($childrenContainer);
955
+ } else {
956
+ this.template(data, option);
957
+ }
958
+
959
+ $.data(option, 'data', data);
960
+
961
+ return option;
962
+ };
963
+
964
+ Results.prototype.bind = function (container, $container) {
965
+ var self = this;
966
+
967
+ var id = container.id + '-results';
968
+
969
+ this.$results.attr('id', id);
970
+
971
+ container.on('results:all', function (params) {
972
+ self.clear();
973
+ self.append(params.data);
974
+
975
+ if (container.isOpen()) {
976
+ self.setClasses();
977
+ }
978
+ });
979
+
980
+ container.on('results:append', function (params) {
981
+ self.append(params.data);
982
+
983
+ if (container.isOpen()) {
984
+ self.setClasses();
985
+ }
986
+ });
987
+
988
+ container.on('query', function (params) {
989
+ self.showLoading(params);
990
+ });
991
+
992
+ container.on('select', function () {
993
+ if (!container.isOpen()) {
994
+ return;
995
+ }
996
+
997
+ self.setClasses();
998
+ });
999
+
1000
+ container.on('unselect', function () {
1001
+ if (!container.isOpen()) {
1002
+ return;
1003
+ }
1004
+
1005
+ self.setClasses();
1006
+ });
1007
+
1008
+ container.on('open', function () {
1009
+ // When the dropdown is open, aria-expended="true"
1010
+ self.$results.attr('aria-expanded', 'true');
1011
+ self.$results.attr('aria-hidden', 'false');
1012
+
1013
+ self.setClasses();
1014
+ self.ensureHighlightVisible();
1015
+ });
1016
+
1017
+ container.on('close', function () {
1018
+ // When the dropdown is closed, aria-expended="false"
1019
+ self.$results.attr('aria-expanded', 'false');
1020
+ self.$results.attr('aria-hidden', 'true');
1021
+ self.$results.removeAttr('aria-activedescendant');
1022
+ });
1023
+
1024
+ container.on('results:toggle', function () {
1025
+ var $highlighted = self.getHighlightedResults();
1026
+
1027
+ if ($highlighted.length === 0) {
1028
+ return;
1029
+ }
1030
+
1031
+ $highlighted.trigger('mouseup');
1032
+ });
1033
+
1034
+ container.on('results:select', function () {
1035
+ var $highlighted = self.getHighlightedResults();
1036
+
1037
+ if ($highlighted.length === 0) {
1038
+ return;
1039
+ }
1040
+
1041
+ var data = $highlighted.data('data');
1042
+
1043
+ if ($highlighted.attr('aria-selected') == 'true') {
1044
+ self.trigger('close');
1045
+ } else {
1046
+ self.trigger('select', {
1047
+ data: data
1048
+ });
1049
+ }
1050
+ });
1051
+
1052
+ container.on('results:previous', function () {
1053
+ var $highlighted = self.getHighlightedResults();
1054
+
1055
+ var $options = self.$results.find('[aria-selected]');
1056
+
1057
+ var currentIndex = $options.index($highlighted);
1058
+
1059
+ // If we are already at te top, don't move further
1060
+ if (currentIndex === 0) {
1061
+ return;
1062
+ }
1063
+
1064
+ var nextIndex = currentIndex - 1;
1065
+
1066
+ // If none are highlighted, highlight the first
1067
+ if ($highlighted.length === 0) {
1068
+ nextIndex = 0;
1069
+ }
1070
+
1071
+ var $next = $options.eq(nextIndex);
1072
+
1073
+ $next.trigger('mouseenter');
1074
+
1075
+ var currentOffset = self.$results.offset().top;
1076
+ var nextTop = $next.offset().top;
1077
+ var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset);
1078
+
1079
+ if (nextIndex === 0) {
1080
+ self.$results.scrollTop(0);
1081
+ } else if (nextTop - currentOffset < 0) {
1082
+ self.$results.scrollTop(nextOffset);
1083
+ }
1084
+ });
1085
+
1086
+ container.on('results:next', function () {
1087
+ var $highlighted = self.getHighlightedResults();
1088
+
1089
+ var $options = self.$results.find('[aria-selected]');
1090
+
1091
+ var currentIndex = $options.index($highlighted);
1092
+
1093
+ var nextIndex = currentIndex + 1;
1094
+
1095
+ // If we are at the last option, stay there
1096
+ if (nextIndex >= $options.length) {
1097
+ return;
1098
+ }
1099
+
1100
+ var $next = $options.eq(nextIndex);
1101
+
1102
+ $next.trigger('mouseenter');
1103
+
1104
+ var currentOffset = self.$results.offset().top +
1105
+ self.$results.outerHeight(false);
1106
+ var nextBottom = $next.offset().top + $next.outerHeight(false);
1107
+ var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset;
1108
+
1109
+ if (nextIndex === 0) {
1110
+ self.$results.scrollTop(0);
1111
+ } else if (nextBottom > currentOffset) {
1112
+ self.$results.scrollTop(nextOffset);
1113
+ }
1114
+ });
1115
+
1116
+ container.on('results:focus', function (params) {
1117
+ params.element.addClass('select2-results__option--highlighted');
1118
+ });
1119
+
1120
+ container.on('results:message', function (params) {
1121
+ self.displayMessage(params);
1122
+ });
1123
+
1124
+ if ($.fn.mousewheel) {
1125
+ this.$results.on('mousewheel', function (e) {
1126
+ var top = self.$results.scrollTop();
1127
+
1128
+ var bottom = (
1129
+ self.$results.get(0).scrollHeight -
1130
+ self.$results.scrollTop() +
1131
+ e.deltaY
1132
+ );
1133
+
1134
+ var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
1135
+ var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();
1136
+
1137
+ if (isAtTop) {
1138
+ self.$results.scrollTop(0);
1139
+
1140
+ e.preventDefault();
1141
+ e.stopPropagation();
1142
+ } else if (isAtBottom) {
1143
+ self.$results.scrollTop(
1144
+ self.$results.get(0).scrollHeight - self.$results.height()
1145
+ );
1146
+
1147
+ e.preventDefault();
1148
+ e.stopPropagation();
1149
+ }
1150
+ });
1151
+ }
1152
+
1153
+ this.$results.on('mouseup', '.select2-results__option[aria-selected]',
1154
+ function (evt) {
1155
+ var $this = $(this);
1156
+
1157
+ var data = $this.data('data');
1158
+
1159
+ if ($this.attr('aria-selected') === 'true') {
1160
+ if (self.options.get('multiple')) {
1161
+ self.trigger('unselect', {
1162
+ originalEvent: evt,
1163
+ data: data
1164
+ });
1165
+ } else {
1166
+ self.trigger('close');
1167
+ }
1168
+
1169
+ return;
1170
+ }
1171
+
1172
+ self.trigger('select', {
1173
+ originalEvent: evt,
1174
+ data: data
1175
+ });
1176
+ });
1177
+
1178
+ this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
1179
+ function (evt) {
1180
+ var data = $(this).data('data');
1181
+
1182
+ self.getHighlightedResults()
1183
+ .removeClass('select2-results__option--highlighted');
1184
+
1185
+ self.trigger('results:focus', {
1186
+ data: data,
1187
+ element: $(this)
1188
+ });
1189
+ });
1190
+ };
1191
+
1192
+ Results.prototype.getHighlightedResults = function () {
1193
+ var $highlighted = this.$results
1194
+ .find('.select2-results__option--highlighted');
1195
+
1196
+ return $highlighted;
1197
+ };
1198
+
1199
+ Results.prototype.destroy = function () {
1200
+ this.$results.remove();
1201
+ };
1202
+
1203
+ Results.prototype.ensureHighlightVisible = function () {
1204
+ var $highlighted = this.getHighlightedResults();
1205
+
1206
+ if ($highlighted.length === 0) {
1207
+ return;
1208
+ }
1209
+
1210
+ var $options = this.$results.find('[aria-selected]');
1211
+
1212
+ var currentIndex = $options.index($highlighted);
1213
+
1214
+ var currentOffset = this.$results.offset().top;
1215
+ var nextTop = $highlighted.offset().top;
1216
+ var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset);
1217
+
1218
+ var offsetDelta = nextTop - currentOffset;
1219
+ nextOffset -= $highlighted.outerHeight(false) * 2;
1220
+
1221
+ if (currentIndex <= 2) {
1222
+ this.$results.scrollTop(0);
1223
+ } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) {
1224
+ this.$results.scrollTop(nextOffset);
1225
+ }
1226
+ };
1227
+
1228
+ Results.prototype.template = function (result, container) {
1229
+ var template = this.options.get('templateResult');
1230
+ var escapeMarkup = this.options.get('escapeMarkup');
1231
+
1232
+ var content = template(result);
1233
+
1234
+ if (content == null) {
1235
+ container.style.display = 'none';
1236
+ } else if (typeof content === 'string') {
1237
+ container.innerHTML = escapeMarkup(content);
1238
+ } else {
1239
+ $(container).append(content);
1240
+ }
1241
+ };
1242
+
1243
+ return Results;
1244
+ });
1245
+
1246
+ S2.define('select2/keys',[
1247
+
1248
+ ], function () {
1249
+ var KEYS = {
1250
+ BACKSPACE: 8,
1251
+ TAB: 9,
1252
+ ENTER: 13,
1253
+ SHIFT: 16,
1254
+ CTRL: 17,
1255
+ ALT: 18,
1256
+ ESC: 27,
1257
+ SPACE: 32,
1258
+ PAGE_UP: 33,
1259
+ PAGE_DOWN: 34,
1260
+ END: 35,
1261
+ HOME: 36,
1262
+ LEFT: 37,
1263
+ UP: 38,
1264
+ RIGHT: 39,
1265
+ DOWN: 40,
1266
+ DELETE: 46
1267
+ };
1268
+
1269
+ return KEYS;
1270
+ });
1271
+
1272
+ S2.define('select2/selection/base',[
1273
+ 'jquery',
1274
+ '../utils',
1275
+ '../keys'
1276
+ ], function ($, Utils, KEYS) {
1277
+ function BaseSelection ($element, options) {
1278
+ this.$element = $element;
1279
+ this.options = options;
1280
+
1281
+ BaseSelection.__super__.constructor.call(this);
1282
+ }
1283
+
1284
+ Utils.Extend(BaseSelection, Utils.Observable);
1285
+
1286
+ BaseSelection.prototype.render = function () {
1287
+ var $selection = $(
1288
+ '<span class="select2-selection" role="combobox" ' +
1289
+ 'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
1290
+ '</span>'
1291
+ );
1292
+
1293
+ this._tabindex = 0;
1294
+
1295
+ if (this.$element.data('old-tabindex') != null) {
1296
+ this._tabindex = this.$element.data('old-tabindex');
1297
+ } else if (this.$element.attr('tabindex') != null) {
1298
+ this._tabindex = this.$element.attr('tabindex');
1299
+ }
1300
+
1301
+ $selection.attr('title', this.$element.attr('title'));
1302
+ $selection.attr('tabindex', this._tabindex);
1303
+
1304
+ this.$selection = $selection;
1305
+
1306
+ return $selection;
1307
+ };
1308
+
1309
+ BaseSelection.prototype.bind = function (container, $container) {
1310
+ var self = this;
1311
+
1312
+ var id = container.id + '-container';
1313
+ var resultsId = container.id + '-results';
1314
+
1315
+ this.container = container;
1316
+
1317
+ this.$selection.on('focus', function (evt) {
1318
+ self.trigger('focus', evt);
1319
+ });
1320
+
1321
+ this.$selection.on('blur', function (evt) {
1322
+ self.trigger('blur', evt);
1323
+ });
1324
+
1325
+ this.$selection.on('keydown', function (evt) {
1326
+ self.trigger('keypress', evt);
1327
+
1328
+ if (evt.which === KEYS.SPACE) {
1329
+ evt.preventDefault();
1330
+ }
1331
+ });
1332
+
1333
+ container.on('results:focus', function (params) {
1334
+ self.$selection.attr('aria-activedescendant', params.data._resultId);
1335
+ });
1336
+
1337
+ container.on('selection:update', function (params) {
1338
+ self.update(params.data);
1339
+ });
1340
+
1341
+ container.on('open', function () {
1342
+ // When the dropdown is open, aria-expanded="true"
1343
+ self.$selection.attr('aria-expanded', 'true');
1344
+ self.$selection.attr('aria-owns', resultsId);
1345
+
1346
+ self._attachCloseHandler(container);
1347
+ });
1348
+
1349
+ container.on('close', function () {
1350
+ // When the dropdown is closed, aria-expanded="false"
1351
+ self.$selection.attr('aria-expanded', 'false');
1352
+ self.$selection.removeAttr('aria-activedescendant');
1353
+ self.$selection.removeAttr('aria-owns');
1354
+
1355
+ self.$selection.focus();
1356
+
1357
+ self._detachCloseHandler(container);
1358
+ });
1359
+
1360
+ container.on('enable', function () {
1361
+ self.$selection.attr('tabindex', self._tabindex);
1362
+ });
1363
+
1364
+ container.on('disable', function () {
1365
+ self.$selection.attr('tabindex', '-1');
1366
+ });
1367
+ };
1368
+
1369
+ BaseSelection.prototype._attachCloseHandler = function (container) {
1370
+ var self = this;
1371
+
1372
+ $(document.body).on('mousedown.select2.' + container.id, function (e) {
1373
+ var $target = $(e.target);
1374
+
1375
+ var $select = $target.closest('.select2');
1376
+
1377
+ var $all = $('.select2.select2-container--open');
1378
+
1379
+ $all.each(function () {
1380
+ var $this = $(this);
1381
+
1382
+ if (this == $select[0]) {
1383
+ return;
1384
+ }
1385
+
1386
+ var $element = $this.data('element');
1387
+
1388
+ $element.select2('close');
1389
+ });
1390
+ });
1391
+ };
1392
+
1393
+ BaseSelection.prototype._detachCloseHandler = function (container) {
1394
+ $(document.body).off('mousedown.select2.' + container.id);
1395
+ };
1396
+
1397
+ BaseSelection.prototype.position = function ($selection, $container) {
1398
+ var $selectionContainer = $container.find('.selection');
1399
+ $selectionContainer.append($selection);
1400
+ };
1401
+
1402
+ BaseSelection.prototype.destroy = function () {
1403
+ this._detachCloseHandler(this.container);
1404
+ };
1405
+
1406
+ BaseSelection.prototype.update = function (data) {
1407
+ throw new Error('The `update` method must be defined in child classes.');
1408
+ };
1409
+
1410
+ return BaseSelection;
1411
+ });
1412
+
1413
+ S2.define('select2/selection/single',[
1414
+ 'jquery',
1415
+ './base',
1416
+ '../utils',
1417
+ '../keys'
1418
+ ], function ($, BaseSelection, Utils, KEYS) {
1419
+ function SingleSelection () {
1420
+ SingleSelection.__super__.constructor.apply(this, arguments);
1421
+ }
1422
+
1423
+ Utils.Extend(SingleSelection, BaseSelection);
1424
+
1425
+ SingleSelection.prototype.render = function () {
1426
+ var $selection = SingleSelection.__super__.render.call(this);
1427
+
1428
+ $selection.addClass('select2-selection--single');
1429
+
1430
+ $selection.html(
1431
+ '<span class="select2-selection__rendered"></span>' +
1432
+ '<span class="select2-selection__arrow" role="presentation">' +
1433
+ '<b role="presentation"></b>' +
1434
+ '</span>'
1435
+ );
1436
+
1437
+ return $selection;
1438
+ };
1439
+
1440
+ SingleSelection.prototype.bind = function (container, $container) {
1441
+ var self = this;
1442
+
1443
+ SingleSelection.__super__.bind.apply(this, arguments);
1444
+
1445
+ var id = container.id + '-container';
1446
+
1447
+ this.$selection.find('.select2-selection__rendered').attr('id', id);
1448
+ this.$selection.attr('aria-labelledby', id);
1449
+
1450
+ this.$selection.on('mousedown', function (evt) {
1451
+ // Only respond to left clicks
1452
+ if (evt.which !== 1) {
1453
+ return;
1454
+ }
1455
+
1456
+ self.trigger('toggle', {
1457
+ originalEvent: evt
1458
+ });
1459
+ });
1460
+
1461
+ this.$selection.on('focus', function (evt) {
1462
+ // User focuses on the container
1463
+ });
1464
+
1465
+ this.$selection.on('blur', function (evt) {
1466
+ // User exits the container
1467
+ });
1468
+
1469
+ container.on('selection:update', function (params) {
1470
+ self.update(params.data);
1471
+ });
1472
+ };
1473
+
1474
+ SingleSelection.prototype.clear = function () {
1475
+ this.$selection.find('.select2-selection__rendered').empty();
1476
+ };
1477
+
1478
+ SingleSelection.prototype.display = function (data) {
1479
+ var template = this.options.get('templateSelection');
1480
+ var escapeMarkup = this.options.get('escapeMarkup');
1481
+
1482
+ return escapeMarkup(template(data));
1483
+ };
1484
+
1485
+ SingleSelection.prototype.selectionContainer = function () {
1486
+ return $('<span></span>');
1487
+ };
1488
+
1489
+ SingleSelection.prototype.update = function (data) {
1490
+ if (data.length === 0) {
1491
+ this.clear();
1492
+ return;
1493
+ }
1494
+
1495
+ var selection = data[0];
1496
+
1497
+ var formatted = this.display(selection);
1498
+
1499
+ var $rendered = this.$selection.find('.select2-selection__rendered');
1500
+ $rendered.empty().append(formatted);
1501
+ $rendered.prop('title', selection.title || selection.text);
1502
+ };
1503
+
1504
+ return SingleSelection;
1505
+ });
1506
+
1507
+ S2.define('select2/selection/multiple',[
1508
+ 'jquery',
1509
+ './base',
1510
+ '../utils'
1511
+ ], function ($, BaseSelection, Utils) {
1512
+ function MultipleSelection ($element, options) {
1513
+ MultipleSelection.__super__.constructor.apply(this, arguments);
1514
+ }
1515
+
1516
+ Utils.Extend(MultipleSelection, BaseSelection);
1517
+
1518
+ MultipleSelection.prototype.render = function () {
1519
+ var $selection = MultipleSelection.__super__.render.call(this);
1520
+
1521
+ $selection.addClass('select2-selection--multiple');
1522
+
1523
+ $selection.html(
1524
+ '<ul class="select2-selection__rendered"></ul>'
1525
+ );
1526
+
1527
+ return $selection;
1528
+ };
1529
+
1530
+ MultipleSelection.prototype.bind = function (container, $container) {
1531
+ var self = this;
1532
+
1533
+ MultipleSelection.__super__.bind.apply(this, arguments);
1534
+
1535
+ this.$selection.on('click', function (evt) {
1536
+ self.trigger('toggle', {
1537
+ originalEvent: evt
1538
+ });
1539
+ });
1540
+
1541
+ this.$selection.on('click', '.select2-selection__choice__remove',
1542
+ function (evt) {
1543
+ var $remove = $(this);
1544
+ var $selection = $remove.parent();
1545
+
1546
+ var data = $selection.data('data');
1547
+
1548
+ self.trigger('unselect', {
1549
+ originalEvent: evt,
1550
+ data: data
1551
+ });
1552
+ });
1553
+ };
1554
+
1555
+ MultipleSelection.prototype.clear = function () {
1556
+ this.$selection.find('.select2-selection__rendered').empty();
1557
+ };
1558
+
1559
+ MultipleSelection.prototype.display = function (data) {
1560
+ var template = this.options.get('templateSelection');
1561
+ var escapeMarkup = this.options.get('escapeMarkup');
1562
+
1563
+ return escapeMarkup(template(data));
1564
+ };
1565
+
1566
+ MultipleSelection.prototype.selectionContainer = function () {
1567
+ var $container = $(
1568
+ '<li class="select2-selection__choice">' +
1569
+ '<span class="select2-selection__choice__remove" role="presentation">' +
1570
+ '&times;' +
1571
+ '</span>' +
1572
+ '</li>'
1573
+ );
1574
+
1575
+ return $container;
1576
+ };
1577
+
1578
+ MultipleSelection.prototype.update = function (data) {
1579
+ this.clear();
1580
+
1581
+ if (data.length === 0) {
1582
+ return;
1583
+ }
1584
+
1585
+ var $selections = [];
1586
+
1587
+ for (var d = 0; d < data.length; d++) {
1588
+ var selection = data[d];
1589
+
1590
+ var formatted = this.display(selection);
1591
+ var $selection = this.selectionContainer();
1592
+
1593
+ $selection.append(formatted);
1594
+ $selection.prop('title', selection.title || selection.text);
1595
+
1596
+ $selection.data('data', selection);
1597
+
1598
+ $selections.push($selection);
1599
+ }
1600
+
1601
+ var $rendered = this.$selection.find('.select2-selection__rendered');
1602
+
1603
+ Utils.appendMany($rendered, $selections);
1604
+ };
1605
+
1606
+ return MultipleSelection;
1607
+ });
1608
+
1609
+ S2.define('select2/selection/placeholder',[
1610
+ '../utils'
1611
+ ], function (Utils) {
1612
+ function Placeholder (decorated, $element, options) {
1613
+ this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
1614
+
1615
+ decorated.call(this, $element, options);
1616
+ }
1617
+
1618
+ Placeholder.prototype.normalizePlaceholder = function (_, placeholder) {
1619
+ if (typeof placeholder === 'string') {
1620
+ placeholder = {
1621
+ id: '',
1622
+ text: placeholder
1623
+ };
1624
+ }
1625
+
1626
+ return placeholder;
1627
+ };
1628
+
1629
+ Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
1630
+ var $placeholder = this.selectionContainer();
1631
+
1632
+ $placeholder.html(this.display(placeholder));
1633
+ $placeholder.addClass('select2-selection__placeholder')
1634
+ .removeClass('select2-selection__choice');
1635
+
1636
+ return $placeholder;
1637
+ };
1638
+
1639
+ Placeholder.prototype.update = function (decorated, data) {
1640
+ var singlePlaceholder = (
1641
+ data.length == 1 && data[0].id != this.placeholder.id
1642
+ );
1643
+ var multipleSelections = data.length > 1;
1644
+
1645
+ if (multipleSelections || singlePlaceholder) {
1646
+ return decorated.call(this, data);
1647
+ }
1648
+
1649
+ this.clear();
1650
+
1651
+ var $placeholder = this.createPlaceholder(this.placeholder);
1652
+
1653
+ this.$selection.find('.select2-selection__rendered').append($placeholder);
1654
+ };
1655
+
1656
+ return Placeholder;
1657
+ });
1658
+
1659
+ S2.define('select2/selection/allowClear',[
1660
+ 'jquery',
1661
+ '../keys'
1662
+ ], function ($, KEYS) {
1663
+ function AllowClear () { }
1664
+
1665
+ AllowClear.prototype.bind = function (decorated, container, $container) {
1666
+ var self = this;
1667
+
1668
+ decorated.call(this, container, $container);
1669
+
1670
+ if (this.placeholder == null) {
1671
+ if (this.options.get('debug') && window.console && console.error) {
1672
+ console.error(
1673
+ 'Select2: The `allowClear` option should be used in combination ' +
1674
+ 'with the `placeholder` option.'
1675
+ );
1676
+ }
1677
+ }
1678
+
1679
+ this.$selection.on('mousedown', '.select2-selection__clear',
1680
+ function (evt) {
1681
+ self._handleClear(evt);
1682
+ });
1683
+
1684
+ container.on('keypress', function (evt) {
1685
+ self._handleKeyboardClear(evt, container);
1686
+ });
1687
+ };
1688
+
1689
+ AllowClear.prototype._handleClear = function (_, evt) {
1690
+ // Ignore the event if it is disabled
1691
+ if (this.options.get('disabled')) {
1692
+ return;
1693
+ }
1694
+
1695
+ var $clear = this.$selection.find('.select2-selection__clear');
1696
+
1697
+ // Ignore the event if nothing has been selected
1698
+ if ($clear.length === 0) {
1699
+ return;
1700
+ }
1701
+
1702
+ evt.stopPropagation();
1703
+
1704
+ var data = $clear.data('data');
1705
+
1706
+ for (var d = 0; d < data.length; d++) {
1707
+ var unselectData = {
1708
+ data: data[d]
1709
+ };
1710
+
1711
+ // Trigger the `unselect` event, so people can prevent it from being
1712
+ // cleared.
1713
+ this.trigger('unselect', unselectData);
1714
+
1715
+ // If the event was prevented, don't clear it out.
1716
+ if (unselectData.prevented) {
1717
+ return;
1718
+ }
1719
+ }
1720
+
1721
+ this.$element.val(this.placeholder.id).trigger('change');
1722
+
1723
+ this.trigger('toggle');
1724
+ };
1725
+
1726
+ AllowClear.prototype._handleKeyboardClear = function (_, evt, container) {
1727
+ if (container.isOpen()) {
1728
+ return;
1729
+ }
1730
+
1731
+ if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) {
1732
+ this._handleClear(evt);
1733
+ }
1734
+ };
1735
+
1736
+ AllowClear.prototype.update = function (decorated, data) {
1737
+ decorated.call(this, data);
1738
+
1739
+ if (this.$selection.find('.select2-selection__placeholder').length > 0 ||
1740
+ data.length === 0) {
1741
+ return;
1742
+ }
1743
+
1744
+ var $remove = $(
1745
+ '<span class="select2-selection__clear">' +
1746
+ '&times;' +
1747
+ '</span>'
1748
+ );
1749
+ $remove.data('data', data);
1750
+
1751
+ this.$selection.find('.select2-selection__rendered').prepend($remove);
1752
+ };
1753
+
1754
+ return AllowClear;
1755
+ });
1756
+
1757
+ S2.define('select2/selection/search',[
1758
+ 'jquery',
1759
+ '../utils',
1760
+ '../keys'
1761
+ ], function ($, Utils, KEYS) {
1762
+ function Search (decorated, $element, options) {
1763
+ decorated.call(this, $element, options);
1764
+ }
1765
+
1766
+ Search.prototype.render = function (decorated) {
1767
+ var $search = $(
1768
+ '<li class="select2-search select2-search--inline">' +
1769
+ '<input class="select2-search__field" type="search" tabindex="-1"' +
1770
+ ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
1771
+ ' spellcheck="false" role="textbox" />' +
1772
+ '</li>'
1773
+ );
1774
+
1775
+ this.$searchContainer = $search;
1776
+ this.$search = $search.find('input');
1777
+
1778
+ var $rendered = decorated.call(this);
1779
+
1780
+ return $rendered;
1781
+ };
1782
+
1783
+ Search.prototype.bind = function (decorated, container, $container) {
1784
+ var self = this;
1785
+
1786
+ decorated.call(this, container, $container);
1787
+
1788
+ container.on('open', function () {
1789
+ self.$search.attr('tabindex', 0);
1790
+
1791
+ self.$search.focus();
1792
+ });
1793
+
1794
+ container.on('close', function () {
1795
+ self.$search.attr('tabindex', -1);
1796
+
1797
+ self.$search.val('');
1798
+ self.$search.focus();
1799
+ });
1800
+
1801
+ container.on('enable', function () {
1802
+ self.$search.prop('disabled', false);
1803
+ });
1804
+
1805
+ container.on('disable', function () {
1806
+ self.$search.prop('disabled', true);
1807
+ });
1808
+
1809
+ this.$selection.on('focusin', '.select2-search--inline', function (evt) {
1810
+ self.trigger('focus', evt);
1811
+ });
1812
+
1813
+ this.$selection.on('focusout', '.select2-search--inline', function (evt) {
1814
+ self.trigger('blur', evt);
1815
+ });
1816
+
1817
+ this.$selection.on('keydown', '.select2-search--inline', function (evt) {
1818
+ evt.stopPropagation();
1819
+
1820
+ self.trigger('keypress', evt);
1821
+
1822
+ self._keyUpPrevented = evt.isDefaultPrevented();
1823
+
1824
+ var key = evt.which;
1825
+
1826
+ if (key === KEYS.BACKSPACE && self.$search.val() === '') {
1827
+ var $previousChoice = self.$searchContainer
1828
+ .prev('.select2-selection__choice');
1829
+
1830
+ if ($previousChoice.length > 0) {
1831
+ var item = $previousChoice.data('data');
1832
+
1833
+ self.searchRemoveChoice(item);
1834
+
1835
+ evt.preventDefault();
1836
+ }
1837
+ }
1838
+ });
1839
+
1840
+ // Workaround for browsers which do not support the `input` event
1841
+ // This will prevent double-triggering of events for browsers which support
1842
+ // both the `keyup` and `input` events.
1843
+ this.$selection.on('input', '.select2-search--inline', function (evt) {
1844
+ // Unbind the duplicated `keyup` event
1845
+ self.$selection.off('keyup.search');
1846
+ });
1847
+
1848
+ this.$selection.on('keyup.search input', '.select2-search--inline',
1849
+ function (evt) {
1850
+ self.handleSearch(evt);
1851
+ });
1852
+ };
1853
+
1854
+ Search.prototype.createPlaceholder = function (decorated, placeholder) {
1855
+ this.$search.attr('placeholder', placeholder.text);
1856
+ };
1857
+
1858
+ Search.prototype.update = function (decorated, data) {
1859
+ this.$search.attr('placeholder', '');
1860
+
1861
+ decorated.call(this, data);
1862
+
1863
+ this.$selection.find('.select2-selection__rendered')
1864
+ .append(this.$searchContainer);
1865
+
1866
+ this.resizeSearch();
1867
+ };
1868
+
1869
+ Search.prototype.handleSearch = function () {
1870
+ this.resizeSearch();
1871
+
1872
+ if (!this._keyUpPrevented) {
1873
+ var input = this.$search.val();
1874
+
1875
+ this.trigger('query', {
1876
+ term: input
1877
+ });
1878
+ }
1879
+
1880
+ this._keyUpPrevented = false;
1881
+ };
1882
+
1883
+ Search.prototype.searchRemoveChoice = function (decorated, item) {
1884
+ this.trigger('unselect', {
1885
+ data: item
1886
+ });
1887
+
1888
+ this.trigger('open');
1889
+
1890
+ this.$search.val(item.text + ' ');
1891
+ };
1892
+
1893
+ Search.prototype.resizeSearch = function () {
1894
+ this.$search.css('width', '25px');
1895
+
1896
+ var width = '';
1897
+
1898
+ if (this.$search.attr('placeholder') !== '') {
1899
+ width = this.$selection.find('.select2-selection__rendered').innerWidth();
1900
+ } else {
1901
+ var minimumWidth = this.$search.val().length + 1;
1902
+
1903
+ width = (minimumWidth * 0.75) + 'em';
1904
+ }
1905
+
1906
+ this.$search.css('width', width);
1907
+ };
1908
+
1909
+ return Search;
1910
+ });
1911
+
1912
+ S2.define('select2/selection/eventRelay',[
1913
+ 'jquery'
1914
+ ], function ($) {
1915
+ function EventRelay () { }
1916
+
1917
+ EventRelay.prototype.bind = function (decorated, container, $container) {
1918
+ var self = this;
1919
+ var relayEvents = [
1920
+ 'open', 'opening',
1921
+ 'close', 'closing',
1922
+ 'select', 'selecting',
1923
+ 'unselect', 'unselecting'
1924
+ ];
1925
+
1926
+ var preventableEvents = ['opening', 'closing', 'selecting', 'unselecting'];
1927
+
1928
+ decorated.call(this, container, $container);
1929
+
1930
+ container.on('*', function (name, params) {
1931
+ // Ignore events that should not be relayed
1932
+ if ($.inArray(name, relayEvents) === -1) {
1933
+ return;
1934
+ }
1935
+
1936
+ // The parameters should always be an object
1937
+ params = params || {};
1938
+
1939
+ // Generate the jQuery event for the Select2 event
1940
+ var evt = $.Event('select2:' + name, {
1941
+ params: params
1942
+ });
1943
+
1944
+ self.$element.trigger(evt);
1945
+
1946
+ // Only handle preventable events if it was one
1947
+ if ($.inArray(name, preventableEvents) === -1) {
1948
+ return;
1949
+ }
1950
+
1951
+ params.prevented = evt.isDefaultPrevented();
1952
+ });
1953
+ };
1954
+
1955
+ return EventRelay;
1956
+ });
1957
+
1958
+ S2.define('select2/translation',[
1959
+ 'jquery',
1960
+ 'require'
1961
+ ], function ($, require) {
1962
+ function Translation (dict) {
1963
+ this.dict = dict || {};
1964
+ }
1965
+
1966
+ Translation.prototype.all = function () {
1967
+ return this.dict;
1968
+ };
1969
+
1970
+ Translation.prototype.get = function (key) {
1971
+ return this.dict[key];
1972
+ };
1973
+
1974
+ Translation.prototype.extend = function (translation) {
1975
+ this.dict = $.extend({}, translation.all(), this.dict);
1976
+ };
1977
+
1978
+ // Static functions
1979
+
1980
+ Translation._cache = {};
1981
+
1982
+ Translation.loadPath = function (path) {
1983
+ if (!(path in Translation._cache)) {
1984
+ var translations = require(path);
1985
+
1986
+ Translation._cache[path] = translations;
1987
+ }
1988
+
1989
+ return new Translation(Translation._cache[path]);
1990
+ };
1991
+
1992
+ return Translation;
1993
+ });
1994
+
1995
+ S2.define('select2/diacritics',[
1996
+
1997
+ ], function () {
1998
+ var diacritics = {
1999
+ '\u24B6': 'A',
2000
+ '\uFF21': 'A',
2001
+ '\u00C0': 'A',
2002
+ '\u00C1': 'A',
2003
+ '\u00C2': 'A',
2004
+ '\u1EA6': 'A',
2005
+ '\u1EA4': 'A',
2006
+ '\u1EAA': 'A',
2007
+ '\u1EA8': 'A',
2008
+ '\u00C3': 'A',
2009
+ '\u0100': 'A',
2010
+ '\u0102': 'A',
2011
+ '\u1EB0': 'A',
2012
+ '\u1EAE': 'A',
2013
+ '\u1EB4': 'A',
2014
+ '\u1EB2': 'A',
2015
+ '\u0226': 'A',
2016
+ '\u01E0': 'A',
2017
+ '\u00C4': 'A',
2018
+ '\u01DE': 'A',
2019
+ '\u1EA2': 'A',
2020
+ '\u00C5': 'A',
2021
+ '\u01FA': 'A',
2022
+ '\u01CD': 'A',
2023
+ '\u0200': 'A',
2024
+ '\u0202': 'A',
2025
+ '\u1EA0': 'A',
2026
+ '\u1EAC': 'A',
2027
+ '\u1EB6': 'A',
2028
+ '\u1E00': 'A',
2029
+ '\u0104': 'A',
2030
+ '\u023A': 'A',
2031
+ '\u2C6F': 'A',
2032
+ '\uA732': 'AA',
2033
+ '\u00C6': 'AE',
2034
+ '\u01FC': 'AE',
2035
+ '\u01E2': 'AE',
2036
+ '\uA734': 'AO',
2037
+ '\uA736': 'AU',
2038
+ '\uA738': 'AV',
2039
+ '\uA73A': 'AV',
2040
+ '\uA73C': 'AY',
2041
+ '\u24B7': 'B',
2042
+ '\uFF22': 'B',
2043
+ '\u1E02': 'B',
2044
+ '\u1E04': 'B',
2045
+ '\u1E06': 'B',
2046
+ '\u0243': 'B',
2047
+ '\u0182': 'B',
2048
+ '\u0181': 'B',
2049
+ '\u24B8': 'C',
2050
+ '\uFF23': 'C',
2051
+ '\u0106': 'C',
2052
+ '\u0108': 'C',
2053
+ '\u010A': 'C',
2054
+ '\u010C': 'C',
2055
+ '\u00C7': 'C',
2056
+ '\u1E08': 'C',
2057
+ '\u0187': 'C',
2058
+ '\u023B': 'C',
2059
+ '\uA73E': 'C',
2060
+ '\u24B9': 'D',
2061
+ '\uFF24': 'D',
2062
+ '\u1E0A': 'D',
2063
+ '\u010E': 'D',
2064
+ '\u1E0C': 'D',
2065
+ '\u1E10': 'D',
2066
+ '\u1E12': 'D',
2067
+ '\u1E0E': 'D',
2068
+ '\u0110': 'D',
2069
+ '\u018B': 'D',
2070
+ '\u018A': 'D',
2071
+ '\u0189': 'D',
2072
+ '\uA779': 'D',
2073
+ '\u01F1': 'DZ',
2074
+ '\u01C4': 'DZ',
2075
+ '\u01F2': 'Dz',
2076
+ '\u01C5': 'Dz',
2077
+ '\u24BA': 'E',
2078
+ '\uFF25': 'E',
2079
+ '\u00C8': 'E',
2080
+ '\u00C9': 'E',
2081
+ '\u00CA': 'E',
2082
+ '\u1EC0': 'E',
2083
+ '\u1EBE': 'E',
2084
+ '\u1EC4': 'E',
2085
+ '\u1EC2': 'E',
2086
+ '\u1EBC': 'E',
2087
+ '\u0112': 'E',
2088
+ '\u1E14': 'E',
2089
+ '\u1E16': 'E',
2090
+ '\u0114': 'E',
2091
+ '\u0116': 'E',
2092
+ '\u00CB': 'E',
2093
+ '\u1EBA': 'E',
2094
+ '\u011A': 'E',
2095
+ '\u0204': 'E',
2096
+ '\u0206': 'E',
2097
+ '\u1EB8': 'E',
2098
+ '\u1EC6': 'E',
2099
+ '\u0228': 'E',
2100
+ '\u1E1C': 'E',
2101
+ '\u0118': 'E',
2102
+ '\u1E18': 'E',
2103
+ '\u1E1A': 'E',
2104
+ '\u0190': 'E',
2105
+ '\u018E': 'E',
2106
+ '\u24BB': 'F',
2107
+ '\uFF26': 'F',
2108
+ '\u1E1E': 'F',
2109
+ '\u0191': 'F',
2110
+ '\uA77B': 'F',
2111
+ '\u24BC': 'G',
2112
+ '\uFF27': 'G',
2113
+ '\u01F4': 'G',
2114
+ '\u011C': 'G',
2115
+ '\u1E20': 'G',
2116
+ '\u011E': 'G',
2117
+ '\u0120': 'G',
2118
+ '\u01E6': 'G',
2119
+ '\u0122': 'G',
2120
+ '\u01E4': 'G',
2121
+ '\u0193': 'G',
2122
+ '\uA7A0': 'G',
2123
+ '\uA77D': 'G',
2124
+ '\uA77E': 'G',
2125
+ '\u24BD': 'H',
2126
+ '\uFF28': 'H',
2127
+ '\u0124': 'H',
2128
+ '\u1E22': 'H',
2129
+ '\u1E26': 'H',
2130
+ '\u021E': 'H',
2131
+ '\u1E24': 'H',
2132
+ '\u1E28': 'H',
2133
+ '\u1E2A': 'H',
2134
+ '\u0126': 'H',
2135
+ '\u2C67': 'H',
2136
+ '\u2C75': 'H',
2137
+ '\uA78D': 'H',
2138
+ '\u24BE': 'I',
2139
+ '\uFF29': 'I',
2140
+ '\u00CC': 'I',
2141
+ '\u00CD': 'I',
2142
+ '\u00CE': 'I',
2143
+ '\u0128': 'I',
2144
+ '\u012A': 'I',
2145
+ '\u012C': 'I',
2146
+ '\u0130': 'I',
2147
+ '\u00CF': 'I',
2148
+ '\u1E2E': 'I',
2149
+ '\u1EC8': 'I',
2150
+ '\u01CF': 'I',
2151
+ '\u0208': 'I',
2152
+ '\u020A': 'I',
2153
+ '\u1ECA': 'I',
2154
+ '\u012E': 'I',
2155
+ '\u1E2C': 'I',
2156
+ '\u0197': 'I',
2157
+ '\u24BF': 'J',
2158
+ '\uFF2A': 'J',
2159
+ '\u0134': 'J',
2160
+ '\u0248': 'J',
2161
+ '\u24C0': 'K',
2162
+ '\uFF2B': 'K',
2163
+ '\u1E30': 'K',
2164
+ '\u01E8': 'K',
2165
+ '\u1E32': 'K',
2166
+ '\u0136': 'K',
2167
+ '\u1E34': 'K',
2168
+ '\u0198': 'K',
2169
+ '\u2C69': 'K',
2170
+ '\uA740': 'K',
2171
+ '\uA742': 'K',
2172
+ '\uA744': 'K',
2173
+ '\uA7A2': 'K',
2174
+ '\u24C1': 'L',
2175
+ '\uFF2C': 'L',
2176
+ '\u013F': 'L',
2177
+ '\u0139': 'L',
2178
+ '\u013D': 'L',
2179
+ '\u1E36': 'L',
2180
+ '\u1E38': 'L',
2181
+ '\u013B': 'L',
2182
+ '\u1E3C': 'L',
2183
+ '\u1E3A': 'L',
2184
+ '\u0141': 'L',
2185
+ '\u023D': 'L',
2186
+ '\u2C62': 'L',
2187
+ '\u2C60': 'L',
2188
+ '\uA748': 'L',
2189
+ '\uA746': 'L',
2190
+ '\uA780': 'L',
2191
+ '\u01C7': 'LJ',
2192
+ '\u01C8': 'Lj',
2193
+ '\u24C2': 'M',
2194
+ '\uFF2D': 'M',
2195
+ '\u1E3E': 'M',
2196
+ '\u1E40': 'M',
2197
+ '\u1E42': 'M',
2198
+ '\u2C6E': 'M',
2199
+ '\u019C': 'M',
2200
+ '\u24C3': 'N',
2201
+ '\uFF2E': 'N',
2202
+ '\u01F8': 'N',
2203
+ '\u0143': 'N',
2204
+ '\u00D1': 'N',
2205
+ '\u1E44': 'N',
2206
+ '\u0147': 'N',
2207
+ '\u1E46': 'N',
2208
+ '\u0145': 'N',
2209
+ '\u1E4A': 'N',
2210
+ '\u1E48': 'N',
2211
+ '\u0220': 'N',
2212
+ '\u019D': 'N',
2213
+ '\uA790': 'N',
2214
+ '\uA7A4': 'N',
2215
+ '\u01CA': 'NJ',
2216
+ '\u01CB': 'Nj',
2217
+ '\u24C4': 'O',
2218
+ '\uFF2F': 'O',
2219
+ '\u00D2': 'O',
2220
+ '\u00D3': 'O',
2221
+ '\u00D4': 'O',
2222
+ '\u1ED2': 'O',
2223
+ '\u1ED0': 'O',
2224
+ '\u1ED6': 'O',
2225
+ '\u1ED4': 'O',
2226
+ '\u00D5': 'O',
2227
+ '\u1E4C': 'O',
2228
+ '\u022C': 'O',
2229
+ '\u1E4E': 'O',
2230
+ '\u014C': 'O',
2231
+ '\u1E50': 'O',
2232
+ '\u1E52': 'O',
2233
+ '\u014E': 'O',
2234
+ '\u022E': 'O',
2235
+ '\u0230': 'O',
2236
+ '\u00D6': 'O',
2237
+ '\u022A': 'O',
2238
+ '\u1ECE': 'O',
2239
+ '\u0150': 'O',
2240
+ '\u01D1': 'O',
2241
+ '\u020C': 'O',
2242
+ '\u020E': 'O',
2243
+ '\u01A0': 'O',
2244
+ '\u1EDC': 'O',
2245
+ '\u1EDA': 'O',
2246
+ '\u1EE0': 'O',
2247
+ '\u1EDE': 'O',
2248
+ '\u1EE2': 'O',
2249
+ '\u1ECC': 'O',
2250
+ '\u1ED8': 'O',
2251
+ '\u01EA': 'O',
2252
+ '\u01EC': 'O',
2253
+ '\u00D8': 'O',
2254
+ '\u01FE': 'O',
2255
+ '\u0186': 'O',
2256
+ '\u019F': 'O',
2257
+ '\uA74A': 'O',
2258
+ '\uA74C': 'O',
2259
+ '\u01A2': 'OI',
2260
+ '\uA74E': 'OO',
2261
+ '\u0222': 'OU',
2262
+ '\u24C5': 'P',
2263
+ '\uFF30': 'P',
2264
+ '\u1E54': 'P',
2265
+ '\u1E56': 'P',
2266
+ '\u01A4': 'P',
2267
+ '\u2C63': 'P',
2268
+ '\uA750': 'P',
2269
+ '\uA752': 'P',
2270
+ '\uA754': 'P',
2271
+ '\u24C6': 'Q',
2272
+ '\uFF31': 'Q',
2273
+ '\uA756': 'Q',
2274
+ '\uA758': 'Q',
2275
+ '\u024A': 'Q',
2276
+ '\u24C7': 'R',
2277
+ '\uFF32': 'R',
2278
+ '\u0154': 'R',
2279
+ '\u1E58': 'R',
2280
+ '\u0158': 'R',
2281
+ '\u0210': 'R',
2282
+ '\u0212': 'R',
2283
+ '\u1E5A': 'R',
2284
+ '\u1E5C': 'R',
2285
+ '\u0156': 'R',
2286
+ '\u1E5E': 'R',
2287
+ '\u024C': 'R',
2288
+ '\u2C64': 'R',
2289
+ '\uA75A': 'R',
2290
+ '\uA7A6': 'R',
2291
+ '\uA782': 'R',
2292
+ '\u24C8': 'S',
2293
+ '\uFF33': 'S',
2294
+ '\u1E9E': 'S',
2295
+ '\u015A': 'S',
2296
+ '\u1E64': 'S',
2297
+ '\u015C': 'S',
2298
+ '\u1E60': 'S',
2299
+ '\u0160': 'S',
2300
+ '\u1E66': 'S',
2301
+ '\u1E62': 'S',
2302
+ '\u1E68': 'S',
2303
+ '\u0218': 'S',
2304
+ '\u015E': 'S',
2305
+ '\u2C7E': 'S',
2306
+ '\uA7A8': 'S',
2307
+ '\uA784': 'S',
2308
+ '\u24C9': 'T',
2309
+ '\uFF34': 'T',
2310
+ '\u1E6A': 'T',
2311
+ '\u0164': 'T',
2312
+ '\u1E6C': 'T',
2313
+ '\u021A': 'T',
2314
+ '\u0162': 'T',
2315
+ '\u1E70': 'T',
2316
+ '\u1E6E': 'T',
2317
+ '\u0166': 'T',
2318
+ '\u01AC': 'T',
2319
+ '\u01AE': 'T',
2320
+ '\u023E': 'T',
2321
+ '\uA786': 'T',
2322
+ '\uA728': 'TZ',
2323
+ '\u24CA': 'U',
2324
+ '\uFF35': 'U',
2325
+ '\u00D9': 'U',
2326
+ '\u00DA': 'U',
2327
+ '\u00DB': 'U',
2328
+ '\u0168': 'U',
2329
+ '\u1E78': 'U',
2330
+ '\u016A': 'U',
2331
+ '\u1E7A': 'U',
2332
+ '\u016C': 'U',
2333
+ '\u00DC': 'U',
2334
+ '\u01DB': 'U',
2335
+ '\u01D7': 'U',
2336
+ '\u01D5': 'U',
2337
+ '\u01D9': 'U',
2338
+ '\u1EE6': 'U',
2339
+ '\u016E': 'U',
2340
+ '\u0170': 'U',
2341
+ '\u01D3': 'U',
2342
+ '\u0214': 'U',
2343
+ '\u0216': 'U',
2344
+ '\u01AF': 'U',
2345
+ '\u1EEA': 'U',
2346
+ '\u1EE8': 'U',
2347
+ '\u1EEE': 'U',
2348
+ '\u1EEC': 'U',
2349
+ '\u1EF0': 'U',
2350
+ '\u1EE4': 'U',
2351
+ '\u1E72': 'U',
2352
+ '\u0172': 'U',
2353
+ '\u1E76': 'U',
2354
+ '\u1E74': 'U',
2355
+ '\u0244': 'U',
2356
+ '\u24CB': 'V',
2357
+ '\uFF36': 'V',
2358
+ '\u1E7C': 'V',
2359
+ '\u1E7E': 'V',
2360
+ '\u01B2': 'V',
2361
+ '\uA75E': 'V',
2362
+ '\u0245': 'V',
2363
+ '\uA760': 'VY',
2364
+ '\u24CC': 'W',
2365
+ '\uFF37': 'W',
2366
+ '\u1E80': 'W',
2367
+ '\u1E82': 'W',
2368
+ '\u0174': 'W',
2369
+ '\u1E86': 'W',
2370
+ '\u1E84': 'W',
2371
+ '\u1E88': 'W',
2372
+ '\u2C72': 'W',
2373
+ '\u24CD': 'X',
2374
+ '\uFF38': 'X',
2375
+ '\u1E8A': 'X',
2376
+ '\u1E8C': 'X',
2377
+ '\u24CE': 'Y',
2378
+ '\uFF39': 'Y',
2379
+ '\u1EF2': 'Y',
2380
+ '\u00DD': 'Y',
2381
+ '\u0176': 'Y',
2382
+ '\u1EF8': 'Y',
2383
+ '\u0232': 'Y',
2384
+ '\u1E8E': 'Y',
2385
+ '\u0178': 'Y',
2386
+ '\u1EF6': 'Y',
2387
+ '\u1EF4': 'Y',
2388
+ '\u01B3': 'Y',
2389
+ '\u024E': 'Y',
2390
+ '\u1EFE': 'Y',
2391
+ '\u24CF': 'Z',
2392
+ '\uFF3A': 'Z',
2393
+ '\u0179': 'Z',
2394
+ '\u1E90': 'Z',
2395
+ '\u017B': 'Z',
2396
+ '\u017D': 'Z',
2397
+ '\u1E92': 'Z',
2398
+ '\u1E94': 'Z',
2399
+ '\u01B5': 'Z',
2400
+ '\u0224': 'Z',
2401
+ '\u2C7F': 'Z',
2402
+ '\u2C6B': 'Z',
2403
+ '\uA762': 'Z',
2404
+ '\u24D0': 'a',
2405
+ '\uFF41': 'a',
2406
+ '\u1E9A': 'a',
2407
+ '\u00E0': 'a',
2408
+ '\u00E1': 'a',
2409
+ '\u00E2': 'a',
2410
+ '\u1EA7': 'a',
2411
+ '\u1EA5': 'a',
2412
+ '\u1EAB': 'a',
2413
+ '\u1EA9': 'a',
2414
+ '\u00E3': 'a',
2415
+ '\u0101': 'a',
2416
+ '\u0103': 'a',
2417
+ '\u1EB1': 'a',
2418
+ '\u1EAF': 'a',
2419
+ '\u1EB5': 'a',
2420
+ '\u1EB3': 'a',
2421
+ '\u0227': 'a',
2422
+ '\u01E1': 'a',
2423
+ '\u00E4': 'a',
2424
+ '\u01DF': 'a',
2425
+ '\u1EA3': 'a',
2426
+ '\u00E5': 'a',
2427
+ '\u01FB': 'a',
2428
+ '\u01CE': 'a',
2429
+ '\u0201': 'a',
2430
+ '\u0203': 'a',
2431
+ '\u1EA1': 'a',
2432
+ '\u1EAD': 'a',
2433
+ '\u1EB7': 'a',
2434
+ '\u1E01': 'a',
2435
+ '\u0105': 'a',
2436
+ '\u2C65': 'a',
2437
+ '\u0250': 'a',
2438
+ '\uA733': 'aa',
2439
+ '\u00E6': 'ae',
2440
+ '\u01FD': 'ae',
2441
+ '\u01E3': 'ae',
2442
+ '\uA735': 'ao',
2443
+ '\uA737': 'au',
2444
+ '\uA739': 'av',
2445
+ '\uA73B': 'av',
2446
+ '\uA73D': 'ay',
2447
+ '\u24D1': 'b',
2448
+ '\uFF42': 'b',
2449
+ '\u1E03': 'b',
2450
+ '\u1E05': 'b',
2451
+ '\u1E07': 'b',
2452
+ '\u0180': 'b',
2453
+ '\u0183': 'b',
2454
+ '\u0253': 'b',
2455
+ '\u24D2': 'c',
2456
+ '\uFF43': 'c',
2457
+ '\u0107': 'c',
2458
+ '\u0109': 'c',
2459
+ '\u010B': 'c',
2460
+ '\u010D': 'c',
2461
+ '\u00E7': 'c',
2462
+ '\u1E09': 'c',
2463
+ '\u0188': 'c',
2464
+ '\u023C': 'c',
2465
+ '\uA73F': 'c',
2466
+ '\u2184': 'c',
2467
+ '\u24D3': 'd',
2468
+ '\uFF44': 'd',
2469
+ '\u1E0B': 'd',
2470
+ '\u010F': 'd',
2471
+ '\u1E0D': 'd',
2472
+ '\u1E11': 'd',
2473
+ '\u1E13': 'd',
2474
+ '\u1E0F': 'd',
2475
+ '\u0111': 'd',
2476
+ '\u018C': 'd',
2477
+ '\u0256': 'd',
2478
+ '\u0257': 'd',
2479
+ '\uA77A': 'd',
2480
+ '\u01F3': 'dz',
2481
+ '\u01C6': 'dz',
2482
+ '\u24D4': 'e',
2483
+ '\uFF45': 'e',
2484
+ '\u00E8': 'e',
2485
+ '\u00E9': 'e',
2486
+ '\u00EA': 'e',
2487
+ '\u1EC1': 'e',
2488
+ '\u1EBF': 'e',
2489
+ '\u1EC5': 'e',
2490
+ '\u1EC3': 'e',
2491
+ '\u1EBD': 'e',
2492
+ '\u0113': 'e',
2493
+ '\u1E15': 'e',
2494
+ '\u1E17': 'e',
2495
+ '\u0115': 'e',
2496
+ '\u0117': 'e',
2497
+ '\u00EB': 'e',
2498
+ '\u1EBB': 'e',
2499
+ '\u011B': 'e',
2500
+ '\u0205': 'e',
2501
+ '\u0207': 'e',
2502
+ '\u1EB9': 'e',
2503
+ '\u1EC7': 'e',
2504
+ '\u0229': 'e',
2505
+ '\u1E1D': 'e',
2506
+ '\u0119': 'e',
2507
+ '\u1E19': 'e',
2508
+ '\u1E1B': 'e',
2509
+ '\u0247': 'e',
2510
+ '\u025B': 'e',
2511
+ '\u01DD': 'e',
2512
+ '\u24D5': 'f',
2513
+ '\uFF46': 'f',
2514
+ '\u1E1F': 'f',
2515
+ '\u0192': 'f',
2516
+ '\uA77C': 'f',
2517
+ '\u24D6': 'g',
2518
+ '\uFF47': 'g',
2519
+ '\u01F5': 'g',
2520
+ '\u011D': 'g',
2521
+ '\u1E21': 'g',
2522
+ '\u011F': 'g',
2523
+ '\u0121': 'g',
2524
+ '\u01E7': 'g',
2525
+ '\u0123': 'g',
2526
+ '\u01E5': 'g',
2527
+ '\u0260': 'g',
2528
+ '\uA7A1': 'g',
2529
+ '\u1D79': 'g',
2530
+ '\uA77F': 'g',
2531
+ '\u24D7': 'h',
2532
+ '\uFF48': 'h',
2533
+ '\u0125': 'h',
2534
+ '\u1E23': 'h',
2535
+ '\u1E27': 'h',
2536
+ '\u021F': 'h',
2537
+ '\u1E25': 'h',
2538
+ '\u1E29': 'h',
2539
+ '\u1E2B': 'h',
2540
+ '\u1E96': 'h',
2541
+ '\u0127': 'h',
2542
+ '\u2C68': 'h',
2543
+ '\u2C76': 'h',
2544
+ '\u0265': 'h',
2545
+ '\u0195': 'hv',
2546
+ '\u24D8': 'i',
2547
+ '\uFF49': 'i',
2548
+ '\u00EC': 'i',
2549
+ '\u00ED': 'i',
2550
+ '\u00EE': 'i',
2551
+ '\u0129': 'i',
2552
+ '\u012B': 'i',
2553
+ '\u012D': 'i',
2554
+ '\u00EF': 'i',
2555
+ '\u1E2F': 'i',
2556
+ '\u1EC9': 'i',
2557
+ '\u01D0': 'i',
2558
+ '\u0209': 'i',
2559
+ '\u020B': 'i',
2560
+ '\u1ECB': 'i',
2561
+ '\u012F': 'i',
2562
+ '\u1E2D': 'i',
2563
+ '\u0268': 'i',
2564
+ '\u0131': 'i',
2565
+ '\u24D9': 'j',
2566
+ '\uFF4A': 'j',
2567
+ '\u0135': 'j',
2568
+ '\u01F0': 'j',
2569
+ '\u0249': 'j',
2570
+ '\u24DA': 'k',
2571
+ '\uFF4B': 'k',
2572
+ '\u1E31': 'k',
2573
+ '\u01E9': 'k',
2574
+ '\u1E33': 'k',
2575
+ '\u0137': 'k',
2576
+ '\u1E35': 'k',
2577
+ '\u0199': 'k',
2578
+ '\u2C6A': 'k',
2579
+ '\uA741': 'k',
2580
+ '\uA743': 'k',
2581
+ '\uA745': 'k',
2582
+ '\uA7A3': 'k',
2583
+ '\u24DB': 'l',
2584
+ '\uFF4C': 'l',
2585
+ '\u0140': 'l',
2586
+ '\u013A': 'l',
2587
+ '\u013E': 'l',
2588
+ '\u1E37': 'l',
2589
+ '\u1E39': 'l',
2590
+ '\u013C': 'l',
2591
+ '\u1E3D': 'l',
2592
+ '\u1E3B': 'l',
2593
+ '\u017F': 'l',
2594
+ '\u0142': 'l',
2595
+ '\u019A': 'l',
2596
+ '\u026B': 'l',
2597
+ '\u2C61': 'l',
2598
+ '\uA749': 'l',
2599
+ '\uA781': 'l',
2600
+ '\uA747': 'l',
2601
+ '\u01C9': 'lj',
2602
+ '\u24DC': 'm',
2603
+ '\uFF4D': 'm',
2604
+ '\u1E3F': 'm',
2605
+ '\u1E41': 'm',
2606
+ '\u1E43': 'm',
2607
+ '\u0271': 'm',
2608
+ '\u026F': 'm',
2609
+ '\u24DD': 'n',
2610
+ '\uFF4E': 'n',
2611
+ '\u01F9': 'n',
2612
+ '\u0144': 'n',
2613
+ '\u00F1': 'n',
2614
+ '\u1E45': 'n',
2615
+ '\u0148': 'n',
2616
+ '\u1E47': 'n',
2617
+ '\u0146': 'n',
2618
+ '\u1E4B': 'n',
2619
+ '\u1E49': 'n',
2620
+ '\u019E': 'n',
2621
+ '\u0272': 'n',
2622
+ '\u0149': 'n',
2623
+ '\uA791': 'n',
2624
+ '\uA7A5': 'n',
2625
+ '\u01CC': 'nj',
2626
+ '\u24DE': 'o',
2627
+ '\uFF4F': 'o',
2628
+ '\u00F2': 'o',
2629
+ '\u00F3': 'o',
2630
+ '\u00F4': 'o',
2631
+ '\u1ED3': 'o',
2632
+ '\u1ED1': 'o',
2633
+ '\u1ED7': 'o',
2634
+ '\u1ED5': 'o',
2635
+ '\u00F5': 'o',
2636
+ '\u1E4D': 'o',
2637
+ '\u022D': 'o',
2638
+ '\u1E4F': 'o',
2639
+ '\u014D': 'o',
2640
+ '\u1E51': 'o',
2641
+ '\u1E53': 'o',
2642
+ '\u014F': 'o',
2643
+ '\u022F': 'o',
2644
+ '\u0231': 'o',
2645
+ '\u00F6': 'o',
2646
+ '\u022B': 'o',
2647
+ '\u1ECF': 'o',
2648
+ '\u0151': 'o',
2649
+ '\u01D2': 'o',
2650
+ '\u020D': 'o',
2651
+ '\u020F': 'o',
2652
+ '\u01A1': 'o',
2653
+ '\u1EDD': 'o',
2654
+ '\u1EDB': 'o',
2655
+ '\u1EE1': 'o',
2656
+ '\u1EDF': 'o',
2657
+ '\u1EE3': 'o',
2658
+ '\u1ECD': 'o',
2659
+ '\u1ED9': 'o',
2660
+ '\u01EB': 'o',
2661
+ '\u01ED': 'o',
2662
+ '\u00F8': 'o',
2663
+ '\u01FF': 'o',
2664
+ '\u0254': 'o',
2665
+ '\uA74B': 'o',
2666
+ '\uA74D': 'o',
2667
+ '\u0275': 'o',
2668
+ '\u01A3': 'oi',
2669
+ '\u0223': 'ou',
2670
+ '\uA74F': 'oo',
2671
+ '\u24DF': 'p',
2672
+ '\uFF50': 'p',
2673
+ '\u1E55': 'p',
2674
+ '\u1E57': 'p',
2675
+ '\u01A5': 'p',
2676
+ '\u1D7D': 'p',
2677
+ '\uA751': 'p',
2678
+ '\uA753': 'p',
2679
+ '\uA755': 'p',
2680
+ '\u24E0': 'q',
2681
+ '\uFF51': 'q',
2682
+ '\u024B': 'q',
2683
+ '\uA757': 'q',
2684
+ '\uA759': 'q',
2685
+ '\u24E1': 'r',
2686
+ '\uFF52': 'r',
2687
+ '\u0155': 'r',
2688
+ '\u1E59': 'r',
2689
+ '\u0159': 'r',
2690
+ '\u0211': 'r',
2691
+ '\u0213': 'r',
2692
+ '\u1E5B': 'r',
2693
+ '\u1E5D': 'r',
2694
+ '\u0157': 'r',
2695
+ '\u1E5F': 'r',
2696
+ '\u024D': 'r',
2697
+ '\u027D': 'r',
2698
+ '\uA75B': 'r',
2699
+ '\uA7A7': 'r',
2700
+ '\uA783': 'r',
2701
+ '\u24E2': 's',
2702
+ '\uFF53': 's',
2703
+ '\u00DF': 's',
2704
+ '\u015B': 's',
2705
+ '\u1E65': 's',
2706
+ '\u015D': 's',
2707
+ '\u1E61': 's',
2708
+ '\u0161': 's',
2709
+ '\u1E67': 's',
2710
+ '\u1E63': 's',
2711
+ '\u1E69': 's',
2712
+ '\u0219': 's',
2713
+ '\u015F': 's',
2714
+ '\u023F': 's',
2715
+ '\uA7A9': 's',
2716
+ '\uA785': 's',
2717
+ '\u1E9B': 's',
2718
+ '\u24E3': 't',
2719
+ '\uFF54': 't',
2720
+ '\u1E6B': 't',
2721
+ '\u1E97': 't',
2722
+ '\u0165': 't',
2723
+ '\u1E6D': 't',
2724
+ '\u021B': 't',
2725
+ '\u0163': 't',
2726
+ '\u1E71': 't',
2727
+ '\u1E6F': 't',
2728
+ '\u0167': 't',
2729
+ '\u01AD': 't',
2730
+ '\u0288': 't',
2731
+ '\u2C66': 't',
2732
+ '\uA787': 't',
2733
+ '\uA729': 'tz',
2734
+ '\u24E4': 'u',
2735
+ '\uFF55': 'u',
2736
+ '\u00F9': 'u',
2737
+ '\u00FA': 'u',
2738
+ '\u00FB': 'u',
2739
+ '\u0169': 'u',
2740
+ '\u1E79': 'u',
2741
+ '\u016B': 'u',
2742
+ '\u1E7B': 'u',
2743
+ '\u016D': 'u',
2744
+ '\u00FC': 'u',
2745
+ '\u01DC': 'u',
2746
+ '\u01D8': 'u',
2747
+ '\u01D6': 'u',
2748
+ '\u01DA': 'u',
2749
+ '\u1EE7': 'u',
2750
+ '\u016F': 'u',
2751
+ '\u0171': 'u',
2752
+ '\u01D4': 'u',
2753
+ '\u0215': 'u',
2754
+ '\u0217': 'u',
2755
+ '\u01B0': 'u',
2756
+ '\u1EEB': 'u',
2757
+ '\u1EE9': 'u',
2758
+ '\u1EEF': 'u',
2759
+ '\u1EED': 'u',
2760
+ '\u1EF1': 'u',
2761
+ '\u1EE5': 'u',
2762
+ '\u1E73': 'u',
2763
+ '\u0173': 'u',
2764
+ '\u1E77': 'u',
2765
+ '\u1E75': 'u',
2766
+ '\u0289': 'u',
2767
+ '\u24E5': 'v',
2768
+ '\uFF56': 'v',
2769
+ '\u1E7D': 'v',
2770
+ '\u1E7F': 'v',
2771
+ '\u028B': 'v',
2772
+ '\uA75F': 'v',
2773
+ '\u028C': 'v',
2774
+ '\uA761': 'vy',
2775
+ '\u24E6': 'w',
2776
+ '\uFF57': 'w',
2777
+ '\u1E81': 'w',
2778
+ '\u1E83': 'w',
2779
+ '\u0175': 'w',
2780
+ '\u1E87': 'w',
2781
+ '\u1E85': 'w',
2782
+ '\u1E98': 'w',
2783
+ '\u1E89': 'w',
2784
+ '\u2C73': 'w',
2785
+ '\u24E7': 'x',
2786
+ '\uFF58': 'x',
2787
+ '\u1E8B': 'x',
2788
+ '\u1E8D': 'x',
2789
+ '\u24E8': 'y',
2790
+ '\uFF59': 'y',
2791
+ '\u1EF3': 'y',
2792
+ '\u00FD': 'y',
2793
+ '\u0177': 'y',
2794
+ '\u1EF9': 'y',
2795
+ '\u0233': 'y',
2796
+ '\u1E8F': 'y',
2797
+ '\u00FF': 'y',
2798
+ '\u1EF7': 'y',
2799
+ '\u1E99': 'y',
2800
+ '\u1EF5': 'y',
2801
+ '\u01B4': 'y',
2802
+ '\u024F': 'y',
2803
+ '\u1EFF': 'y',
2804
+ '\u24E9': 'z',
2805
+ '\uFF5A': 'z',
2806
+ '\u017A': 'z',
2807
+ '\u1E91': 'z',
2808
+ '\u017C': 'z',
2809
+ '\u017E': 'z',
2810
+ '\u1E93': 'z',
2811
+ '\u1E95': 'z',
2812
+ '\u01B6': 'z',
2813
+ '\u0225': 'z',
2814
+ '\u0240': 'z',
2815
+ '\u2C6C': 'z',
2816
+ '\uA763': 'z',
2817
+ '\u0386': '\u0391',
2818
+ '\u0388': '\u0395',
2819
+ '\u0389': '\u0397',
2820
+ '\u038A': '\u0399',
2821
+ '\u03AA': '\u0399',
2822
+ '\u038C': '\u039F',
2823
+ '\u038E': '\u03A5',
2824
+ '\u03AB': '\u03A5',
2825
+ '\u038F': '\u03A9',
2826
+ '\u03AC': '\u03B1',
2827
+ '\u03AD': '\u03B5',
2828
+ '\u03AE': '\u03B7',
2829
+ '\u03AF': '\u03B9',
2830
+ '\u03CA': '\u03B9',
2831
+ '\u0390': '\u03B9',
2832
+ '\u03CC': '\u03BF',
2833
+ '\u03CD': '\u03C5',
2834
+ '\u03CB': '\u03C5',
2835
+ '\u03B0': '\u03C5',
2836
+ '\u03C9': '\u03C9',
2837
+ '\u03C2': '\u03C3'
2838
+ };
2839
+
2840
+ return diacritics;
2841
+ });
2842
+
2843
+ S2.define('select2/data/base',[
2844
+ '../utils'
2845
+ ], function (Utils) {
2846
+ function BaseAdapter ($element, options) {
2847
+ BaseAdapter.__super__.constructor.call(this);
2848
+ }
2849
+
2850
+ Utils.Extend(BaseAdapter, Utils.Observable);
2851
+
2852
+ BaseAdapter.prototype.current = function (callback) {
2853
+ throw new Error('The `current` method must be defined in child classes.');
2854
+ };
2855
+
2856
+ BaseAdapter.prototype.query = function (params, callback) {
2857
+ throw new Error('The `query` method must be defined in child classes.');
2858
+ };
2859
+
2860
+ BaseAdapter.prototype.bind = function (container, $container) {
2861
+ // Can be implemented in subclasses
2862
+ };
2863
+
2864
+ BaseAdapter.prototype.destroy = function () {
2865
+ // Can be implemented in subclasses
2866
+ };
2867
+
2868
+ BaseAdapter.prototype.generateResultId = function (container, data) {
2869
+ var id = container.id + '-result-';
2870
+
2871
+ id += Utils.generateChars(4);
2872
+
2873
+ if (data.id != null) {
2874
+ id += '-' + data.id.toString();
2875
+ } else {
2876
+ id += '-' + Utils.generateChars(4);
2877
+ }
2878
+ return id;
2879
+ };
2880
+
2881
+ return BaseAdapter;
2882
+ });
2883
+
2884
+ S2.define('select2/data/select',[
2885
+ './base',
2886
+ '../utils',
2887
+ 'jquery'
2888
+ ], function (BaseAdapter, Utils, $) {
2889
+ function SelectAdapter ($element, options) {
2890
+ this.$element = $element;
2891
+ this.options = options;
2892
+
2893
+ SelectAdapter.__super__.constructor.call(this);
2894
+ }
2895
+
2896
+ Utils.Extend(SelectAdapter, BaseAdapter);
2897
+
2898
+ SelectAdapter.prototype.current = function (callback) {
2899
+ var data = [];
2900
+ var self = this;
2901
+
2902
+ this.$element.find(':selected').each(function () {
2903
+ var $option = $(this);
2904
+
2905
+ var option = self.item($option);
2906
+
2907
+ data.push(option);
2908
+ });
2909
+
2910
+ callback(data);
2911
+ };
2912
+
2913
+ SelectAdapter.prototype.select = function (data) {
2914
+ var self = this;
2915
+
2916
+ data.selected = true;
2917
+
2918
+ // If data.element is a DOM node, use it instead
2919
+ if ($(data.element).is('option')) {
2920
+ data.element.selected = true;
2921
+
2922
+ this.$element.trigger('change');
2923
+
2924
+ return;
2925
+ }
2926
+
2927
+ if (this.$element.prop('multiple')) {
2928
+ this.current(function (currentData) {
2929
+ var val = [];
2930
+
2931
+ data = [data];
2932
+ data.push.apply(data, currentData);
2933
+
2934
+ for (var d = 0; d < data.length; d++) {
2935
+ var id = data[d].id;
2936
+
2937
+ if ($.inArray(id, val) === -1) {
2938
+ val.push(id);
2939
+ }
2940
+ }
2941
+
2942
+ self.$element.val(val);
2943
+ self.$element.trigger('change');
2944
+ });
2945
+ } else {
2946
+ var val = data.id;
2947
+
2948
+ this.$element.val(val);
2949
+ this.$element.trigger('change');
2950
+ }
2951
+ };
2952
+
2953
+ SelectAdapter.prototype.unselect = function (data) {
2954
+ var self = this;
2955
+
2956
+ if (!this.$element.prop('multiple')) {
2957
+ return;
2958
+ }
2959
+
2960
+ data.selected = false;
2961
+
2962
+ if ($(data.element).is('option')) {
2963
+ data.element.selected = false;
2964
+
2965
+ this.$element.trigger('change');
2966
+
2967
+ return;
2968
+ }
2969
+
2970
+ this.current(function (currentData) {
2971
+ var val = [];
2972
+
2973
+ for (var d = 0; d < currentData.length; d++) {
2974
+ var id = currentData[d].id;
2975
+
2976
+ if (id !== data.id && $.inArray(id, val) === -1) {
2977
+ val.push(id);
2978
+ }
2979
+ }
2980
+
2981
+ self.$element.val(val);
2982
+
2983
+ self.$element.trigger('change');
2984
+ });
2985
+ };
2986
+
2987
+ SelectAdapter.prototype.bind = function (container, $container) {
2988
+ var self = this;
2989
+
2990
+ this.container = container;
2991
+
2992
+ container.on('select', function (params) {
2993
+ self.select(params.data);
2994
+ });
2995
+
2996
+ container.on('unselect', function (params) {
2997
+ self.unselect(params.data);
2998
+ });
2999
+ };
3000
+
3001
+ SelectAdapter.prototype.destroy = function () {
3002
+ // Remove anything added to child elements
3003
+ this.$element.find('*').each(function () {
3004
+ // Remove any custom data set by Select2
3005
+ $.removeData(this, 'data');
3006
+ });
3007
+ };
3008
+
3009
+ SelectAdapter.prototype.query = function (params, callback) {
3010
+ var data = [];
3011
+ var self = this;
3012
+
3013
+ var $options = this.$element.children();
3014
+
3015
+ $options.each(function () {
3016
+ var $option = $(this);
3017
+
3018
+ if (!$option.is('option') && !$option.is('optgroup')) {
3019
+ return;
3020
+ }
3021
+
3022
+ var option = self.item($option);
3023
+
3024
+ var matches = self.matches(params, option);
3025
+
3026
+ if (matches !== null) {
3027
+ data.push(matches);
3028
+ }
3029
+ });
3030
+
3031
+ callback({
3032
+ results: data
3033
+ });
3034
+ };
3035
+
3036
+ SelectAdapter.prototype.addOptions = function ($options) {
3037
+ Utils.appendMany(this.$element, $options);
3038
+ };
3039
+
3040
+ SelectAdapter.prototype.option = function (data) {
3041
+ var option;
3042
+
3043
+ if (data.children) {
3044
+ option = document.createElement('optgroup');
3045
+ option.label = data.text;
3046
+ } else {
3047
+ option = document.createElement('option');
3048
+
3049
+ if (option.textContent !== undefined) {
3050
+ option.textContent = data.text;
3051
+ } else {
3052
+ option.innerText = data.text;
3053
+ }
3054
+ }
3055
+
3056
+ if (data.id) {
3057
+ option.value = data.id;
3058
+ }
3059
+
3060
+ if (data.disabled) {
3061
+ option.disabled = true;
3062
+ }
3063
+
3064
+ if (data.selected) {
3065
+ option.selected = true;
3066
+ }
3067
+
3068
+ if (data.title) {
3069
+ option.title = data.title;
3070
+ }
3071
+
3072
+ var $option = $(option);
3073
+
3074
+ var normalizedData = this._normalizeItem(data);
3075
+ normalizedData.element = option;
3076
+
3077
+ // Override the option's data with the combined data
3078
+ $.data(option, 'data', normalizedData);
3079
+
3080
+ return $option;
3081
+ };
3082
+
3083
+ SelectAdapter.prototype.item = function ($option) {
3084
+ var data = {};
3085
+
3086
+ data = $.data($option[0], 'data');
3087
+
3088
+ if (data != null) {
3089
+ return data;
3090
+ }
3091
+
3092
+ if ($option.is('option')) {
3093
+ data = {
3094
+ id: $option.val(),
3095
+ text: $option.text(),
3096
+ disabled: $option.prop('disabled'),
3097
+ selected: $option.prop('selected'),
3098
+ title: $option.prop('title')
3099
+ };
3100
+ } else if ($option.is('optgroup')) {
3101
+ data = {
3102
+ text: $option.prop('label'),
3103
+ children: [],
3104
+ title: $option.prop('title')
3105
+ };
3106
+
3107
+ var $children = $option.children('option');
3108
+ var children = [];
3109
+
3110
+ for (var c = 0; c < $children.length; c++) {
3111
+ var $child = $($children[c]);
3112
+
3113
+ var child = this.item($child);
3114
+
3115
+ children.push(child);
3116
+ }
3117
+
3118
+ data.children = children;
3119
+ }
3120
+
3121
+ data = this._normalizeItem(data);
3122
+ data.element = $option[0];
3123
+
3124
+ $.data($option[0], 'data', data);
3125
+
3126
+ return data;
3127
+ };
3128
+
3129
+ SelectAdapter.prototype._normalizeItem = function (item) {
3130
+ if (!$.isPlainObject(item)) {
3131
+ item = {
3132
+ id: item,
3133
+ text: item
3134
+ };
3135
+ }
3136
+
3137
+ item = $.extend({}, {
3138
+ text: ''
3139
+ }, item);
3140
+
3141
+ var defaults = {
3142
+ selected: false,
3143
+ disabled: false
3144
+ };
3145
+
3146
+ if (item.id != null) {
3147
+ item.id = item.id.toString();
3148
+ }
3149
+
3150
+ if (item.text != null) {
3151
+ item.text = item.text.toString();
3152
+ }
3153
+
3154
+ if (item._resultId == null && item.id && this.container != null) {
3155
+ item._resultId = this.generateResultId(this.container, item);
3156
+ }
3157
+
3158
+ return $.extend({}, defaults, item);
3159
+ };
3160
+
3161
+ SelectAdapter.prototype.matches = function (params, data) {
3162
+ var matcher = this.options.get('matcher');
3163
+
3164
+ return matcher(params, data);
3165
+ };
3166
+
3167
+ return SelectAdapter;
3168
+ });
3169
+
3170
+ S2.define('select2/data/array',[
3171
+ './select',
3172
+ '../utils',
3173
+ 'jquery'
3174
+ ], function (SelectAdapter, Utils, $) {
3175
+ function ArrayAdapter ($element, options) {
3176
+ var data = options.get('data') || [];
3177
+
3178
+ ArrayAdapter.__super__.constructor.call(this, $element, options);
3179
+
3180
+ this.addOptions(this.convertToOptions(data));
3181
+ }
3182
+
3183
+ Utils.Extend(ArrayAdapter, SelectAdapter);
3184
+
3185
+ ArrayAdapter.prototype.select = function (data) {
3186
+ var $option = this.$element.find('option').filter(function (i, elm) {
3187
+ return elm.value == data.id.toString();
3188
+ });
3189
+
3190
+ if ($option.length === 0) {
3191
+ $option = this.option(data);
3192
+
3193
+ this.addOptions($option);
3194
+ }
3195
+
3196
+ ArrayAdapter.__super__.select.call(this, data);
3197
+ };
3198
+
3199
+ ArrayAdapter.prototype.convertToOptions = function (data) {
3200
+ var self = this;
3201
+
3202
+ var $existing = this.$element.find('option');
3203
+ var existingIds = $existing.map(function () {
3204
+ return self.item($(this)).id;
3205
+ }).get();
3206
+
3207
+ var $options = [];
3208
+
3209
+ // Filter out all items except for the one passed in the argument
3210
+ function onlyItem (item) {
3211
+ return function () {
3212
+ return $(this).val() == item.id;
3213
+ };
3214
+ }
3215
+
3216
+ for (var d = 0; d < data.length; d++) {
3217
+ var item = this._normalizeItem(data[d]);
3218
+
3219
+ // Skip items which were pre-loaded, only merge the data
3220
+ if ($.inArray(item.id, existingIds) >= 0) {
3221
+ var $existingOption = $existing.filter(onlyItem(item));
3222
+
3223
+ var existingData = this.item($existingOption);
3224
+ var newData = $.extend(true, {}, existingData, item);
3225
+
3226
+ var $newOption = this.option(existingData);
3227
+
3228
+ $existingOption.replaceWith($newOption);
3229
+
3230
+ continue;
3231
+ }
3232
+
3233
+ var $option = this.option(item);
3234
+
3235
+ if (item.children) {
3236
+ var $children = this.convertToOptions(item.children);
3237
+
3238
+ Utils.appendMany($option, $children);
3239
+ }
3240
+
3241
+ $options.push($option);
3242
+ }
3243
+
3244
+ return $options;
3245
+ };
3246
+
3247
+ return ArrayAdapter;
3248
+ });
3249
+
3250
+ S2.define('select2/data/ajax',[
3251
+ './array',
3252
+ '../utils',
3253
+ 'jquery'
3254
+ ], function (ArrayAdapter, Utils, $) {
3255
+ function AjaxAdapter ($element, options) {
3256
+ this.ajaxOptions = this._applyDefaults(options.get('ajax'));
3257
+
3258
+ if (this.ajaxOptions.processResults != null) {
3259
+ this.processResults = this.ajaxOptions.processResults;
3260
+ }
3261
+
3262
+ ArrayAdapter.__super__.constructor.call(this, $element, options);
3263
+ }
3264
+
3265
+ Utils.Extend(AjaxAdapter, ArrayAdapter);
3266
+
3267
+ AjaxAdapter.prototype._applyDefaults = function (options) {
3268
+ var defaults = {
3269
+ data: function (params) {
3270
+ return {
3271
+ q: params.term
3272
+ };
3273
+ },
3274
+ transport: function (params, success, failure) {
3275
+ var $request = $.ajax(params);
3276
+
3277
+ $request.then(success);
3278
+ $request.fail(failure);
3279
+
3280
+ return $request;
3281
+ }
3282
+ };
3283
+
3284
+ return $.extend({}, defaults, options, true);
3285
+ };
3286
+
3287
+ AjaxAdapter.prototype.processResults = function (results) {
3288
+ return results;
3289
+ };
3290
+
3291
+ AjaxAdapter.prototype.query = function (params, callback) {
3292
+ var matches = [];
3293
+ var self = this;
3294
+
3295
+ if (this._request != null) {
3296
+ // JSONP requests cannot always be aborted
3297
+ if ($.isFunction(this._request.abort)) {
3298
+ this._request.abort();
3299
+ }
3300
+
3301
+ this._request = null;
3302
+ }
3303
+
3304
+ var options = $.extend({
3305
+ type: 'GET'
3306
+ }, this.ajaxOptions);
3307
+
3308
+ if (typeof options.url === 'function') {
3309
+ options.url = options.url(params);
3310
+ }
3311
+
3312
+ if (typeof options.data === 'function') {
3313
+ options.data = options.data(params);
3314
+ }
3315
+
3316
+ function request () {
3317
+ var $request = options.transport(options, function (data) {
3318
+ var results = self.processResults(data, params);
3319
+
3320
+ if (self.options.get('debug') && window.console && console.error) {
3321
+ // Check to make sure that the response included a `results` key.
3322
+ if (!results || !results.results || !$.isArray(results.results)) {
3323
+ console.error(
3324
+ 'Select2: The AJAX results did not return an array in the ' +
3325
+ '`results` key of the response.'
3326
+ );
3327
+ }
3328
+ }
3329
+
3330
+ callback(results);
3331
+ }, function () {
3332
+ // TODO: Handle AJAX errors
3333
+ });
3334
+
3335
+ self._request = $request;
3336
+ }
3337
+
3338
+ if (this.ajaxOptions.delay && params.term !== '') {
3339
+ if (this._queryTimeout) {
3340
+ window.clearTimeout(this._queryTimeout);
3341
+ }
3342
+
3343
+ this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay);
3344
+ } else {
3345
+ request();
3346
+ }
3347
+ };
3348
+
3349
+ return AjaxAdapter;
3350
+ });
3351
+
3352
+ S2.define('select2/data/tags',[
3353
+ 'jquery'
3354
+ ], function ($) {
3355
+ function Tags (decorated, $element, options) {
3356
+ var tags = options.get('tags');
3357
+
3358
+ var createTag = options.get('createTag');
3359
+
3360
+ if (createTag !== undefined) {
3361
+ this.createTag = createTag;
3362
+ }
3363
+
3364
+ decorated.call(this, $element, options);
3365
+
3366
+ if ($.isArray(tags)) {
3367
+ for (var t = 0; t < tags.length; t++) {
3368
+ var tag = tags[t];
3369
+ var item = this._normalizeItem(tag);
3370
+
3371
+ var $option = this.option(item);
3372
+
3373
+ this.$element.append($option);
3374
+ }
3375
+ }
3376
+ }
3377
+
3378
+ Tags.prototype.query = function (decorated, params, callback) {
3379
+ var self = this;
3380
+
3381
+ this._removeOldTags();
3382
+
3383
+ if (params.term == null || params.page != null) {
3384
+ decorated.call(this, params, callback);
3385
+ return;
3386
+ }
3387
+
3388
+ function wrapper (obj, child) {
3389
+ var data = obj.results;
3390
+
3391
+ for (var i = 0; i < data.length; i++) {
3392
+ var option = data[i];
3393
+
3394
+ var checkChildren = (
3395
+ option.children != null &&
3396
+ !wrapper({
3397
+ results: option.children
3398
+ }, true)
3399
+ );
3400
+
3401
+ var checkText = option.text === params.term;
3402
+
3403
+ if (checkText || checkChildren) {
3404
+ if (child) {
3405
+ return false;
3406
+ }
3407
+
3408
+ obj.data = data;
3409
+ callback(obj);
3410
+
3411
+ return;
3412
+ }
3413
+ }
3414
+
3415
+ if (child) {
3416
+ return true;
3417
+ }
3418
+
3419
+ var tag = self.createTag(params);
3420
+
3421
+ if (tag != null) {
3422
+ var $option = self.option(tag);
3423
+ $option.attr('data-select2-tag', true);
3424
+
3425
+ self.addOptions([$option]);
3426
+
3427
+ self.insertTag(data, tag);
3428
+ }
3429
+
3430
+ obj.results = data;
3431
+
3432
+ callback(obj);
3433
+ }
3434
+
3435
+ decorated.call(this, params, wrapper);
3436
+ };
3437
+
3438
+ Tags.prototype.createTag = function (decorated, params) {
3439
+ var term = $.trim(params.term);
3440
+
3441
+ if (term === '') {
3442
+ return null;
3443
+ }
3444
+
3445
+ return {
3446
+ id: term,
3447
+ text: term
3448
+ };
3449
+ };
3450
+
3451
+ Tags.prototype.insertTag = function (_, data, tag) {
3452
+ data.unshift(tag);
3453
+ };
3454
+
3455
+ Tags.prototype._removeOldTags = function (_) {
3456
+ var tag = this._lastTag;
3457
+
3458
+ var $options = this.$element.find('option[data-select2-tag]');
3459
+
3460
+ $options.each(function () {
3461
+ if (this.selected) {
3462
+ return;
3463
+ }
3464
+
3465
+ $(this).remove();
3466
+ });
3467
+ };
3468
+
3469
+ return Tags;
3470
+ });
3471
+
3472
+ S2.define('select2/data/tokenizer',[
3473
+ 'jquery'
3474
+ ], function ($) {
3475
+ function Tokenizer (decorated, $element, options) {
3476
+ var tokenizer = options.get('tokenizer');
3477
+
3478
+ if (tokenizer !== undefined) {
3479
+ this.tokenizer = tokenizer;
3480
+ }
3481
+
3482
+ decorated.call(this, $element, options);
3483
+ }
3484
+
3485
+ Tokenizer.prototype.bind = function (decorated, container, $container) {
3486
+ decorated.call(this, container, $container);
3487
+
3488
+ this.$search = container.dropdown.$search || container.selection.$search ||
3489
+ $container.find('.select2-search__field');
3490
+ };
3491
+
3492
+ Tokenizer.prototype.query = function (decorated, params, callback) {
3493
+ var self = this;
3494
+
3495
+ function select (data) {
3496
+ self.select(data);
3497
+ }
3498
+
3499
+ params.term = params.term || '';
3500
+
3501
+ var tokenData = this.tokenizer(params, this.options, select);
3502
+
3503
+ if (tokenData.term !== params.term) {
3504
+ // Replace the search term if we have the search box
3505
+ if (this.$search.length) {
3506
+ this.$search.val(tokenData.term);
3507
+ this.$search.focus();
3508
+ }
3509
+
3510
+ params.term = tokenData.term;
3511
+ }
3512
+
3513
+ decorated.call(this, params, callback);
3514
+ };
3515
+
3516
+ Tokenizer.prototype.tokenizer = function (_, params, options, callback) {
3517
+ var separators = options.get('tokenSeparators') || [];
3518
+ var term = params.term;
3519
+ var i = 0;
3520
+
3521
+ var createTag = this.createTag || function (params) {
3522
+ return {
3523
+ id: params.term,
3524
+ text: params.term
3525
+ };
3526
+ };
3527
+
3528
+ while (i < term.length) {
3529
+ var termChar = term[i];
3530
+
3531
+ if ($.inArray(termChar, separators) === -1) {
3532
+ i++;
3533
+
3534
+ continue;
3535
+ }
3536
+
3537
+ var part = term.substr(0, i);
3538
+ var partParams = $.extend({}, params, {
3539
+ term: part
3540
+ });
3541
+
3542
+ var data = createTag(partParams);
3543
+
3544
+ callback(data);
3545
+
3546
+ // Reset the term to not include the tokenized portion
3547
+ term = term.substr(i + 1) || '';
3548
+ i = 0;
3549
+ }
3550
+
3551
+ return {
3552
+ term: term
3553
+ };
3554
+ };
3555
+
3556
+ return Tokenizer;
3557
+ });
3558
+
3559
+ S2.define('select2/data/minimumInputLength',[
3560
+
3561
+ ], function () {
3562
+ function MinimumInputLength (decorated, $e, options) {
3563
+ this.minimumInputLength = options.get('minimumInputLength');
3564
+
3565
+ decorated.call(this, $e, options);
3566
+ }
3567
+
3568
+ MinimumInputLength.prototype.query = function (decorated, params, callback) {
3569
+ params.term = params.term || '';
3570
+
3571
+ if (params.term.length < this.minimumInputLength) {
3572
+ this.trigger('results:message', {
3573
+ message: 'inputTooShort',
3574
+ args: {
3575
+ minimum: this.minimumInputLength,
3576
+ input: params.term,
3577
+ params: params
3578
+ }
3579
+ });
3580
+
3581
+ return;
3582
+ }
3583
+
3584
+ decorated.call(this, params, callback);
3585
+ };
3586
+
3587
+ return MinimumInputLength;
3588
+ });
3589
+
3590
+ S2.define('select2/data/maximumInputLength',[
3591
+
3592
+ ], function () {
3593
+ function MaximumInputLength (decorated, $e, options) {
3594
+ this.maximumInputLength = options.get('maximumInputLength');
3595
+
3596
+ decorated.call(this, $e, options);
3597
+ }
3598
+
3599
+ MaximumInputLength.prototype.query = function (decorated, params, callback) {
3600
+ params.term = params.term || '';
3601
+
3602
+ if (this.maximumInputLength > 0 &&
3603
+ params.term.length > this.maximumInputLength) {
3604
+ this.trigger('results:message', {
3605
+ message: 'inputTooLong',
3606
+ args: {
3607
+ maximum: this.maximumInputLength,
3608
+ input: params.term,
3609
+ params: params
3610
+ }
3611
+ });
3612
+
3613
+ return;
3614
+ }
3615
+
3616
+ decorated.call(this, params, callback);
3617
+ };
3618
+
3619
+ return MaximumInputLength;
3620
+ });
3621
+
3622
+ S2.define('select2/data/maximumSelectionLength',[
3623
+
3624
+ ], function (){
3625
+ function MaximumSelectionLength (decorated, $e, options) {
3626
+ this.maximumSelectionLength = options.get('maximumSelectionLength');
3627
+
3628
+ decorated.call(this, $e, options);
3629
+ }
3630
+
3631
+ MaximumSelectionLength.prototype.query =
3632
+ function (decorated, params, callback) {
3633
+ var self = this;
3634
+
3635
+ this.current(function (currentData) {
3636
+ var count = currentData != null ? currentData.length : 0;
3637
+ if (self.maximumSelectionLength > 0 &&
3638
+ count >= self.maximumSelectionLength) {
3639
+ self.trigger('results:message', {
3640
+ message: 'maximumSelected',
3641
+ args: {
3642
+ maximum: self.maximumSelectionLength
3643
+ }
3644
+ });
3645
+ return;
3646
+ }
3647
+ decorated.call(self, params, callback);
3648
+ });
3649
+ };
3650
+
3651
+ return MaximumSelectionLength;
3652
+ });
3653
+
3654
+ S2.define('select2/dropdown',[
3655
+ 'jquery',
3656
+ './utils'
3657
+ ], function ($, Utils) {
3658
+ function Dropdown ($element, options) {
3659
+ this.$element = $element;
3660
+ this.options = options;
3661
+
3662
+ Dropdown.__super__.constructor.call(this);
3663
+ }
3664
+
3665
+ Utils.Extend(Dropdown, Utils.Observable);
3666
+
3667
+ Dropdown.prototype.render = function () {
3668
+ var $dropdown = $(
3669
+ '<span class="select2-dropdown">' +
3670
+ '<span class="select2-results"></span>' +
3671
+ '</span>'
3672
+ );
3673
+
3674
+ $dropdown.attr('dir', this.options.get('dir'));
3675
+
3676
+ this.$dropdown = $dropdown;
3677
+
3678
+ return $dropdown;
3679
+ };
3680
+
3681
+ Dropdown.prototype.position = function ($dropdown, $container) {
3682
+ // Should be implmented in subclasses
3683
+ };
3684
+
3685
+ Dropdown.prototype.destroy = function () {
3686
+ // Remove the dropdown from the DOM
3687
+ this.$dropdown.remove();
3688
+ };
3689
+
3690
+ return Dropdown;
3691
+ });
3692
+
3693
+ S2.define('select2/dropdown/search',[
3694
+ 'jquery',
3695
+ '../utils'
3696
+ ], function ($, Utils) {
3697
+ function Search () { }
3698
+
3699
+ Search.prototype.render = function (decorated) {
3700
+ var $rendered = decorated.call(this);
3701
+
3702
+ var $search = $(
3703
+ '<span class="select2-search select2-search--dropdown">' +
3704
+ '<input class="select2-search__field" type="search" tabindex="-1"' +
3705
+ ' autocomplete="off" autocorrect="off" autocapitalize="off"' +
3706
+ ' spellcheck="false" role="textbox" />' +
3707
+ '</span>'
3708
+ );
3709
+
3710
+ this.$searchContainer = $search;
3711
+ this.$search = $search.find('input');
3712
+
3713
+ $rendered.prepend($search);
3714
+
3715
+ return $rendered;
3716
+ };
3717
+
3718
+ Search.prototype.bind = function (decorated, container, $container) {
3719
+ var self = this;
3720
+
3721
+ decorated.call(this, container, $container);
3722
+
3723
+ this.$search.on('keydown', function (evt) {
3724
+ self.trigger('keypress', evt);
3725
+
3726
+ self._keyUpPrevented = evt.isDefaultPrevented();
3727
+ });
3728
+
3729
+ // Workaround for browsers which do not support the `input` event
3730
+ // This will prevent double-triggering of events for browsers which support
3731
+ // both the `keyup` and `input` events.
3732
+ this.$search.on('input', function (evt) {
3733
+ // Unbind the duplicated `keyup` event
3734
+ $(this).off('keyup');
3735
+ });
3736
+
3737
+ this.$search.on('keyup input', function (evt) {
3738
+ self.handleSearch(evt);
3739
+ });
3740
+
3741
+ container.on('open', function () {
3742
+ self.$search.attr('tabindex', 0);
3743
+
3744
+ self.$search.focus();
3745
+
3746
+ window.setTimeout(function () {
3747
+ self.$search.focus();
3748
+ }, 0);
3749
+ });
3750
+
3751
+ container.on('close', function () {
3752
+ self.$search.attr('tabindex', -1);
3753
+
3754
+ self.$search.val('');
3755
+ });
3756
+
3757
+ container.on('results:all', function (params) {
3758
+ if (params.query.term == null || params.query.term === '') {
3759
+ var showSearch = self.showSearch(params);
3760
+
3761
+ if (showSearch) {
3762
+ self.$searchContainer.removeClass('select2-search--hide');
3763
+ } else {
3764
+ self.$searchContainer.addClass('select2-search--hide');
3765
+ }
3766
+ }
3767
+ });
3768
+ };
3769
+
3770
+ Search.prototype.handleSearch = function (evt) {
3771
+ if (!this._keyUpPrevented) {
3772
+ var input = this.$search.val();
3773
+
3774
+ this.trigger('query', {
3775
+ term: input
3776
+ });
3777
+ }
3778
+
3779
+ this._keyUpPrevented = false;
3780
+ };
3781
+
3782
+ Search.prototype.showSearch = function (_, params) {
3783
+ return true;
3784
+ };
3785
+
3786
+ return Search;
3787
+ });
3788
+
3789
+ S2.define('select2/dropdown/hidePlaceholder',[
3790
+
3791
+ ], function () {
3792
+ function HidePlaceholder (decorated, $element, options, dataAdapter) {
3793
+ this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
3794
+
3795
+ decorated.call(this, $element, options, dataAdapter);
3796
+ }
3797
+
3798
+ HidePlaceholder.prototype.append = function (decorated, data) {
3799
+ data.results = this.removePlaceholder(data.results);
3800
+
3801
+ decorated.call(this, data);
3802
+ };
3803
+
3804
+ HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) {
3805
+ if (typeof placeholder === 'string') {
3806
+ placeholder = {
3807
+ id: '',
3808
+ text: placeholder
3809
+ };
3810
+ }
3811
+
3812
+ return placeholder;
3813
+ };
3814
+
3815
+ HidePlaceholder.prototype.removePlaceholder = function (_, data) {
3816
+ var modifiedData = data.slice(0);
3817
+
3818
+ for (var d = data.length - 1; d >= 0; d--) {
3819
+ var item = data[d];
3820
+
3821
+ if (this.placeholder.id === item.id) {
3822
+ modifiedData.splice(d, 1);
3823
+ }
3824
+ }
3825
+
3826
+ return modifiedData;
3827
+ };
3828
+
3829
+ return HidePlaceholder;
3830
+ });
3831
+
3832
+ S2.define('select2/dropdown/infiniteScroll',[
3833
+ 'jquery'
3834
+ ], function ($) {
3835
+ function InfiniteScroll (decorated, $element, options, dataAdapter) {
3836
+ this.lastParams = {};
3837
+
3838
+ decorated.call(this, $element, options, dataAdapter);
3839
+
3840
+ this.$loadingMore = this.createLoadingMore();
3841
+ this.loading = false;
3842
+ }
3843
+
3844
+ InfiniteScroll.prototype.append = function (decorated, data) {
3845
+ this.$loadingMore.remove();
3846
+ this.loading = false;
3847
+
3848
+ decorated.call(this, data);
3849
+
3850
+ if (this.showLoadingMore(data)) {
3851
+ this.$results.append(this.$loadingMore);
3852
+ }
3853
+ };
3854
+
3855
+ InfiniteScroll.prototype.bind = function (decorated, container, $container) {
3856
+ var self = this;
3857
+
3858
+ decorated.call(this, container, $container);
3859
+
3860
+ container.on('query', function (params) {
3861
+ self.lastParams = params;
3862
+ self.loading = true;
3863
+ });
3864
+
3865
+ container.on('query:append', function (params) {
3866
+ self.lastParams = params;
3867
+ self.loading = true;
3868
+ });
3869
+
3870
+ this.$results.on('scroll', function () {
3871
+ var isLoadMoreVisible = $.contains(
3872
+ document.documentElement,
3873
+ self.$loadingMore[0]
3874
+ );
3875
+
3876
+ if (self.loading || !isLoadMoreVisible) {
3877
+ return;
3878
+ }
3879
+
3880
+ var currentOffset = self.$results.offset().top +
3881
+ self.$results.outerHeight(false);
3882
+ var loadingMoreOffset = self.$loadingMore.offset().top +
3883
+ self.$loadingMore.outerHeight(false);
3884
+
3885
+ if (currentOffset + 50 >= loadingMoreOffset) {
3886
+ self.loadMore();
3887
+ }
3888
+ });
3889
+ };
3890
+
3891
+ InfiniteScroll.prototype.loadMore = function () {
3892
+ this.loading = true;
3893
+
3894
+ var params = $.extend({}, {page: 1}, this.lastParams);
3895
+
3896
+ params.page++;
3897
+
3898
+ this.trigger('query:append', params);
3899
+ };
3900
+
3901
+ InfiniteScroll.prototype.showLoadingMore = function (_, data) {
3902
+ return data.pagination && data.pagination.more;
3903
+ };
3904
+
3905
+ InfiniteScroll.prototype.createLoadingMore = function () {
3906
+ var $option = $(
3907
+ '<li class="option load-more" role="treeitem"></li>'
3908
+ );
3909
+
3910
+ var message = this.options.get('translations').get('loadingMore');
3911
+
3912
+ $option.html(message(this.lastParams));
3913
+
3914
+ return $option;
3915
+ };
3916
+
3917
+ return InfiniteScroll;
3918
+ });
3919
+
3920
+ S2.define('select2/dropdown/attachBody',[
3921
+ 'jquery',
3922
+ '../utils'
3923
+ ], function ($, Utils) {
3924
+ function AttachBody (decorated, $element, options) {
3925
+ this.$dropdownParent = options.get('dropdownParent') || document.body;
3926
+
3927
+ decorated.call(this, $element, options);
3928
+ }
3929
+
3930
+ AttachBody.prototype.bind = function (decorated, container, $container) {
3931
+ var self = this;
3932
+
3933
+ var setupResultsEvents = false;
3934
+
3935
+ decorated.call(this, container, $container);
3936
+
3937
+ container.on('open', function () {
3938
+ self._showDropdown();
3939
+ self._attachPositioningHandler(container);
3940
+
3941
+ if (!setupResultsEvents) {
3942
+ setupResultsEvents = true;
3943
+
3944
+ container.on('results:all', function () {
3945
+ self._positionDropdown();
3946
+ self._resizeDropdown();
3947
+ });
3948
+
3949
+ container.on('results:append', function () {
3950
+ self._positionDropdown();
3951
+ self._resizeDropdown();
3952
+ });
3953
+ }
3954
+ });
3955
+
3956
+ container.on('close', function () {
3957
+ self._hideDropdown();
3958
+ self._detachPositioningHandler(container);
3959
+ });
3960
+
3961
+ this.$dropdownContainer.on('mousedown', function (evt) {
3962
+ evt.stopPropagation();
3963
+ });
3964
+ };
3965
+
3966
+ AttachBody.prototype.position = function (decorated, $dropdown, $container) {
3967
+ // Clone all of the container classes
3968
+ $dropdown.attr('class', $container.attr('class'));
3969
+
3970
+ $dropdown.removeClass('select2');
3971
+ $dropdown.addClass('select2-container--open');
3972
+
3973
+ $dropdown.css({
3974
+ position: 'absolute',
3975
+ top: -999999
3976
+ });
3977
+
3978
+ this.$container = $container;
3979
+ };
3980
+
3981
+ AttachBody.prototype.render = function (decorated) {
3982
+ var $container = $('<span></span>');
3983
+
3984
+ var $dropdown = decorated.call(this);
3985
+ $container.append($dropdown);
3986
+
3987
+ this.$dropdownContainer = $container;
3988
+
3989
+ return $container;
3990
+ };
3991
+
3992
+ AttachBody.prototype._hideDropdown = function (decorated) {
3993
+ this.$dropdownContainer.detach();
3994
+ };
3995
+
3996
+ AttachBody.prototype._attachPositioningHandler = function (container) {
3997
+ var self = this;
3998
+
3999
+ var scrollEvent = 'scroll.select2.' + container.id;
4000
+ var resizeEvent = 'resize.select2.' + container.id;
4001
+ var orientationEvent = 'orientationchange.select2.' + container.id;
4002
+
4003
+ var $watchers = this.$container.parents().filter(Utils.hasScroll);
4004
+ $watchers.each(function () {
4005
+ $(this).data('select2-scroll-position', {
4006
+ x: $(this).scrollLeft(),
4007
+ y: $(this).scrollTop()
4008
+ });
4009
+ });
4010
+
4011
+ $watchers.on(scrollEvent, function (ev) {
4012
+ var position = $(this).data('select2-scroll-position');
4013
+ $(this).scrollTop(position.y);
4014
+ });
4015
+
4016
+ $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent,
4017
+ function (e) {
4018
+ self._positionDropdown();
4019
+ self._resizeDropdown();
4020
+ });
4021
+ };
4022
+
4023
+ AttachBody.prototype._detachPositioningHandler = function (container) {
4024
+ var scrollEvent = 'scroll.select2.' + container.id;
4025
+ var resizeEvent = 'resize.select2.' + container.id;
4026
+ var orientationEvent = 'orientationchange.select2.' + container.id;
4027
+
4028
+ var $watchers = this.$container.parents().filter(Utils.hasScroll);
4029
+ $watchers.off(scrollEvent);
4030
+
4031
+ $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent);
4032
+ };
4033
+
4034
+ AttachBody.prototype._positionDropdown = function () {
4035
+ var $window = $(window);
4036
+
4037
+ var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
4038
+ var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');
4039
+
4040
+ var newDirection = null;
4041
+
4042
+ var position = this.$container.position();
4043
+ var offset = this.$container.offset();
4044
+
4045
+ offset.bottom = offset.top + this.$container.outerHeight(false);
4046
+
4047
+ var container = {
4048
+ height: this.$container.outerHeight(false)
4049
+ };
4050
+
4051
+ container.top = offset.top;
4052
+ container.bottom = offset.top + container.height;
4053
+
4054
+ var dropdown = {
4055
+ height: this.$dropdown.outerHeight(false)
4056
+ };
4057
+
4058
+ var viewport = {
4059
+ top: $window.scrollTop(),
4060
+ bottom: $window.scrollTop() + $window.height()
4061
+ };
4062
+
4063
+ var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
4064
+ var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);
4065
+
4066
+ var css = {
4067
+ left: offset.left,
4068
+ top: container.bottom
4069
+ };
4070
+
4071
+ if (!isCurrentlyAbove && !isCurrentlyBelow) {
4072
+ newDirection = 'below';
4073
+ }
4074
+
4075
+ if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
4076
+ newDirection = 'above';
4077
+ } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
4078
+ newDirection = 'below';
4079
+ }
4080
+
4081
+ if (newDirection == 'above' ||
4082
+ (isCurrentlyAbove && newDirection !== 'below')) {
4083
+ css.top = container.top - dropdown.height;
4084
+ }
4085
+
4086
+ if (newDirection != null) {
4087
+ this.$dropdown
4088
+ .removeClass('select2-dropdown--below select2-dropdown--above')
4089
+ .addClass('select2-dropdown--' + newDirection);
4090
+ this.$container
4091
+ .removeClass('select2-container--below select2-container--above')
4092
+ .addClass('select2-container--' + newDirection);
4093
+ }
4094
+
4095
+ this.$dropdownContainer.css(css);
4096
+ };
4097
+
4098
+ AttachBody.prototype._resizeDropdown = function () {
4099
+ this.$dropdownContainer.width();
4100
+
4101
+ var css = {
4102
+ width: this.$container.outerWidth(false) + 'px'
4103
+ };
4104
+
4105
+ if (this.options.get('dropdownAutoWidth')) {
4106
+ css.minWidth = css.width;
4107
+ css.width = 'auto';
4108
+ }
4109
+
4110
+ this.$dropdown.css(css);
4111
+ };
4112
+
4113
+ AttachBody.prototype._showDropdown = function (decorated) {
4114
+ this.$dropdownContainer.appendTo(this.$dropdownParent);
4115
+
4116
+ this._positionDropdown();
4117
+ this._resizeDropdown();
4118
+ };
4119
+
4120
+ return AttachBody;
4121
+ });
4122
+
4123
+ S2.define('select2/dropdown/minimumResultsForSearch',[
4124
+
4125
+ ], function () {
4126
+ function countResults (data) {
4127
+ var count = 0;
4128
+
4129
+ for (var d = 0; d < data.length; d++) {
4130
+ var item = data[d];
4131
+
4132
+ if (item.children) {
4133
+ count += countResults(item.children);
4134
+ } else {
4135
+ count++;
4136
+ }
4137
+ }
4138
+
4139
+ return count;
4140
+ }
4141
+
4142
+ function MinimumResultsForSearch (decorated, $element, options, dataAdapter) {
4143
+ this.minimumResultsForSearch = options.get('minimumResultsForSearch');
4144
+
4145
+ if (this.minimumResultsForSearch < 0) {
4146
+ this.minimumResultsForSearch = Infinity;
4147
+ }
4148
+
4149
+ decorated.call(this, $element, options, dataAdapter);
4150
+ }
4151
+
4152
+ MinimumResultsForSearch.prototype.showSearch = function (decorated, params) {
4153
+ if (countResults(params.data.results) < this.minimumResultsForSearch) {
4154
+ return false;
4155
+ }
4156
+
4157
+ return decorated.call(this, params);
4158
+ };
4159
+
4160
+ return MinimumResultsForSearch;
4161
+ });
4162
+
4163
+ S2.define('select2/dropdown/selectOnClose',[
4164
+
4165
+ ], function () {
4166
+ function SelectOnClose () { }
4167
+
4168
+ SelectOnClose.prototype.bind = function (decorated, container, $container) {
4169
+ var self = this;
4170
+
4171
+ decorated.call(this, container, $container);
4172
+
4173
+ container.on('close', function () {
4174
+ self._handleSelectOnClose();
4175
+ });
4176
+ };
4177
+
4178
+ SelectOnClose.prototype._handleSelectOnClose = function () {
4179
+ var $highlightedResults = this.getHighlightedResults();
4180
+
4181
+ if ($highlightedResults.length < 1) {
4182
+ return;
4183
+ }
4184
+
4185
+ this.trigger('select', {
4186
+ data: $highlightedResults.data('data')
4187
+ });
4188
+ };
4189
+
4190
+ return SelectOnClose;
4191
+ });
4192
+
4193
+ S2.define('select2/dropdown/closeOnSelect',[
4194
+
4195
+ ], function () {
4196
+ function CloseOnSelect () { }
4197
+
4198
+ CloseOnSelect.prototype.bind = function (decorated, container, $container) {
4199
+ var self = this;
4200
+
4201
+ decorated.call(this, container, $container);
4202
+
4203
+ container.on('select', function (evt) {
4204
+ self._selectTriggered(evt);
4205
+ });
4206
+
4207
+ container.on('unselect', function (evt) {
4208
+ self._selectTriggered(evt);
4209
+ });
4210
+ };
4211
+
4212
+ CloseOnSelect.prototype._selectTriggered = function (_, evt) {
4213
+ var originalEvent = evt.originalEvent;
4214
+
4215
+ // Don't close if the control key is being held
4216
+ if (originalEvent && originalEvent.ctrlKey) {
4217
+ return;
4218
+ }
4219
+
4220
+ this.trigger('close');
4221
+ };
4222
+
4223
+ return CloseOnSelect;
4224
+ });
4225
+
4226
+ S2.define('select2/i18n/en',[],function () {
4227
+ // English
4228
+ return {
4229
+ errorLoading: function () {
4230
+ return 'The results could not be loaded.';
4231
+ },
4232
+ inputTooLong: function (args) {
4233
+ var overChars = args.input.length - args.maximum;
4234
+
4235
+ var message = 'Please delete ' + overChars + ' character';
4236
+
4237
+ if (overChars != 1) {
4238
+ message += 's';
4239
+ }
4240
+
4241
+ return message;
4242
+ },
4243
+ inputTooShort: function (args) {
4244
+ var remainingChars = args.minimum - args.input.length;
4245
+
4246
+ var message = 'Please enter ' + remainingChars + ' or more characters';
4247
+
4248
+ return message;
4249
+ },
4250
+ loadingMore: function () {
4251
+ return 'Loading more results…';
4252
+ },
4253
+ maximumSelected: function (args) {
4254
+ var message = 'You can only select ' + args.maximum + ' item';
4255
+
4256
+ if (args.maximum != 1) {
4257
+ message += 's';
4258
+ }
4259
+
4260
+ return message;
4261
+ },
4262
+ noResults: function () {
4263
+ return 'No results found';
4264
+ },
4265
+ searching: function () {
4266
+ return 'Searching…';
4267
+ }
4268
+ };
4269
+ });
4270
+
4271
+ S2.define('select2/defaults',[
4272
+ 'jquery',
4273
+ 'require',
4274
+
4275
+ './results',
4276
+
4277
+ './selection/single',
4278
+ './selection/multiple',
4279
+ './selection/placeholder',
4280
+ './selection/allowClear',
4281
+ './selection/search',
4282
+ './selection/eventRelay',
4283
+
4284
+ './utils',
4285
+ './translation',
4286
+ './diacritics',
4287
+
4288
+ './data/select',
4289
+ './data/array',
4290
+ './data/ajax',
4291
+ './data/tags',
4292
+ './data/tokenizer',
4293
+ './data/minimumInputLength',
4294
+ './data/maximumInputLength',
4295
+ './data/maximumSelectionLength',
4296
+
4297
+ './dropdown',
4298
+ './dropdown/search',
4299
+ './dropdown/hidePlaceholder',
4300
+ './dropdown/infiniteScroll',
4301
+ './dropdown/attachBody',
4302
+ './dropdown/minimumResultsForSearch',
4303
+ './dropdown/selectOnClose',
4304
+ './dropdown/closeOnSelect',
4305
+
4306
+ './i18n/en'
4307
+ ], function ($, require,
4308
+
4309
+ ResultsList,
4310
+
4311
+ SingleSelection, MultipleSelection, Placeholder, AllowClear,
4312
+ SelectionSearch, EventRelay,
4313
+
4314
+ Utils, Translation, DIACRITICS,
4315
+
4316
+ SelectData, ArrayData, AjaxData, Tags, Tokenizer,
4317
+ MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
4318
+
4319
+ Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
4320
+ AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,
4321
+
4322
+ EnglishTranslation) {
4323
+ function Defaults () {
4324
+ this.reset();
4325
+ }
4326
+
4327
+ Defaults.prototype.apply = function (options) {
4328
+ options = $.extend({}, this.defaults, options);
4329
+
4330
+ if (options.dataAdapter == null) {
4331
+ if (options.ajax != null) {
4332
+ options.dataAdapter = AjaxData;
4333
+ } else if (options.data != null) {
4334
+ options.dataAdapter = ArrayData;
4335
+ } else {
4336
+ options.dataAdapter = SelectData;
4337
+ }
4338
+
4339
+ if (options.minimumInputLength > 0) {
4340
+ options.dataAdapter = Utils.Decorate(
4341
+ options.dataAdapter,
4342
+ MinimumInputLength
4343
+ );
4344
+ }
4345
+
4346
+ if (options.maximumInputLength > 0) {
4347
+ options.dataAdapter = Utils.Decorate(
4348
+ options.dataAdapter,
4349
+ MaximumInputLength
4350
+ );
4351
+ }
4352
+
4353
+ if (options.maximumSelectionLength > 0) {
4354
+ options.dataAdapter = Utils.Decorate(
4355
+ options.dataAdapter,
4356
+ MaximumSelectionLength
4357
+ );
4358
+ }
4359
+
4360
+ if (options.tags) {
4361
+ options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
4362
+ }
4363
+
4364
+ if (options.tokenSeparators != null || options.tokenizer != null) {
4365
+ options.dataAdapter = Utils.Decorate(
4366
+ options.dataAdapter,
4367
+ Tokenizer
4368
+ );
4369
+ }
4370
+
4371
+ if (options.query != null) {
4372
+ var Query = require(options.amdBase + 'compat/query');
4373
+
4374
+ options.dataAdapter = Utils.Decorate(
4375
+ options.dataAdapter,
4376
+ Query
4377
+ );
4378
+ }
4379
+
4380
+ if (options.initSelection != null) {
4381
+ var InitSelection = require(options.amdBase + 'compat/initSelection');
4382
+
4383
+ options.dataAdapter = Utils.Decorate(
4384
+ options.dataAdapter,
4385
+ InitSelection
4386
+ );
4387
+ }
4388
+ }
4389
+
4390
+ if (options.resultsAdapter == null) {
4391
+ options.resultsAdapter = ResultsList;
4392
+
4393
+ if (options.ajax != null) {
4394
+ options.resultsAdapter = Utils.Decorate(
4395
+ options.resultsAdapter,
4396
+ InfiniteScroll
4397
+ );
4398
+ }
4399
+
4400
+ if (options.placeholder != null) {
4401
+ options.resultsAdapter = Utils.Decorate(
4402
+ options.resultsAdapter,
4403
+ HidePlaceholder
4404
+ );
4405
+ }
4406
+
4407
+ if (options.selectOnClose) {
4408
+ options.resultsAdapter = Utils.Decorate(
4409
+ options.resultsAdapter,
4410
+ SelectOnClose
4411
+ );
4412
+ }
4413
+ }
4414
+
4415
+ if (options.dropdownAdapter == null) {
4416
+ if (options.multiple) {
4417
+ options.dropdownAdapter = Dropdown;
4418
+ } else {
4419
+ var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
4420
+
4421
+ options.dropdownAdapter = SearchableDropdown;
4422
+ }
4423
+
4424
+ if (options.minimumResultsForSearch !== 0) {
4425
+ options.dropdownAdapter = Utils.Decorate(
4426
+ options.dropdownAdapter,
4427
+ MinimumResultsForSearch
4428
+ );
4429
+ }
4430
+
4431
+ if (options.closeOnSelect) {
4432
+ options.dropdownAdapter = Utils.Decorate(
4433
+ options.dropdownAdapter,
4434
+ CloseOnSelect
4435
+ );
4436
+ }
4437
+
4438
+ if (
4439
+ options.dropdownCssClass != null ||
4440
+ options.dropdownCss != null ||
4441
+ options.adaptDropdownCssClass != null
4442
+ ) {
4443
+ var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');
4444
+
4445
+ options.dropdownAdapter = Utils.Decorate(
4446
+ options.dropdownAdapter,
4447
+ DropdownCSS
4448
+ );
4449
+ }
4450
+
4451
+ options.dropdownAdapter = Utils.Decorate(
4452
+ options.dropdownAdapter,
4453
+ AttachBody
4454
+ );
4455
+ }
4456
+
4457
+ if (options.selectionAdapter == null) {
4458
+ if (options.multiple) {
4459
+ options.selectionAdapter = MultipleSelection;
4460
+ } else {
4461
+ options.selectionAdapter = SingleSelection;
4462
+ }
4463
+
4464
+ // Add the placeholder mixin if a placeholder was specified
4465
+ if (options.placeholder != null) {
4466
+ options.selectionAdapter = Utils.Decorate(
4467
+ options.selectionAdapter,
4468
+ Placeholder
4469
+ );
4470
+ }
4471
+
4472
+ if (options.allowClear) {
4473
+ options.selectionAdapter = Utils.Decorate(
4474
+ options.selectionAdapter,
4475
+ AllowClear
4476
+ );
4477
+ }
4478
+
4479
+ if (options.multiple) {
4480
+ options.selectionAdapter = Utils.Decorate(
4481
+ options.selectionAdapter,
4482
+ SelectionSearch
4483
+ );
4484
+ }
4485
+
4486
+ if (
4487
+ options.containerCssClass != null ||
4488
+ options.containerCss != null ||
4489
+ options.adaptContainerCssClass != null
4490
+ ) {
4491
+ var ContainerCSS = require(options.amdBase + 'compat/containerCss');
4492
+
4493
+ options.selectionAdapter = Utils.Decorate(
4494
+ options.selectionAdapter,
4495
+ ContainerCSS
4496
+ );
4497
+ }
4498
+
4499
+ options.selectionAdapter = Utils.Decorate(
4500
+ options.selectionAdapter,
4501
+ EventRelay
4502
+ );
4503
+ }
4504
+
4505
+ if (typeof options.language === 'string') {
4506
+ // Check if the language is specified with a region
4507
+ if (options.language.indexOf('-') > 0) {
4508
+ // Extract the region information if it is included
4509
+ var languageParts = options.language.split('-');
4510
+ var baseLanguage = languageParts[0];
4511
+
4512
+ options.language = [options.language, baseLanguage];
4513
+ } else {
4514
+ options.language = [options.language];
4515
+ }
4516
+ }
4517
+
4518
+ if ($.isArray(options.language)) {
4519
+ var languages = new Translation();
4520
+ options.language.push('en');
4521
+
4522
+ var languageNames = options.language;
4523
+
4524
+ for (var l = 0; l < languageNames.length; l++) {
4525
+ var name = languageNames[l];
4526
+ var language = {};
4527
+
4528
+ try {
4529
+ // Try to load it with the original name
4530
+ language = Translation.loadPath(name);
4531
+ } catch (e) {
4532
+ try {
4533
+ // If we couldn't load it, check if it wasn't the full path
4534
+ name = this.defaults.amdLanguageBase + name;
4535
+ language = Translation.loadPath(name);
4536
+ } catch (ex) {
4537
+ // The translation could not be loaded at all. Sometimes this is
4538
+ // because of a configuration problem, other times this can be
4539
+ // because of how Select2 helps load all possible translation files.
4540
+ if (options.debug && window.console && console.warn) {
4541
+ console.warn(
4542
+ 'Select2: The language file for "' + name + '" could not be ' +
4543
+ 'automatically loaded. A fallback will be used instead.'
4544
+ );
4545
+ }
4546
+
4547
+ continue;
4548
+ }
4549
+ }
4550
+
4551
+ languages.extend(language);
4552
+ }
4553
+
4554
+ options.translations = languages;
4555
+ } else {
4556
+ var baseTranslation = Translation.loadPath(
4557
+ this.defaults.amdLanguageBase + 'en'
4558
+ );
4559
+ var customTranslation = new Translation(options.language);
4560
+
4561
+ customTranslation.extend(baseTranslation);
4562
+
4563
+ options.translations = customTranslation;
4564
+ }
4565
+
4566
+ return options;
4567
+ };
4568
+
4569
+ Defaults.prototype.reset = function () {
4570
+ function stripDiacritics (text) {
4571
+ // Used 'uni range + named function' from http://jsperf.com/diacritics/18
4572
+ function match(a) {
4573
+ return DIACRITICS[a] || a;
4574
+ }
4575
+
4576
+ return text.replace(/[^\u0000-\u007E]/g, match);
4577
+ }
4578
+
4579
+ function matcher (params, data) {
4580
+ // Always return the object if there is nothing to compare
4581
+ if ($.trim(params.term) === '') {
4582
+ return data;
4583
+ }
4584
+
4585
+ // Do a recursive check for options with children
4586
+ if (data.children && data.children.length > 0) {
4587
+ // Clone the data object if there are children
4588
+ // This is required as we modify the object to remove any non-matches
4589
+ var match = $.extend(true, {}, data);
4590
+
4591
+ // Check each child of the option
4592
+ for (var c = data.children.length - 1; c >= 0; c--) {
4593
+ var child = data.children[c];
4594
+
4595
+ var matches = matcher(params, child);
4596
+
4597
+ // If there wasn't a match, remove the object in the array
4598
+ if (matches == null) {
4599
+ match.children.splice(c, 1);
4600
+ }
4601
+ }
4602
+
4603
+ // If any children matched, return the new object
4604
+ if (match.children.length > 0) {
4605
+ return match;
4606
+ }
4607
+
4608
+ // If there were no matching children, check just the plain object
4609
+ return matcher(params, match);
4610
+ }
4611
+
4612
+ var original = stripDiacritics(data.text).toUpperCase();
4613
+ var term = stripDiacritics(params.term).toUpperCase();
4614
+
4615
+ // Check if the text contains the term
4616
+ if (original.indexOf(term) > -1) {
4617
+ return data;
4618
+ }
4619
+
4620
+ // If it doesn't contain the term, don't return anything
4621
+ return null;
4622
+ }
4623
+
4624
+ this.defaults = {
4625
+ amdBase: './',
4626
+ amdLanguageBase: './i18n/',
4627
+ closeOnSelect: true,
4628
+ debug: false,
4629
+ dropdownAutoWidth: false,
4630
+ escapeMarkup: Utils.escapeMarkup,
4631
+ language: EnglishTranslation,
4632
+ matcher: matcher,
4633
+ minimumInputLength: 0,
4634
+ maximumInputLength: 0,
4635
+ maximumSelectionLength: 0,
4636
+ minimumResultsForSearch: 0,
4637
+ selectOnClose: false,
4638
+ sorter: function (data) {
4639
+ return data;
4640
+ },
4641
+ templateResult: function (result) {
4642
+ return result.text;
4643
+ },
4644
+ templateSelection: function (selection) {
4645
+ return selection.text;
4646
+ },
4647
+ theme: 'default',
4648
+ width: 'resolve'
4649
+ };
4650
+ };
4651
+
4652
+ Defaults.prototype.set = function (key, value) {
4653
+ var camelKey = $.camelCase(key);
4654
+
4655
+ var data = {};
4656
+ data[camelKey] = value;
4657
+
4658
+ var convertedData = Utils._convertData(data);
4659
+
4660
+ $.extend(this.defaults, convertedData);
4661
+ };
4662
+
4663
+ var defaults = new Defaults();
4664
+
4665
+ return defaults;
4666
+ });
4667
+
4668
+ S2.define('select2/options',[
4669
+ 'require',
4670
+ 'jquery',
4671
+ './defaults',
4672
+ './utils'
4673
+ ], function (require, $, Defaults, Utils) {
4674
+ function Options (options, $element) {
4675
+ this.options = options;
4676
+
4677
+ if ($element != null) {
4678
+ this.fromElement($element);
4679
+ }
4680
+
4681
+ this.options = Defaults.apply(this.options);
4682
+
4683
+ if ($element && $element.is('input')) {
4684
+ var InputCompat = require(this.get('amdBase') + 'compat/inputData');
4685
+
4686
+ this.options.dataAdapter = Utils.Decorate(
4687
+ this.options.dataAdapter,
4688
+ InputCompat
4689
+ );
4690
+ }
4691
+ }
4692
+
4693
+ Options.prototype.fromElement = function ($e) {
4694
+ var excludedData = ['select2'];
4695
+
4696
+ if (this.options.multiple == null) {
4697
+ this.options.multiple = $e.prop('multiple');
4698
+ }
4699
+
4700
+ if (this.options.disabled == null) {
4701
+ this.options.disabled = $e.prop('disabled');
4702
+ }
4703
+
4704
+ if (this.options.language == null) {
4705
+ if ($e.prop('lang')) {
4706
+ this.options.language = $e.prop('lang').toLowerCase();
4707
+ } else if ($e.closest('[lang]').prop('lang')) {
4708
+ this.options.language = $e.closest('[lang]').prop('lang');
4709
+ }
4710
+ }
4711
+
4712
+ if (this.options.dir == null) {
4713
+ if ($e.prop('dir')) {
4714
+ this.options.dir = $e.prop('dir');
4715
+ } else if ($e.closest('[dir]').prop('dir')) {
4716
+ this.options.dir = $e.closest('[dir]').prop('dir');
4717
+ } else {
4718
+ this.options.dir = 'ltr';
4719
+ }
4720
+ }
4721
+
4722
+ $e.prop('disabled', this.options.disabled);
4723
+ $e.prop('multiple', this.options.multiple);
4724
+
4725
+ if ($e.data('select2Tags')) {
4726
+ if (this.options.debug && window.console && console.warn) {
4727
+ console.warn(
4728
+ 'Select2: The `data-select2-tags` attribute has been changed to ' +
4729
+ 'use the `data-data` and `data-tags="true"` attributes and will be ' +
4730
+ 'removed in future versions of Select2.'
4731
+ );
4732
+ }
4733
+
4734
+ $e.data('data', $e.data('select2Tags'));
4735
+ $e.data('tags', true);
4736
+ }
4737
+
4738
+ if ($e.data('ajaxUrl')) {
4739
+ if (this.options.debug && window.console && console.warn) {
4740
+ console.warn(
4741
+ 'Select2: The `data-ajax-url` attribute has been changed to ' +
4742
+ '`data-ajax--url` and support for the old attribute will be removed' +
4743
+ ' in future versions of Select2.'
4744
+ );
4745
+ }
4746
+
4747
+ $e.attr('ajax--url', $e.data('ajaxUrl'));
4748
+ $e.data('ajax--url', $e.data('ajaxUrl'));
4749
+ }
4750
+
4751
+ var dataset = {};
4752
+
4753
+ // Prefer the element's `dataset` attribute if it exists
4754
+ // jQuery 1.x does not correctly handle data attributes with multiple dashes
4755
+ if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) {
4756
+ dataset = $.extend(true, {}, $e[0].dataset, $e.data());
4757
+ } else {
4758
+ dataset = $e.data();
4759
+ }
4760
+
4761
+ var data = $.extend(true, {}, dataset);
4762
+
4763
+ data = Utils._convertData(data);
4764
+
4765
+ for (var key in data) {
4766
+ if ($.inArray(key, excludedData) > -1) {
4767
+ continue;
4768
+ }
4769
+
4770
+ if ($.isPlainObject(this.options[key])) {
4771
+ $.extend(this.options[key], data[key]);
4772
+ } else {
4773
+ this.options[key] = data[key];
4774
+ }
4775
+ }
4776
+
4777
+ return this;
4778
+ };
4779
+
4780
+ Options.prototype.get = function (key) {
4781
+ return this.options[key];
4782
+ };
4783
+
4784
+ Options.prototype.set = function (key, val) {
4785
+ this.options[key] = val;
4786
+ };
4787
+
4788
+ return Options;
4789
+ });
4790
+
4791
+ S2.define('select2/core',[
4792
+ 'jquery',
4793
+ './options',
4794
+ './utils',
4795
+ './keys'
4796
+ ], function ($, Options, Utils, KEYS) {
4797
+ var Select2 = function ($element, options) {
4798
+ if ($element.data('select2') != null) {
4799
+ $element.data('select2').destroy();
4800
+ }
4801
+
4802
+ this.$element = $element;
4803
+
4804
+ this.id = this._generateId($element);
4805
+
4806
+ options = options || {};
4807
+
4808
+ this.options = new Options(options, $element);
4809
+
4810
+ Select2.__super__.constructor.call(this);
4811
+
4812
+ // Set up the tabindex
4813
+
4814
+ var tabindex = $element.attr('tabindex') || 0;
4815
+ $element.data('old-tabindex', tabindex);
4816
+ $element.attr('tabindex', '-1');
4817
+
4818
+ // Set up containers and adapters
4819
+
4820
+ var DataAdapter = this.options.get('dataAdapter');
4821
+ this.dataAdapter = new DataAdapter($element, this.options);
4822
+
4823
+ var $container = this.render();
4824
+
4825
+ this._placeContainer($container);
4826
+
4827
+ var SelectionAdapter = this.options.get('selectionAdapter');
4828
+ this.selection = new SelectionAdapter($element, this.options);
4829
+ this.$selection = this.selection.render();
4830
+
4831
+ this.selection.position(this.$selection, $container);
4832
+
4833
+ var DropdownAdapter = this.options.get('dropdownAdapter');
4834
+ this.dropdown = new DropdownAdapter($element, this.options);
4835
+ this.$dropdown = this.dropdown.render();
4836
+
4837
+ this.dropdown.position(this.$dropdown, $container);
4838
+
4839
+ var ResultsAdapter = this.options.get('resultsAdapter');
4840
+ this.results = new ResultsAdapter($element, this.options, this.dataAdapter);
4841
+ this.$results = this.results.render();
4842
+
4843
+ this.results.position(this.$results, this.$dropdown);
4844
+
4845
+ // Bind events
4846
+
4847
+ var self = this;
4848
+
4849
+ // Bind the container to all of the adapters
4850
+ this._bindAdapters();
4851
+
4852
+ // Register any DOM event handlers
4853
+ this._registerDomEvents();
4854
+
4855
+ // Register any internal event handlers
4856
+ this._registerDataEvents();
4857
+ this._registerSelectionEvents();
4858
+ this._registerDropdownEvents();
4859
+ this._registerResultsEvents();
4860
+ this._registerEvents();
4861
+
4862
+ // Set the initial state
4863
+ this.dataAdapter.current(function (initialData) {
4864
+ self.trigger('selection:update', {
4865
+ data: initialData
4866
+ });
4867
+ });
4868
+
4869
+ // Hide the original select
4870
+ $element.addClass('select2-hidden-accessible');
4871
+ $element.attr('aria-hidden', 'true');
4872
+
4873
+ // Synchronize any monitored attributes
4874
+ this._syncAttributes();
4875
+
4876
+ $element.data('select2', this);
4877
+ };
4878
+
4879
+ Utils.Extend(Select2, Utils.Observable);
4880
+
4881
+ Select2.prototype._generateId = function ($element) {
4882
+ var id = '';
4883
+
4884
+ if ($element.attr('id') != null) {
4885
+ id = $element.attr('id');
4886
+ } else if ($element.attr('name') != null) {
4887
+ id = $element.attr('name') + '-' + Utils.generateChars(2);
4888
+ } else {
4889
+ id = Utils.generateChars(4);
4890
+ }
4891
+
4892
+ id = 'select2-' + id;
4893
+
4894
+ return id;
4895
+ };
4896
+
4897
+ Select2.prototype._placeContainer = function ($container) {
4898
+ $container.insertAfter(this.$element);
4899
+
4900
+ var width = this._resolveWidth(this.$element, this.options.get('width'));
4901
+
4902
+ if (width != null) {
4903
+ $container.css('width', width);
4904
+ }
4905
+ };
4906
+
4907
+ Select2.prototype._resolveWidth = function ($element, method) {
4908
+ var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;
4909
+
4910
+ if (method == 'resolve') {
4911
+ var styleWidth = this._resolveWidth($element, 'style');
4912
+
4913
+ if (styleWidth != null) {
4914
+ return styleWidth;
4915
+ }
4916
+
4917
+ return this._resolveWidth($element, 'element');
4918
+ }
4919
+
4920
+ if (method == 'element') {
4921
+ var elementWidth = $element.outerWidth(false);
4922
+
4923
+ if (elementWidth <= 0) {
4924
+ return 'auto';
4925
+ }
4926
+
4927
+ return elementWidth + 'px';
4928
+ }
4929
+
4930
+ if (method == 'style') {
4931
+ var style = $element.attr('style');
4932
+
4933
+ if (typeof(style) !== 'string') {
4934
+ return null;
4935
+ }
4936
+
4937
+ var attrs = style.split(';');
4938
+
4939
+ for (var i = 0, l = attrs.length; i < l; i = i + 1) {
4940
+ var attr = attrs[i].replace(/\s/g, '');
4941
+ var matches = attr.match(WIDTH);
4942
+
4943
+ if (matches !== null && matches.length >= 1) {
4944
+ return matches[1];
4945
+ }
4946
+ }
4947
+
4948
+ return null;
4949
+ }
4950
+
4951
+ return method;
4952
+ };
4953
+
4954
+ Select2.prototype._bindAdapters = function () {
4955
+ this.dataAdapter.bind(this, this.$container);
4956
+ this.selection.bind(this, this.$container);
4957
+
4958
+ this.dropdown.bind(this, this.$container);
4959
+ this.results.bind(this, this.$container);
4960
+ };
4961
+
4962
+ Select2.prototype._registerDomEvents = function () {
4963
+ var self = this;
4964
+
4965
+ this.$element.on('change.select2', function () {
4966
+ self.dataAdapter.current(function (data) {
4967
+ self.trigger('selection:update', {
4968
+ data: data
4969
+ });
4970
+ });
4971
+ });
4972
+
4973
+ this._sync = Utils.bind(this._syncAttributes, this);
4974
+
4975
+ if (this.$element[0].attachEvent) {
4976
+ this.$element[0].attachEvent('onpropertychange', this._sync);
4977
+ }
4978
+
4979
+ var observer = window.MutationObserver ||
4980
+ window.WebKitMutationObserver ||
4981
+ window.MozMutationObserver
4982
+ ;
4983
+
4984
+ if (observer != null) {
4985
+ this._observer = new observer(function (mutations) {
4986
+ $.each(mutations, self._sync);
4987
+ });
4988
+ this._observer.observe(this.$element[0], {
4989
+ attributes: true,
4990
+ subtree: false
4991
+ });
4992
+ } else if (this.$element[0].addEventListener) {
4993
+ this.$element[0].addEventListener('DOMAttrModified', self._sync, false);
4994
+ }
4995
+ };
4996
+
4997
+ Select2.prototype._registerDataEvents = function () {
4998
+ var self = this;
4999
+
5000
+ this.dataAdapter.on('*', function (name, params) {
5001
+ self.trigger(name, params);
5002
+ });
5003
+ };
5004
+
5005
+ Select2.prototype._registerSelectionEvents = function () {
5006
+ var self = this;
5007
+ var nonRelayEvents = ['toggle'];
5008
+
5009
+ this.selection.on('toggle', function () {
5010
+ self.toggleDropdown();
5011
+ });
5012
+
5013
+ this.selection.on('*', function (name, params) {
5014
+ if ($.inArray(name, nonRelayEvents) !== -1) {
5015
+ return;
5016
+ }
5017
+
5018
+ self.trigger(name, params);
5019
+ });
5020
+ };
5021
+
5022
+ Select2.prototype._registerDropdownEvents = function () {
5023
+ var self = this;
5024
+
5025
+ this.dropdown.on('*', function (name, params) {
5026
+ self.trigger(name, params);
5027
+ });
5028
+ };
5029
+
5030
+ Select2.prototype._registerResultsEvents = function () {
5031
+ var self = this;
5032
+
5033
+ this.results.on('*', function (name, params) {
5034
+ self.trigger(name, params);
5035
+ });
5036
+ };
5037
+
5038
+ Select2.prototype._registerEvents = function () {
5039
+ var self = this;
5040
+
5041
+ this.on('open', function () {
5042
+ self.$container.addClass('select2-container--open');
5043
+ });
5044
+
5045
+ this.on('close', function () {
5046
+ self.$container.removeClass('select2-container--open');
5047
+ });
5048
+
5049
+ this.on('enable', function () {
5050
+ self.$container.removeClass('select2-container--disabled');
5051
+ });
5052
+
5053
+ this.on('disable', function () {
5054
+ self.$container.addClass('select2-container--disabled');
5055
+ });
5056
+
5057
+ this.on('focus', function () {
5058
+ self.$container.addClass('select2-container--focus');
5059
+ });
5060
+
5061
+ this.on('blur', function () {
5062
+ self.$container.removeClass('select2-container--focus');
5063
+ });
5064
+
5065
+ this.on('query', function (params) {
5066
+ if (!self.isOpen()) {
5067
+ self.trigger('open');
5068
+ }
5069
+
5070
+ this.dataAdapter.query(params, function (data) {
5071
+ self.trigger('results:all', {
5072
+ data: data,
5073
+ query: params
5074
+ });
5075
+ });
5076
+ });
5077
+
5078
+ this.on('query:append', function (params) {
5079
+ this.dataAdapter.query(params, function (data) {
5080
+ self.trigger('results:append', {
5081
+ data: data,
5082
+ query: params
5083
+ });
5084
+ });
5085
+ });
5086
+
5087
+ this.on('keypress', function (evt) {
5088
+ var key = evt.which;
5089
+
5090
+ if (self.isOpen()) {
5091
+ if (key === KEYS.ENTER) {
5092
+ self.trigger('results:select');
5093
+
5094
+ evt.preventDefault();
5095
+ } else if ((key === KEYS.SPACE && evt.ctrlKey)) {
5096
+ self.trigger('results:toggle');
5097
+
5098
+ evt.preventDefault();
5099
+ } else if (key === KEYS.UP) {
5100
+ self.trigger('results:previous');
5101
+
5102
+ evt.preventDefault();
5103
+ } else if (key === KEYS.DOWN) {
5104
+ self.trigger('results:next');
5105
+
5106
+ evt.preventDefault();
5107
+ } else if (key === KEYS.ESC || key === KEYS.TAB) {
5108
+ self.close();
5109
+
5110
+ evt.preventDefault();
5111
+ }
5112
+ } else {
5113
+ if (key === KEYS.ENTER || key === KEYS.SPACE ||
5114
+ ((key === KEYS.DOWN || key === KEYS.UP) && evt.altKey)) {
5115
+ self.open();
5116
+
5117
+ evt.preventDefault();
5118
+ }
5119
+ }
5120
+ });
5121
+ };
5122
+
5123
+ Select2.prototype._syncAttributes = function () {
5124
+ this.options.set('disabled', this.$element.prop('disabled'));
5125
+
5126
+ if (this.options.get('disabled')) {
5127
+ if (this.isOpen()) {
5128
+ this.close();
5129
+ }
5130
+
5131
+ this.trigger('disable');
5132
+ } else {
5133
+ this.trigger('enable');
5134
+ }
5135
+ };
5136
+
5137
+ /**
5138
+ * Override the trigger method to automatically trigger pre-events when
5139
+ * there are events that can be prevented.
5140
+ */
5141
+ Select2.prototype.trigger = function (name, args) {
5142
+ var actualTrigger = Select2.__super__.trigger;
5143
+ var preTriggerMap = {
5144
+ 'open': 'opening',
5145
+ 'close': 'closing',
5146
+ 'select': 'selecting',
5147
+ 'unselect': 'unselecting'
5148
+ };
5149
+
5150
+ if (name in preTriggerMap) {
5151
+ var preTriggerName = preTriggerMap[name];
5152
+ var preTriggerArgs = {
5153
+ prevented: false,
5154
+ name: name,
5155
+ args: args
5156
+ };
5157
+
5158
+ actualTrigger.call(this, preTriggerName, preTriggerArgs);
5159
+
5160
+ if (preTriggerArgs.prevented) {
5161
+ args.prevented = true;
5162
+
5163
+ return;
5164
+ }
5165
+ }
5166
+
5167
+ actualTrigger.call(this, name, args);
5168
+ };
5169
+
5170
+ Select2.prototype.toggleDropdown = function () {
5171
+ if (this.options.get('disabled')) {
5172
+ return;
5173
+ }
5174
+
5175
+ if (this.isOpen()) {
5176
+ this.close();
5177
+ } else {
5178
+ this.open();
5179
+ }
5180
+ };
5181
+
5182
+ Select2.prototype.open = function () {
5183
+ if (this.isOpen()) {
5184
+ return;
5185
+ }
5186
+
5187
+ this.trigger('query', {});
5188
+
5189
+ this.trigger('open');
5190
+ };
5191
+
5192
+ Select2.prototype.close = function () {
5193
+ if (!this.isOpen()) {
5194
+ return;
5195
+ }
5196
+
5197
+ this.trigger('close');
5198
+ };
5199
+
5200
+ Select2.prototype.isOpen = function () {
5201
+ return this.$container.hasClass('select2-container--open');
5202
+ };
5203
+
5204
+ Select2.prototype.enable = function (args) {
5205
+ if (this.options.get('debug') && window.console && console.warn) {
5206
+ console.warn(
5207
+ 'Select2: The `select2("enable")` method has been deprecated and will' +
5208
+ ' be removed in later Select2 versions. Use $element.prop("disabled")' +
5209
+ ' instead.'
5210
+ );
5211
+ }
5212
+
5213
+ if (args == null || args.length === 0) {
5214
+ args = [true];
5215
+ }
5216
+
5217
+ var disabled = !args[0];
5218
+
5219
+ this.$element.prop('disabled', disabled);
5220
+ };
5221
+
5222
+ Select2.prototype.data = function () {
5223
+ if (this.options.get('debug') &&
5224
+ arguments.length > 0 && window.console && console.warn) {
5225
+ console.warn(
5226
+ 'Select2: Data can no longer be set using `select2("data")`. You ' +
5227
+ 'should consider setting the value instead using `$element.val()`.'
5228
+ );
5229
+ }
5230
+
5231
+ var data = [];
5232
+
5233
+ this.dataAdapter.current(function (currentData) {
5234
+ data = currentData;
5235
+ });
5236
+
5237
+ return data;
5238
+ };
5239
+
5240
+ Select2.prototype.val = function (args) {
5241
+ if (this.options.get('debug') && window.console && console.warn) {
5242
+ console.warn(
5243
+ 'Select2: The `select2("val")` method has been deprecated and will be' +
5244
+ ' removed in later Select2 versions. Use $element.val() instead.'
5245
+ );
5246
+ }
5247
+
5248
+ if (args == null || args.length === 0) {
5249
+ return this.$element.val();
5250
+ }
5251
+
5252
+ var newVal = args[0];
5253
+
5254
+ if ($.isArray(newVal)) {
5255
+ newVal = $.map(newVal, function (obj) {
5256
+ return obj.toString();
5257
+ });
5258
+ }
5259
+
5260
+ this.$element.val(newVal).trigger('change');
5261
+ };
5262
+
5263
+ Select2.prototype.destroy = function () {
5264
+ this.$container.remove();
5265
+
5266
+ if (this.$element[0].detachEvent) {
5267
+ this.$element[0].detachEvent('onpropertychange', this._sync);
5268
+ }
5269
+
5270
+ if (this._observer != null) {
5271
+ this._observer.disconnect();
5272
+ this._observer = null;
5273
+ } else if (this.$element[0].removeEventListener) {
5274
+ this.$element[0]
5275
+ .removeEventListener('DOMAttrModified', this._sync, false);
5276
+ }
5277
+
5278
+ this._sync = null;
5279
+
5280
+ this.$element.off('.select2');
5281
+ this.$element.attr('tabindex', this.$element.data('old-tabindex'));
5282
+
5283
+ this.$element.removeClass('select2-hidden-accessible');
5284
+ this.$element.attr('aria-hidden', 'false');
5285
+ this.$element.removeData('select2');
5286
+
5287
+ this.dataAdapter.destroy();
5288
+ this.selection.destroy();
5289
+ this.dropdown.destroy();
5290
+ this.results.destroy();
5291
+
5292
+ this.dataAdapter = null;
5293
+ this.selection = null;
5294
+ this.dropdown = null;
5295
+ this.results = null;
5296
+ };
5297
+
5298
+ Select2.prototype.render = function () {
5299
+ var $container = $(
5300
+ '<span class="select2 select2-container">' +
5301
+ '<span class="selection"></span>' +
5302
+ '<span class="dropdown-wrapper" aria-hidden="true"></span>' +
5303
+ '</span>'
5304
+ );
5305
+
5306
+ $container.attr('dir', this.options.get('dir'));
5307
+
5308
+ this.$container = $container;
5309
+
5310
+ this.$container.addClass('select2-container--' + this.options.get('theme'));
5311
+
5312
+ $container.data('element', this.$element);
5313
+
5314
+ return $container;
5315
+ };
5316
+
5317
+ return Select2;
5318
+ });
5319
+
5320
+ S2.define('jquery.select2',[
5321
+ 'jquery',
5322
+ 'require',
5323
+
5324
+ './select2/core',
5325
+ './select2/defaults'
5326
+ ], function ($, require, Select2, Defaults) {
5327
+ // Force jQuery.mousewheel to be loaded if it hasn't already
5328
+ require('jquery.mousewheel');
5329
+
5330
+ if ($.fn.select2 == null) {
5331
+ // All methods that should return the element
5332
+ var thisMethods = ['open', 'close', 'destroy'];
5333
+
5334
+ $.fn.select2 = function (options) {
5335
+ options = options || {};
5336
+
5337
+ if (typeof options === 'object') {
5338
+ this.each(function () {
5339
+ var instanceOptions = $.extend({}, options, true);
5340
+
5341
+ var instance = new Select2($(this), instanceOptions);
5342
+ });
5343
+
5344
+ return this;
5345
+ } else if (typeof options === 'string') {
5346
+ var instance = this.data('select2');
5347
+
5348
+ if (instance == null && window.console && console.error) {
5349
+ console.error(
5350
+ 'The select2(\'' + options + '\') method was called on an ' +
5351
+ 'element that is not using Select2.'
5352
+ );
5353
+ }
5354
+
5355
+ var args = Array.prototype.slice.call(arguments, 1);
5356
+
5357
+ var ret = instance[options](args);
5358
+
5359
+ // Check if we should be returning `this`
5360
+ if ($.inArray(options, thisMethods) > -1) {
5361
+ return this;
5362
+ }
5363
+
5364
+ return ret;
5365
+ } else {
5366
+ throw new Error('Invalid arguments for Select2: ' + options);
5367
+ }
5368
+ };
5369
+ }
5370
+
5371
+ if ($.fn.select2.defaults == null) {
5372
+ $.fn.select2.defaults = Defaults;
5373
+ }
5374
+
5375
+ return Select2;
5376
+ });
5377
+
5378
+ S2.define('jquery.mousewheel',[
5379
+ 'jquery'
5380
+ ], function ($) {
5381
+ // Used to shim jQuery.mousewheel for non-full builds.
5382
+ return $;
5383
+ });
5384
+
5385
+ // Return the AMD loader configuration so it can be used outside of this file
5386
+ return {
5387
+ define: S2.define,
5388
+ require: S2.require
5389
+ };
5390
+ }());
5391
+
5392
+ // Autoload the jQuery bindings
5393
+ // We know that all of the modules exist above this, so we're safe
5394
+ var select2 = S2.require('jquery.select2');
5395
+
5396
+ // Hold the AMD module references on the jQuery function that was just loaded
5397
+ // This allows Select2 to use the internal loader outside of this file, such
5398
+ // as in the language files.
5399
+ jQuery.fn.select2.amd = S2;
5400
+
5401
+ // Return the Select2 instance for anyone who is importing it.
5402
+ return select2;
5403
+ }));
assets/js/vendor/select2.min.js ADDED
@@ -0,0 +1,2 @@
 
 
1
+ /*! Select2 4.0.0 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(n=n.slice(0,n.length-1),a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.concat(a),k=0;k<a.length;k+=1)if(m=a[k],"."===m)a.splice(k,1),k-=1;else if(".."===m){if(1===k&&(".."===a[2]||".."===a[0]))break;k>0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){return n.apply(b,v.call(arguments,0).concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n<c.length;n+=1)if(m=o(c[n],f),k=m.f,"require"===k)u[n]=p.require(a);else if("exports"===k)u[n]=p.exports(a),s=!0;else if("module"===k)h=u[n]=p.module(a);else if(e(q,k)||e(r,k)||e(t,k))u[n]=j(k);else{if(!m.p)throw new Error(a+" missing "+k);m.p.load(m.n,g(f,!0),i(k),{}),u[n]=q[k]}l=d?d.apply(q[a],u):void 0,a&&(h&&h.exports!==b&&h.exports!==q[a]?q[a]=h.exports:l===b&&s||(q[a]=l))}else a&&(q[a]=d)},a=c=n=function(a,c,d,e,f){if("string"==typeof a)return p[a]?p[a](c):j(o(a,c).f);if(!a.splice){if(s=a,s.deps&&n(s.deps,s.callback),!c)return;c.splice?(a=c,c=d,d=null):a=b}return c=c||function(){},"function"==typeof d&&(d=e,e=f),e?m(b,a,c,d):setTimeout(function(){m(b,a,c,d)},4),n},n.config=function(a){return n(a)},a._defined=q,d=function(a,b,c){b.splice||(c=b,b=[]),e(q,a)||e(r,a)||(r[a]=[a,b,c])},d.amd={jQuery:!0}}(),b.requirejs=a,b.require=c,b.define=d}}(),b.define("almond",function(){}),b.define("jquery",[],function(){var b=a||$;return null==b&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),b}),b.define("select2/utils",["jquery"],function(a){function b(a){var b=a.prototype,c=[];for(var d in b){var e=b[d];"function"==typeof e&&"constructor"!==d&&c.push(d)}return c}var c={};c.Extend=function(a,b){function c(){this.constructor=a}var d={}.hasOwnProperty;for(var e in b)d.call(b,e)&&(a[e]=b[e]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},c.Decorate=function(a,c){function d(){var b=Array.prototype.unshift,d=c.prototype.constructor.length,e=a.prototype.constructor;d>0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;h<g.length;h++){var i=g[h];d.prototype[i]=a.prototype[i]}for(var j=(function(a){var b=function(){};a in d.prototype&&(b=d.prototype[a]);var e=c.prototype[a];return function(){var a=Array.prototype.unshift;return a.call(arguments,b),e.apply(this,arguments)}}),k=0;k<f.length;k++){var l=f[k];d.prototype[l]=j(l)}return d};var d=function(){this.listeners={}};return d.prototype.on=function(a,b){this.listeners=this.listeners||{},a in this.listeners?this.listeners[a].push(b):this.listeners[a]=[b]},d.prototype.trigger=function(a){var b=Array.prototype.slice;this.listeners=this.listeners||{},a in this.listeners&&this.invoke(this.listeners[a],b.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},d.prototype.invoke=function(a,b){for(var c=0,d=a.length;d>c;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e<c.length;e++){var f=c[e];f=f.substring(0,1).toLowerCase()+f.substring(1),f in d||(d[f]={}),e==c.length-1&&(d[f]=a[b]),d=d[f]}delete a[b]}}return a},c.hasScroll=function(b,c){var d=a(c),e=c.style.overflowX,f=c.style.overflowY;return e!==f||"hidden"!==f&&"visible"!==f?"scroll"===e||"scroll"===f?!0:d.innerHeight()<c.scrollHeight||d.innerWidth()<c.scrollWidth:!1},c.escapeMarkup=function(a){var b={"\\":"&#92;","&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#47;"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<ul class="select2-results__options" role="tree"></ul>');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('<li role="treeitem" class="select2-results__option"></li>'),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),this.$results.append(d)},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c<a.results.length;c++){var d=a.results[c],e=this.option(d);b.push(e)}this.$results.append(b)},c.prototype.position=function(a,b){var c=b.find(".select2-results");c.append(a)},c.prototype.sort=function(a){var b=this.options.get("sorter");return b(a)},c.prototype.setClasses=function(){var b=this;this.data.current(function(c){var d=a.map(c,function(a){return a.id.toString()}),e=b.$results.find(".select2-results__option[aria-selected]");e.each(function(){var b=a(this),c=a.data(this,"data"),e=""+c.id;null!=c.element&&c.element.selected||null==c.element&&a.inArray(e,d)>-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")});var f=e.filter("[aria-selected=true]");f.length>0?f.first().trigger("mouseenter"):e.first().trigger("mouseenter")})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";{a(h)}this.template(b,h);for(var i=[],j=0;j<b.children.length;j++){var k=b.children[j],l=this.option(k);i.push(l)}var m=a("<ul></ul>",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b){var c=this,d=b.id+"-results";this.$results.attr("id",d),b.on("results:all",function(a){c.clear(),c.append(a.data),b.isOpen()&&c.setClasses()}),b.on("results:append",function(a){c.append(a.data),b.isOpen()&&c.setClasses()}),b.on("query",function(a){c.showLoading(a)}),b.on("select",function(){b.isOpen()&&c.setClasses()}),b.on("unselect",function(){b.isOpen()&&c.setClasses()}),b.on("open",function(){c.$results.attr("aria-expanded","true"),c.$results.attr("aria-hidden","false"),c.setClasses(),c.ensureHighlightVisible()}),b.on("close",function(){c.$results.attr("aria-expanded","false"),c.$results.attr("aria-hidden","true"),c.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=c.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=c.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?c.trigger("close"):c.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=c.getHighlightedResults(),b=c.$results.find("[aria-selected]"),d=b.index(a);if(0!==d){var e=d-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=c.$results.offset().top,h=f.offset().top,i=c.$results.scrollTop()+(h-g);0===e?c.$results.scrollTop(0):0>h-g&&c.$results.scrollTop(i)}}),b.on("results:next",function(){var a=c.getHighlightedResults(),b=c.$results.find("[aria-selected]"),d=b.index(a),e=d+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=c.$results.offset().top+c.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=c.$results.scrollTop()+h-g;0===e?c.$results.scrollTop(0):h>g&&c.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){c.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=c.$results.scrollTop(),d=c.$results.get(0).scrollHeight-c.$results.scrollTop()+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&d<=c.$results.height();e?(c.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(c.$results.scrollTop(c.$results.get(0).scrollHeight-c.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var d=a(this),e=d.data("data");return"true"===d.attr("aria-selected")?void(c.options.get("multiple")?c.trigger("unselect",{originalEvent:b,data:e}):c.trigger("close")):void c.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(){var b=a(this).data("data");c.getHighlightedResults().removeClass("select2-results__option--highlighted"),c.trigger("results:focus",{data:b,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('<span class="select2-selection" role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-expanded="false"></span>');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a){var b=this,d=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){b.trigger("focus",a)}),this.$selection.on("blur",function(a){b.trigger("blur",a)}),this.$selection.on("keydown",function(a){b.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){b.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){b.update(a.data)}),a.on("open",function(){b.$selection.attr("aria-expanded","true"),b.$selection.attr("aria-owns",d),b._attachCloseHandler(a)}),a.on("close",function(){b.$selection.attr("aria-expanded","false"),b.$selection.removeAttr("aria-activedescendant"),b.$selection.removeAttr("aria-owns"),b.$selection.focus(),b._detachCloseHandler(a)}),a.on("enable",function(){b.$selection.attr("tabindex",b._tabindex)}),a.on("disable",function(){b.$selection.attr("tabindex","-1")})},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c){function d(){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html('<span class="select2-selection__rendered"></span><span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>'),a},d.prototype.bind=function(a){var b=this;d.__super__.bind.apply(this,arguments);var c=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",c),this.$selection.attr("aria-labelledby",c),this.$selection.on("mousedown",function(a){1===a.which&&b.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(){}),this.$selection.on("blur",function(){}),a.on("selection:update",function(a){b.update(a.data)})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a){var b=this.options.get("templateSelection"),c=this.options.get("escapeMarkup");return c(b(a))},d.prototype.selectionContainer=function(){return a("<span></span>")},d.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.display(b),d=this.$selection.find(".select2-selection__rendered");d.empty().append(c),d.prop("title",b.title||b.text)},d}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('<ul class="select2-selection__rendered"></ul>'),a},d.prototype.bind=function(){var b=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){b.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(c){var d=a(this),e=d.parent(),f=e.data("data");b.trigger("unselect",{originalEvent:c,data:f})})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a){var b=this.options.get("templateSelection"),c=this.options.get("escapeMarkup");return c(b(a))},d.prototype.selectionContainer=function(){var b=a('<li class="select2-selection__choice"><span class="select2-selection__choice__remove" role="presentation">&times;</span></li>');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d<a.length;d++){var e=a[d],f=this.display(e),g=this.selectionContainer();g.append(f),g.prop("title",e.title||e.text),g.data("data",e),b.push(g)}var h=this.$selection.find(".select2-selection__rendered");c.appendMany(h,b)}},d}),b.define("select2/selection/placeholder",["../utils"],function(){function a(a,b,c){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c)}return a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.createPlaceholder=function(a,b){var c=this.selectionContainer();return c.html(this.display(b)),c.addClass("select2-selection__placeholder").removeClass("select2-selection__choice"),c},a.prototype.update=function(a,b){var c=1==b.length&&b[0].id!=this.placeholder.id,d=b.length>1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},a}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e<d.length;e++){var f={data:d[e]};if(this.trigger("unselect",f),f.prevented)return}this.$element.val(this.placeholder.id).trigger("change"),this.trigger("toggle")}}},c.prototype._handleKeyboardClear=function(a,c,d){d.isOpen()||(c.which==b.DELETE||c.which==b.BACKSPACE)&&this._handleClear(c)},c.prototype.update=function(b,c){if(b.call(this,c),!(this.$selection.find(".select2-selection__placeholder").length>0||0===c.length)){var d=a('<span class="select2-selection__clear">&times;</span>');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('<li class="select2-search select2-search--inline"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></li>');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus()}),b.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val(""),e.$search.focus()}),b.on("enable",function(){e.$search.prop("disabled",!1)}),b.on("disable",function(){e.$search.prop("disabled",!0)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e.trigger("blur",a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}}),this.$selection.on("input",".select2-search--inline",function(){e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input",".select2-search--inline",function(a){e.handleSearch(a)})},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.trigger("open"),this.$search.val(b.text+" ")},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f<a.length;f++){var g=a[f].id;-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")});else{var d=a.id;this.$element.val(d),this.$element.trigger("change")}},d.prototype.unselect=function(a){var b=this;if(this.$element.prop("multiple"))return a.selected=!1,c(a.element).is("option")?(a.element.selected=!1,void this.$element.trigger("change")):void this.current(function(d){for(var e=[],f=0;f<d.length;f++){var g=d[f].id;g!==a.id&&-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")})},d.prototype.bind=function(a){var b=this;this.container=a,a.on("select",function(a){b.select(a.data)}),a.on("unselect",function(a){b.unselect(a.data)})},d.prototype.destroy=function(){this.$element.find("*").each(function(){c.removeData(this,"data")})},d.prototype.query=function(a,b){var d=[],e=this,f=this.$element.children();f.each(function(){var b=c(this);if(b.is("option")||b.is("optgroup")){var f=e.item(b),g=e.matches(a,f);null!==g&&d.push(g)}}),b({results:d})},d.prototype.addOptions=function(a){b.appendMany(this.$element,a)},d.prototype.option=function(a){var b;a.children?(b=document.createElement("optgroup"),b.label=a.text):(b=document.createElement("option"),void 0!==b.textContent?b.textContent=a.text:b.innerText=a.text),a.id&&(b.value=a.id),a.disabled&&(b.disabled=!0),a.selected&&(b.selected=!0),a.title&&(b.title=a.title);var d=c(b),e=this._normalizeItem(a);return e.element=b,c.data(b,"data",e),d},d.prototype.item=function(a){var b={};
2
+ if(b=c.data(a[0],"data"),null!=b)return b;if(a.is("option"))b={id:a.val(),text:a.text(),disabled:a.prop("disabled"),selected:a.prop("selected"),title:a.prop("title")};else if(a.is("optgroup")){b={text:a.prop("label"),children:[],title:a.prop("title")};for(var d=a.children("option"),e=[],f=0;f<d.length;f++){var g=c(d[f]),h=this.item(g);e.push(h)}b.children=e}return b=this._normalizeItem(b),b.element=a[0],c.data(a[0],"data",b),b},d.prototype._normalizeItem=function(a){c.isPlainObject(a)||(a={id:a,text:a}),a=c.extend({},{text:""},a);var b={selected:!1,disabled:!1};return null!=a.id&&(a.id=a.id.toString()),null!=a.text&&(a.text=a.text.toString()),null==a._resultId&&a.id&&null!=this.container&&(a._resultId=this.generateResultId(this.container,a)),c.extend({},b,a)},d.prototype.matches=function(a,b){var c=this.options.get("matcher");return c(a,b)},d}),b.define("select2/data/array",["./select","../utils","jquery"],function(a,b,c){function d(a,b){var c=b.get("data")||[];d.__super__.constructor.call(this,a,b),this.addOptions(this.convertToOptions(c))}return b.Extend(d,a),d.prototype.select=function(a){var b=this.$element.find("option").filter(function(b,c){return c.value==a.id.toString()});0===b.length&&(b=this.option(a),this.addOptions(b)),d.__super__.select.call(this,a)},d.prototype.convertToOptions=function(a){function d(a){return function(){return c(this).val()==a.id}}for(var e=this,f=this.$element.find("option"),g=f.map(function(){return e.item(c(this)).id}).get(),h=[],i=0;i<a.length;i++){var j=this._normalizeItem(a[i]);if(c.inArray(j.id,g)>=0){var k=f.filter(d(j)),l=this.item(k),m=(c.extend(!0,{},l,j),this.option(l));k.replaceWith(m)}else{var n=this.option(j);if(j.children){var o=this.convertToOptions(j.children);b.appendMany(n,o)}h.push(n)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(b,c){this.ajaxOptions=this._applyDefaults(c.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),a.__super__.constructor.call(this,b,c)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return{q:a.term}},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url(a)),"function"==typeof f.data&&(f.data=f.data(a)),this.ajaxOptions.delay&&""!==a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");if(void 0!==f&&(this.createTag=f),b.call(this,c,d),a.isArray(e))for(var g=0;g<e.length;g++){var h=e[g],i=this._normalizeItem(h),j=this.option(i);this.$element.append(j)}}return b.prototype.query=function(a,b,c){function d(a,f){for(var g=a.results,h=0;h<g.length;h++){var i=g[h],j=null!=i.children&&!d({results:i.children},!0),k=i.text===b.term;if(k||j)return f?!1:(a.data=g,void c(a))}if(f)return!0;var l=e.createTag(b);if(null!=l){var m=e.option(l);m.attr("data-select2-tag",!0),e.addOptions([m]),e.insertTag(g,l)}a.results=g,c(a)}var e=this;return this._removeOldTags(),null==b.term||null!=b.page?void a.call(this,b,c):void a.call(this,b,d)},b.prototype.createTag=function(b,c){var d=a.trim(c.term);return""===d?null:{id:d,text:d}},b.prototype.insertTag=function(a,b,c){b.unshift(c)},b.prototype._removeOldTags=function(){var b=(this._lastTag,this.$element.find("option[data-select2-tag]"));b.each(function(){this.selected||a(this).remove()})},b}),b.define("select2/data/tokenizer",["jquery"],function(a){function b(a,b,c){var d=c.get("tokenizer");void 0!==d&&(this.tokenizer=d),a.call(this,b,c)}return b.prototype.bind=function(a,b,c){a.call(this,b,c),this.$search=b.dropdown.$search||b.selection.$search||c.find(".select2-search__field")},b.prototype.query=function(a,b,c){function d(a){e.select(a)}var e=this;b.term=b.term||"";var f=this.tokenizer(b,this.options,d);f.term!==b.term&&(this.$search.length&&(this.$search.val(f.term),this.$search.focus()),b.term=f.term),a.call(this,b,c)},b.prototype.tokenizer=function(b,c,d,e){for(var f=d.get("tokenSeparators")||[],g=c.term,h=0,i=this.createTag||function(a){return{id:a.term,text:a.term}};h<g.length;){var j=g[h];if(-1!==a.inArray(j,f)){var k=g.substr(0,h),l=a.extend({},c,{term:k}),m=i(l);e(m),g=g.substr(h+1)||"",h=0}else h++}return{term:g}},b}),b.define("select2/data/minimumInputLength",[],function(){function a(a,b,c){this.minimumInputLength=c.get("minimumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",b.term.length<this.minimumInputLength?void this.trigger("results:message",{message:"inputTooShort",args:{minimum:this.minimumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumInputLength",[],function(){function a(a,b,c){this.maximumInputLength=c.get("maximumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",this.maximumInputLength>0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<span class="select2-dropdown"><span class="select2-results"></span></span>');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.position=function(){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a){function b(){}return b.prototype.render=function(b){var c=b.call(this),d=a('<span class="select2-search select2-search--dropdown"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></span>');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},b.prototype.handleSearch=function(){if(!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},b.prototype.showSearch=function(){return!0},b}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('<li class="option load-more" role="treeitem"></li>'),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(a,b,c){this.$dropdownParent=c.get("dropdownParent")||document.body,a.call(this,b,c)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a("<span></span>"),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c){var d=this,e="scroll.select2."+c.id,f="resize.select2."+c.id,g="orientationchange.select2."+c.id,h=this.$container.parents().filter(b.hasScroll);h.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),h.on(e,function(){var b=a(this).data("select2-scroll-position");a(this).scrollTop(b.y)}),a(window).on(e+" "+f+" "+g,function(){d._positionDropdown(),d._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c){var d="scroll.select2."+c.id,e="resize.select2."+c.id,f="orientationchange.select2."+c.id,g=this.$container.parents().filter(b.hasScroll);g.off(d),a(window).off(d+" "+e+" "+f)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=(this.$container.position(),this.$container.offset());f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.top<f.top-h.height,k=i.bottom>f.bottom+h.height,l={left:f.left,top:g.bottom};c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){this.$dropdownContainer.width();var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d<b.length;d++){var e=b[d];e.children?c+=a(e.children):c++}return c}function b(a,b,c,d){this.minimumResultsForSearch=c.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),a.call(this,b,c,d)}return b.prototype.showSearch=function(b,c){return a(c.data.results)<this.minimumResultsForSearch?!1:b.call(this,c)},b}),b.define("select2/dropdown/selectOnClose",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("close",function(){d._handleSelectOnClose()})},a.prototype._handleSelectOnClose=function(){var a=this.getHighlightedResults();a.length<1||this.trigger("select",{data:a.data("data")})},a}),b.define("select2/dropdown/closeOnSelect",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("select",function(a){d._selectTriggered(a)}),b.on("unselect",function(a){d._selectTriggered(a)})},a.prototype._selectTriggered=function(a,b){var c=b.originalEvent;c&&c.ctrlKey||this.trigger("close")},a}),b.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(a){var b=a.input.length-a.maximum,c="Please delete "+b+" character";return 1!=b&&(c+="s"),c},inputTooShort:function(a){var b=a.minimum-a.input.length,c="Please enter "+b+" or more characters";return c},loadingMore:function(){return"Loading more results…"},maximumSelected:function(a){var b="You can only select "+a.maximum+" item";return 1!=a.maximum&&(b+="s"),b},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),b.define("select2/defaults",["jquery","require","./results","./selection/single","./selection/multiple","./selection/placeholder","./selection/allowClear","./selection/search","./selection/eventRelay","./utils","./translation","./diacritics","./data/select","./data/array","./data/ajax","./data/tags","./data/tokenizer","./data/minimumInputLength","./data/maximumInputLength","./data/maximumSelectionLength","./dropdown","./dropdown/search","./dropdown/hidePlaceholder","./dropdown/infiniteScroll","./dropdown/attachBody","./dropdown/minimumResultsForSearch","./dropdown/selectOnClose","./dropdown/closeOnSelect","./i18n/en"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C){function D(){this.reset()}D.prototype.apply=function(l){if(l=a.extend({},this.defaults,l),null==l.dataAdapter){if(l.dataAdapter=null!=l.ajax?o:null!=l.data?n:m,l.minimumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.selectionAdapter=l.multiple?e:d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L<K.length;L++){var M=K[L],N={};try{N=k.loadPath(M)}catch(O){try{M=this.defaults.amdLanguageBase+M,N=k.loadPath(M)}catch(P){l.debug&&window.console&&console.warn&&console.warn('Select2: The language file for "'+M+'" could not be automatically loaded. A fallback will be used instead.');continue}}J.extend(N)}l.translations=J}else{var Q=k.loadPath(this.defaults.amdLanguageBase+"en"),R=new k(l.language);R.extend(Q),l.translations=R}return l},D.prototype.reset=function(){function b(a){function b(a){return l[a]||a}return a.replace(/[^\u0000-\u007E]/g,b)}function c(d,e){if(""===a.trim(d.term))return e;if(e.children&&e.children.length>0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(this.options.dir=a.prop("dir")?a.prop("dir"):a.closest("[dir]").prop("dir")?a.closest("[dir]").prop("dir"):"ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this._sync=c.bind(this._syncAttributes,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._sync);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._sync)}),this._observer.observe(this.$element[0],{attributes:!0,subtree:!1})):this.$element[0].addEventListener&&this.$element[0].addEventListener("DOMAttrModified",b._sync,!1)},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("focus",function(){a.$container.addClass("select2-container--focus")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open"),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ENTER?(a.trigger("results:select"),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle"),b.preventDefault()):c===d.UP?(a.trigger("results:previous"),b.preventDefault()):c===d.DOWN?(a.trigger("results:next"),b.preventDefault()):(c===d.ESC||c===d.TAB)&&(a.close(),b.preventDefault()):(c===d.ENTER||c===d.SPACE||(c===d.DOWN||c===d.UP)&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable")):this.trigger("enable")},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||(this.trigger("query",{}),this.trigger("open"))},e.prototype.close=function(){this.isOpen()&&this.trigger("close")},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._sync),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&this.$element[0].removeEventListener("DOMAttrModified",this._sync,!1),this._sync=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},e.prototype.render=function(){var b=a('<span class="select2 select2-container"><span class="selection"></span><span class="dropdown-wrapper" aria-hidden="true"></span></span>');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("jquery.select2",["jquery","require","./select2/core","./select2/defaults"],function(a,b,c,d){if(b("jquery.mousewheel"),null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){{var d=a.extend({},b,!0);new c(a(this),d)}}),this;if("string"==typeof b){var d=this.data("select2");null==d&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2.");var f=Array.prototype.slice.call(arguments,1),g=d[b](f);return a.inArray(b,e)>-1?this:g}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),b.define("jquery.mousewheel",["jquery"],function(a){return a}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c});
assets/js/vendor/tiptip.js ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * TipTip
3
+ * Copyright 2010 Drew Wilson
4
+ * www.drewwilson.com
5
+ * code.drewwilson.com/entry/tiptip-jquery-plugin
6
+ *
7
+ * Version 1.3 - Updated: Mar. 23, 2010
8
+ *
9
+ * This Plug-In will create a custom tooltip to replace the default
10
+ * browser tooltip. It is extremely lightweight and very smart in
11
+ * that it detects the edges of the browser window and will make sure
12
+ * the tooltip stays within the current window size. As a result the
13
+ * tooltip will adjust itself to be displayed above, below, to the left
14
+ * or to the right depending on what is necessary to stay within the
15
+ * browser window. It is completely customizable as well via CSS.
16
+ *
17
+ * This TipTip jQuery plug-in is dual licensed under the MIT and GPL licenses:
18
+ * http://www.opensource.org/licenses/mit-license.php
19
+ * http://www.gnu.org/licenses/gpl.html
20
+ */
21
+
22
+ (function($){
23
+ $.fn.tipTip = function(options) {
24
+ var defaults = {
25
+ activation: "hover",
26
+ keepAlive: false,
27
+ maxWidth: "200px",
28
+ edgeOffset: 3,
29
+ defaultPosition: "bottom",
30
+ delay: 400,
31
+ fadeIn: 200,
32
+ fadeOut: 200,
33
+ attribute: "title",
34
+ content: false, // HTML or String to fill TipTIp with
35
+ enter: function(){},
36
+ exit: function(){}
37
+ };
38
+ var opts = $.extend(defaults, options);
39
+
40
+ // Setup tip tip elements and render them to the DOM
41
+ if($("#tiptip_holder").length <= 0){
42
+ var tiptip_holder = $('<div id="tiptip_holder" style="max-width:'+ opts.maxWidth +';"></div>');
43
+ var tiptip_content = $('<div id="tiptip_content"></div>');
44
+ var tiptip_arrow = $('<div id="tiptip_arrow"></div>');
45
+ $("body").append(tiptip_holder.html(tiptip_content).prepend(tiptip_arrow.html('<div id="tiptip_arrow_inner"></div>')));
46
+ } else {
47
+ var tiptip_holder = $("#tiptip_holder");
48
+ var tiptip_content = $("#tiptip_content");
49
+ var tiptip_arrow = $("#tiptip_arrow");
50
+ }
51
+
52
+ return this.each(function(){
53
+ var org_elem = $(this);
54
+ if(opts.content){
55
+ var org_title = opts.content;
56
+ } else {
57
+ var org_title = org_elem.attr(opts.attribute);
58
+ }
59
+ if(org_title != ""){
60
+ if(!opts.content){
61
+ org_elem.removeAttr(opts.attribute); //remove original Attribute
62
+ }
63
+ var timeout = false;
64
+
65
+ if(opts.activation == "hover"){
66
+ org_elem.hover(function(){
67
+ active_tiptip();
68
+ }, function(){
69
+ if(!opts.keepAlive){
70
+ deactive_tiptip();
71
+ }
72
+ });
73
+ if(opts.keepAlive){
74
+ tiptip_holder.hover(function(){}, function(){
75
+ deactive_tiptip();
76
+ });
77
+ }
78
+ } else if(opts.activation == "focus"){
79
+ org_elem.focus(function(){
80
+ active_tiptip();
81
+ }).blur(function(){
82
+ deactive_tiptip();
83
+ });
84
+ } else if(opts.activation == "click"){
85
+ org_elem.click(function(){
86
+ active_tiptip();
87
+ return false;
88
+ }).hover(function(){},function(){
89
+ if(!opts.keepAlive){
90
+ deactive_tiptip();
91
+ }
92
+ });
93
+ if(opts.keepAlive){
94
+ tiptip_holder.hover(function(){}, function(){
95
+ deactive_tiptip();
96
+ });
97
+ }
98
+ }
99
+
100
+ function active_tiptip(){
101
+ opts.enter.call(this);
102
+ tiptip_content.html(org_title);
103
+ tiptip_holder.hide().removeAttr("class").css("margin","0");
104
+ tiptip_arrow.removeAttr("style");
105
+
106
+ var top = parseInt(org_elem.offset()['top']);
107
+ var left = parseInt(org_elem.offset()['left']);
108
+ var org_width = parseInt(org_elem.outerWidth());
109
+ var org_height = parseInt(org_elem.outerHeight());
110
+ var tip_w = tiptip_holder.outerWidth();
111
+ var tip_h = tiptip_holder.outerHeight();
112
+ var w_compare = Math.round((org_width - tip_w) / 2);
113
+ var h_compare = Math.round((org_height - tip_h) / 2);
114
+ var marg_left = Math.round(left + w_compare);
115
+ var marg_top = Math.round(top + org_height + opts.edgeOffset);
116
+ var t_class = "";
117
+ var arrow_top = "";
118
+ var arrow_left = Math.round(tip_w - 12) / 2;
119
+
120
+ if(opts.defaultPosition == "bottom"){
121
+ t_class = "_bottom";
122
+ } else if(opts.defaultPosition == "top"){
123
+ t_class = "_top";
124
+ } else if(opts.defaultPosition == "left"){
125
+ t_class = "_left";
126
+ } else if(opts.defaultPosition == "right"){
127
+ t_class = "_right";
128
+ }
129
+
130
+ var right_compare = (w_compare + left) < parseInt($(window).scrollLeft());
131
+ var left_compare = (tip_w + left) > parseInt($(window).width());
132
+
133
+ if((right_compare && w_compare < 0) || (t_class == "_right" && !left_compare) || (t_class == "_left" && left < (tip_w + opts.edgeOffset + 5))){
134
+ t_class = "_right";
135
+ arrow_top = Math.round(tip_h - 13) / 2;
136
+ arrow_left = -12;
137
+ marg_left = Math.round(left + org_width + opts.edgeOffset);
138
+ marg_top = Math.round(top + h_compare);
139
+ } else if((left_compare && w_compare < 0) || (t_class == "_left" && !right_compare)){
140
+ t_class = "_left";
141
+ arrow_top = Math.round(tip_h - 13) / 2;
142
+ arrow_left = Math.round(tip_w);
143
+ marg_left = Math.round(left - (tip_w + opts.edgeOffset + 5));
144
+ marg_top = Math.round(top + h_compare);
145
+ }
146
+
147
+ var top_compare = (top + org_height + opts.edgeOffset + tip_h + 8) > parseInt($(window).height() + $(window).scrollTop());
148
+ var bottom_compare = ((top + org_height) - (opts.edgeOffset + tip_h + 8)) < 0;
149
+
150
+ if(top_compare || (t_class == "_bottom" && top_compare) || (t_class == "_top" && !bottom_compare)){
151
+ if(t_class == "_top" || t_class == "_bottom"){
152
+ t_class = "_top";
153
+ } else {
154
+ t_class = t_class+"_top";
155
+ }
156
+ arrow_top = tip_h;
157
+ marg_top = Math.round(top - (tip_h + 5 + opts.edgeOffset));
158
+ } else if(bottom_compare | (t_class == "_top" && bottom_compare) || (t_class == "_bottom" && !top_compare)){
159
+ if(t_class == "_top" || t_class == "_bottom"){
160
+ t_class = "_bottom";
161
+ } else {
162
+ t_class = t_class+"_bottom";
163
+ }
164
+ arrow_top = -12;
165
+ marg_top = Math.round(top + org_height + opts.edgeOffset);
166
+ }
167
+
168
+ if(t_class == "_right_top" || t_class == "_left_top"){
169
+ marg_top = marg_top + 5;
170
+ } else if(t_class == "_right_bottom" || t_class == "_left_bottom"){
171
+ marg_top = marg_top - 5;
172
+ }
173
+ if(t_class == "_left_top" || t_class == "_left_bottom"){
174
+ marg_left = marg_left + 5;
175
+ }
176
+ tiptip_arrow.css({"margin-left": arrow_left+"px", "margin-top": arrow_top+"px"});
177
+ tiptip_holder.css({"margin-left": marg_left+"px", "margin-top": marg_top+"px"}).attr("class","tip"+t_class);
178
+
179
+ if (timeout){ clearTimeout(timeout); }
180
+ timeout = setTimeout(function(){ tiptip_holder.stop(true,true).fadeIn(opts.fadeIn); }, opts.delay);
181
+ }
182
+
183
+ function deactive_tiptip(){
184
+ opts.exit.call(this);
185
+ if (timeout){ clearTimeout(timeout); }
186
+ tiptip_holder.fadeOut(opts.fadeOut);
187
+ }
188
+ }
189
+ });
190
+ }
191
+ })(jQuery);
assets/js/vendor/tiptip.min.js ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * TipTip
3
+ * Copyright 2010 Drew Wilson
4
+ * www.drewwilson.com
5
+ * code.drewwilson.com/entry/tiptip-jquery-plugin
6
+ *
7
+ * Version 1.3 - Updated: Mar. 23, 2010
8
+ *
9
+ * This Plug-In will create a custom tooltip to replace the default
10
+ * browser tooltip. It is extremely lightweight and very smart in
11
+ * that it detects the edges of the browser window and will make sure
12
+ * the tooltip stays within the current window size. As a result the
13
+ * tooltip will adjust itself to be displayed above, below, to the left
14
+ * or to the right depending on what is necessary to stay within the
15
+ * browser window. It is completely customizable as well via CSS.
16
+ *
17
+ * This TipTip jQuery plug-in is dual licensed under the MIT and GPL licenses:
18
+ * http://www.opensource.org/licenses/mit-license.php
19
+ * http://www.gnu.org/licenses/gpl.html
20
+ */
21
+ (function($){$.fn.tipTip=function(options){var defaults={activation:"hover",keepAlive:false,maxWidth:"200px",edgeOffset:3,defaultPosition:"bottom",delay:400,fadeIn:200,fadeOut:200,attribute:"title",content:false,enter:function(){},exit:function(){}};var opts=$.extend(defaults,options);if($("#tiptip_holder").length<=0){var tiptip_holder=$('<div id="tiptip_holder" style="max-width:'+opts.maxWidth+';"></div>');var tiptip_content=$('<div id="tiptip_content"></div>');var tiptip_arrow=$('<div id="tiptip_arrow"></div>');$("body").append(tiptip_holder.html(tiptip_content).prepend(tiptip_arrow.html('<div id="tiptip_arrow_inner"></div>')))}else{var tiptip_holder=$("#tiptip_holder");var tiptip_content=$("#tiptip_content");var tiptip_arrow=$("#tiptip_arrow")}return this.each(function(){var org_elem=$(this);if(opts.content){var org_title=opts.content}else{var org_title=org_elem.attr(opts.attribute)}if(org_title!=""){if(!opts.content){org_elem.removeAttr(opts.attribute)}var timeout=false;if(opts.activation=="hover"){org_elem.hover(function(){active_tiptip()},function(){if(!opts.keepAlive){deactive_tiptip()}});if(opts.keepAlive){tiptip_holder.hover(function(){},function(){deactive_tiptip()})}}else if(opts.activation=="focus"){org_elem.focus(function(){active_tiptip()}).blur(function(){deactive_tiptip()})}else if(opts.activation=="click"){org_elem.click(function(){active_tiptip();return false}).hover(function(){},function(){if(!opts.keepAlive){deactive_tiptip()}});if(opts.keepAlive){tiptip_holder.hover(function(){},function(){deactive_tiptip()})}}function active_tiptip(){opts.enter.call(this);tiptip_content.html(org_title);tiptip_holder.hide().removeAttr("class").css("margin","0");tiptip_arrow.removeAttr("style");var top=parseInt(org_elem.offset()['top']);var left=parseInt(org_elem.offset()['left']);var org_width=parseInt(org_elem.outerWidth());var org_height=parseInt(org_elem.outerHeight());var tip_w=tiptip_holder.outerWidth();var tip_h=tiptip_holder.outerHeight();var w_compare=Math.round((org_width-tip_w)/2);var h_compare=Math.round((org_height-tip_h)/2);var marg_left=Math.round(left+w_compare);var marg_top=Math.round(top+org_height+opts.edgeOffset);var t_class="";var arrow_top="";var arrow_left=Math.round(tip_w-12)/2;if(opts.defaultPosition=="bottom"){t_class="_bottom"}else if(opts.defaultPosition=="top"){t_class="_top"}else if(opts.defaultPosition=="left"){t_class="_left"}else if(opts.defaultPosition=="right"){t_class="_right"}var right_compare=(w_compare+left)<parseInt($(window).scrollLeft());var left_compare=(tip_w+left)>parseInt($(window).width());if((right_compare&&w_compare<0)||(t_class=="_right"&&!left_compare)||(t_class=="_left"&&left<(tip_w+opts.edgeOffset+5))){t_class="_right";arrow_top=Math.round(tip_h-13)/2;arrow_left=-12;marg_left=Math.round(left+org_width+opts.edgeOffset);marg_top=Math.round(top+h_compare)}else if((left_compare&&w_compare<0)||(t_class=="_left"&&!right_compare)){t_class="_left";arrow_top=Math.round(tip_h-13)/2;arrow_left=Math.round(tip_w);marg_left=Math.round(left-(tip_w+opts.edgeOffset+5));marg_top=Math.round(top+h_compare)}var top_compare=(top+org_height+opts.edgeOffset+tip_h+8)>parseInt($(window).height()+$(window).scrollTop());var bottom_compare=((top+org_height)-(opts.edgeOffset+tip_h+8))<0;if(top_compare||(t_class=="_bottom"&&top_compare)||(t_class=="_top"&&!bottom_compare)){if(t_class=="_top"||t_class=="_bottom"){t_class="_top"}else{t_class=t_class+"_top"}arrow_top=tip_h;marg_top=Math.round(top-(tip_h+5+opts.edgeOffset))}else if(bottom_compare|(t_class=="_top"&&bottom_compare)||(t_class=="_bottom"&&!top_compare)){if(t_class=="_top"||t_class=="_bottom"){t_class="_bottom"}else{t_class=t_class+"_bottom"}arrow_top=-12;marg_top=Math.round(top+org_height+opts.edgeOffset)}if(t_class=="_right_top"||t_class=="_left_top"){marg_top=marg_top+5}else if(t_class=="_right_bottom"||t_class=="_left_bottom"){marg_top=marg_top-5}if(t_class=="_left_top"||t_class=="_left_bottom"){marg_left=marg_left+5}tiptip_arrow.css({"margin-left":arrow_left+"px","margin-top":arrow_top+"px"});tiptip_holder.css({"margin-left":marg_left+"px","margin-top":marg_top+"px"}).attr("class","tip"+t_class);if(timeout){clearTimeout(timeout)}timeout=setTimeout(function(){tiptip_holder.stop(true,true).fadeIn(opts.fadeIn)},opts.delay)}function deactive_tiptip(){opts.exit.call(this);if(timeout){clearTimeout(timeout)}tiptip_holder.fadeOut(opts.fadeOut)}}})}})(jQuery);
assets/screenshot-1.png DELETED
Binary file
assets/screenshot-2.png DELETED
Binary file
assets/screenshot-3.png DELETED
Binary file
assets/screenshot-4.png DELETED
Binary file
assets/screenshot-5.png DELETED
Binary file
assets/screenshot-6.png DELETED
Binary file
assets/screenshot-7.png DELETED
Binary file
class-google-calendar-events-admin.php DELETED
@@ -1,374 +0,0 @@
1
- <?php
2
- /**
3
- * Google Calendar Events Admin
4
- *
5
- * @package GCE Admin
6
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
7
- * @license GPL-2.0+
8
- * @link http://philderksen.com
9
- * @copyright 2014 Phil Derksen
10
- */
11
-
12
-
13
- class Google_Calendar_Events_Admin {
14
-
15
- /**
16
- * Instance of this class.
17
- *
18
- * @since 2.0.0
19
- *
20
- * @var object
21
- */
22
- protected static $instance = null;
23
-
24
- protected $version = '';
25
-
26
- /**
27
- * Slug of the plugin screen.
28
- *
29
- * @since 2.0.0
30
- *
31
- * @var string
32
- */
33
- protected $plugin_screen_hook_suffix = null;
34
-
35
- /**
36
- * Initialize the plugin by loading admin scripts & styles and adding a
37
- * settings page and menu.
38
- *
39
- * @since 2.0.0
40
- */
41
- private function __construct() {
42
-
43
- $plugin = Google_Calendar_Events::get_instance();
44
- $this->plugin_slug = $plugin->get_plugin_slug();
45
-
46
- $this->version = $plugin->get_plugin_version();
47
-
48
- add_filter( 'plugin_action_links_' . plugin_basename( plugin_dir_path( __FILE__ ) . $this->plugin_slug . '.php' ), array( $this, 'add_action_links' ) );
49
-
50
- // Setup admin side constants
51
- add_action( 'init', array( $this, 'define_admin_constants' ) );
52
-
53
- // Add admin styles
54
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
55
-
56
- // Add admin JS
57
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
58
-
59
- // Add the options page and menu item.
60
- add_action( 'admin_menu', array( $this, 'add_plugin_admin_menu' ), 2 );
61
-
62
- if ( isset( $_GET['gce_dismiss_admin_update_notices'] ) ) {
63
- delete_option( 'gce_admin_update_notices' );
64
- } elseif ( 'show' == get_option( 'gce_admin_update_notices' ) && current_user_can( 'manage_options' ) ) {
65
- add_action( 'admin_head', array( $this, 'dismissible_admin_notices_styles' ) );
66
- add_action( 'admin_notices', array( $this, 'show_admin_update_notices' ) );
67
- }
68
-
69
- // Add admin notice after plugin activation. Also check if should be hidden.
70
- // add_action( 'admin_notices', array( $this, 'show_admin_notice' ) );
71
-
72
- // Add media button for adding a shortcode.
73
- add_action( 'media_buttons', array( $this, 'add_shortcode_button' ), 100 );
74
- add_action( 'edit_form_after_editor', array( $this, 'add_shortcode_panel' ), 100 );
75
- }
76
-
77
- /**
78
- * @since 2.4.0
79
- */
80
- public function dismissible_admin_notices_styles() {
81
- ?>
82
- <style type="text/css">
83
- body a.gce-dismiss-notice {
84
- color: #ccc;
85
- float: right;
86
- margin-top: 9px;
87
- text-decoration: none;
88
- }
89
- body a.gce-dismiss-notice:active,
90
- body a.gce-dismiss-notice:focus {
91
- outline: 0;
92
- }
93
- body a.gce-dismiss-notice:hover {
94
- color: #aaa;
95
- }
96
- </style>
97
- <?php
98
- }
99
-
100
- /**
101
- * @since 2.4.0
102
- */
103
- public function show_admin_update_notices() {
104
-
105
- $message = '';
106
-
107
- $settings = get_option( 'gce_settings_general' );
108
- $key = isset( $settings['api_key'] ) ? $settings['api_key'] : '';
109
- if ( empty( $key ) || 'AIzaSyAssdKVved1mPVY0UJCrx96OUOF9u17AuY' == $key ) {
110
- $message .= '<h3>' . __( 'Google API Key Notice', 'gce' ) . '</h3>' .
111
- '<p>' . __( 'This plugin now requires you to use your own Google API key to avoid running into limit requests.', 'gce' ) . '</p>' .
112
- '<p><a href="' . admin_url( 'edit.php?post_type=gce_feed&page=google-calendar-events_general_settings' ) . '" class="button-primary">' . __( 'Enter your Google API key', 'gce' ) . '</a>&nbsp;&nbsp;&nbsp;&nbsp;' .
113
- '<a href="' . gce_ga_campaign_url( 'http://wpdocs.philderksen.com/google-calendar-events/getting-started/api-key-settings/', 'gce_lite', 'settings_link', 'docs' ) . '" class="button-secondary" target="_blank">' . __( 'Instructions', 'gce' ) . '</a>' .
114
- '</p>';
115
- }
116
-
117
- $message .= '<h3>' . __( 'Plugin Upgrade Notice', 'gce' ) . '</h3>' .
118
- '<p>' . __( 'Simple Calendar 3.0 is a pretty major update and will be released very soon.', 'gce' ) . '</p>' .
119
- '<p>' . __( 'To make sure you\'re prepared, you can ', 'gce' ) .
120
- '<a href="https://wordpress.org/support/topic/simple-calendar-30-beta-now-available" target="_blank">' . __( 'read about (and try out) the current beta here', 'gce' ) . '</a></p>' .
121
- '<p><a href="https://www.getdrip.com/forms/9434542/submissions/new" class="button-secondary" target="_blank">' .__( 'Get notified of important updates', 'gce' ) . '</a></p>';
122
-
123
- $url = add_query_arg( array( 'gce_dismiss_admin_update_notices' => true ) );
124
- $dismiss_icon = sprintf( '<a class="dashicons-before dashicons-dismiss gce-dismiss-notice" href="%s"></a>', $url );
125
- $dismiss_link = sprintf( '<p><a href="%s">' . __( 'Dismiss this notice', 'gce' ) . '</a></p>', $url );
126
-
127
- echo '<div class="notice error gce-dismissible-notice">' .
128
- $dismiss_icon . $message . $dismiss_link .
129
- '</div>';
130
- }
131
-
132
- /**
133
- * Show notice after plugin install/activate
134
- * Also check if user chooses to hide it.
135
- *
136
- * @since 2.1.0
137
- */
138
- public function show_admin_notice() {
139
- // Exit all of this is stored value is false/0 or not set.
140
- if ( false == get_option( 'gce_show_admin_install_notice' ) ) {
141
- return;
142
- }
143
-
144
- $screen = get_current_screen();
145
-
146
- // Delete stored value if "hide" button click detected (custom querystring value set to 1).
147
- if ( ! empty( $_REQUEST['gce-dismiss-install-nag'] ) || in_array( $screen->id, $this->plugin_screen_hook_suffix ) || $this->viewing_this_plugin() ) {
148
- delete_option( 'gce_show_admin_install_notice' );
149
- return;
150
- }
151
-
152
- // At this point show install notice. Show it only on the plugin screen.
153
- if( get_current_screen()->id == 'plugins' ) {
154
- include_once( 'includes/admin/admin-notice.php' );
155
- }
156
- }
157
-
158
- /**
159
- * Check if viewing one of this plugin's admin pages.
160
- *
161
- * @since 2.1.0
162
- *$this->viewing_this_plugin()
163
- * @return bool
164
- */
165
- private function viewing_this_plugin() {
166
- if ( ! isset( $this->plugin_screen_hook_suffix ) ) {
167
- return false;
168
- }
169
-
170
- $screen = get_current_screen();
171
-
172
- if ( $screen->id == 'edit-gce_feed' || $screen->id == 'gce_feed' || in_array( $screen->id, $this->plugin_screen_hook_suffix ) || $screen->id == 'widgets' ) {
173
- return true;
174
- } else {
175
- return false;
176
- }
177
- }
178
-
179
- /**
180
- * Fired when the plugin is activated.
181
- *
182
- * @since 2.1.0
183
- */
184
- public static function activate() {
185
- flush_rewrite_rules();
186
- add_option( 'gce_admin_update_notices', 'show' );
187
- //update_option( 'gce_show_admin_install_notice', 1 );
188
- }
189
-
190
- /**
191
- * Fired when the plugin is deactivated.
192
- */
193
- public static function deactivate() {
194
- flush_rewrite_rules();
195
- }
196
-
197
- public function add_plugin_admin_menu() {
198
- // Add Help submenu page
199
- $this->plugin_screen_hook_suffix[] = add_submenu_page(
200
- 'edit.php?post_type=gce_feed',
201
- __( 'General Settings', 'gce' ),
202
- __( 'General Settings', 'gce' ),
203
- 'manage_options',
204
- $this->plugin_slug . '_general_settings',
205
- array( $this, 'display_admin_page' )
206
- );
207
- }
208
-
209
- public function display_admin_page() {
210
- include_once( 'views/admin/admin.php' );
211
- }
212
-
213
- /**
214
- * Enqueue JS for the admin area
215
- *
216
- * @since 2.0.0
217
- */
218
- public function enqueue_admin_scripts() {
219
-
220
- // Script for the add shortcode media button.
221
- wp_enqueue_script( 'gce-admin-add-shortcode', plugins_url( 'js/gce-admin-shortcode.js', __FILE__ ), array( 'jquery', 'thickbox' ), $this->version, true);
222
-
223
- if( $this->viewing_this_plugin() ) {
224
- wp_enqueue_script( 'jquery-ui-datepicker' );
225
- wp_enqueue_script( 'gce-admin', plugins_url( 'js/gce-admin.js', __FILE__ ), array( 'jquery' ), $this->version, true );
226
- }
227
- }
228
-
229
- /**
230
- * Enqueue styles for the admin area
231
- *
232
- * @since 2.0.0
233
- */
234
- public function enqueue_admin_styles() {
235
-
236
- // Style for the add shortcode media button.
237
- wp_enqueue_style( 'gce-admin-shortcode', plugins_url( 'css/admin-shortcode.css', __FILE__ ), array(), $this->version, 'all' );
238
-
239
- if( $this->viewing_this_plugin() ) {
240
- global $wp_scripts;
241
-
242
- // get the jquery ui object
243
- $queryui = $wp_scripts->query( 'jquery-ui-datepicker' );
244
-
245
- // Use minified CSS from CDN referenced at https://code.jquery.com/ui/
246
- wp_enqueue_style( 'jquery-ui-smoothness', '//code.jquery.com/ui/' . $queryui->ver . '/themes/smoothness/jquery-ui.min.css', array(), $this->version );
247
-
248
- wp_enqueue_style( 'gce-admin', plugins_url( 'css/admin.css', __FILE__ ), array(), $this->version, 'all' );
249
- }
250
- }
251
-
252
- /**
253
- * Define constants that will be used throughout admin specific code
254
- *
255
- * @since 2.0.0
256
- */
257
- public function define_admin_constants() {
258
- if( ! defined( 'GCE_DIR' ) ) {
259
- define( 'GCE_DIR', dirname( __FILE__ ) );
260
- }
261
- }
262
-
263
- /**
264
- * Return an instance of this class.
265
- *
266
- * @since 2.0.0
267
- *
268
- * @return object A single instance of this class.
269
- */
270
- public static function get_instance() {
271
-
272
- // If the single instance hasn't been set, set it now.
273
- if ( null == self::$instance ) {
274
- self::$instance = new self;
275
- }
276
-
277
- return self::$instance;
278
- }
279
-
280
- /**
281
- * Return plugin name
282
- *
283
- * @since 2.0.0
284
- */
285
- public function get_plugin_title() {
286
- return __( 'Google Calendar Events', 'gce' );
287
- }
288
-
289
- /**
290
- * Add settings action link to the plugins page.
291
- *
292
- * @since 2.0.0
293
- */
294
- public function add_action_links( $links ) {
295
-
296
- return array_merge(
297
- array(
298
- 'settings' => '<a href="' . admin_url( 'edit.php?post_type=gce_feed&page=google-calendar-events_general_settings' ) . '">' . __( 'General Settings', 'gce' ) . '</a>',
299
- 'feeds' => '<a href="' . admin_url( 'edit.php?post_type=gce_feed' ) . '">' . __( 'Feeds', 'gce' ) . '</a>'
300
- ),
301
- $links
302
- );
303
- }
304
-
305
- /**
306
- * Add a shortcode button.
307
- *
308
- * Adds a button to add a calendar shortcode in WordPress content editor.
309
- *
310
- * @see http://codex.wordpress.org/ThickBox
311
- */
312
- public function add_shortcode_button() {
313
- // Thickbox will ignore height and width, will adjust these in js.
314
- // @see https://core.trac.wordpress.org/ticket/17249
315
- ?>
316
- <a href="#TB_inline?height=250&width=500&inlineId=gce-insert-shortcode-panel" id="gce-insert-shortcode-button" class="thickbox button insert-calendar add_calendar">
317
- <span class="wp-media-buttons-icon"></span> <?php _e( 'Add Calendar', 'gce' ); ?>
318
- </a>
319
- <?php
320
- }
321
-
322
- /**
323
- * Panel for the add shortcode media button.
324
- *
325
- * Prints the panel for choosing a calendar to insert as a shortcode in a page or post.
326
- */
327
- public function add_shortcode_panel() {
328
-
329
- $feeds = get_transient( 'gce_feed_ids' );
330
- if ( ! $feeds ) {
331
-
332
- $query = get_posts( array(
333
- 'post_type' => 'gce_feed',
334
- 'orderby' => 'title',
335
- 'order' => 'ASC',
336
- 'nopaging' => true
337
- ) );
338
-
339
- $results = array();
340
- if ( $query && is_array( $query ) ) {
341
- foreach ( $query as $feed ) {
342
- $results[ $feed->ID ] = $feed->post_title;
343
- }
344
- set_transient( 'gce_feed_ids', $results, 604800 );
345
- }
346
- $feeds = $results;
347
- }
348
-
349
- ?>
350
- <div id="gce-insert-shortcode-panel" style="display:none;">
351
- <div class="gce-insert-shortcode-panel">
352
- <h1><?php _e( 'Add Calendar', 'gce'); ?></h1>
353
- <p><?php _e( 'Add a calendar feed to your post.', 'gce' ); ?></p>
354
- <?php if ( ! empty( $feeds ) ) : ?>
355
- <label for="gce-choose-gce-feed">
356
- <select id="gce-choose-gce-feed" name="">
357
- <?php foreach ( $feeds as $id => $title ) : ?>
358
- <option value="<?php echo $id ?>"><?php echo $title ?></option>
359
- <?php endforeach; ?>
360
- </select>
361
- </label>
362
- <br />
363
- <input type="button" value="<?php _e( 'Insert Calendar', 'gce' ); ?>" id="gce-insert-shortcode" class="button button-primary button-large" name="" />
364
- <?php else : ?>
365
- <p><em><?php _e( 'Could not find any calendars to add to this post.', 'gce' ); ?></em></p>
366
- <p><strong><a href="post-new.php?post_type=gce_feed"><?php _e( 'Please add a new calendar feed first.', 'gce' ); ?></a></strong>
367
- <?php endif; ?>
368
- </div>
369
- </div>
370
- <?php
371
-
372
- }
373
-
374
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
class-google-calendar-events.php DELETED
@@ -1,256 +0,0 @@
1
- <?php
2
- /**
3
- * Google Calendar Events Main Class
4
- *
5
- * @package GCE
6
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
7
- * @license GPL-2.0+
8
- * @copyright 2014 Phil Derksen
9
- */
10
-
11
-
12
- class Google_Calendar_Events {
13
-
14
- /**
15
- * Plugin version, used for cache-busting of style and script file references.
16
- *
17
- * @since 2.0.0
18
- *
19
- * @var string
20
- */
21
- protected $version = '2.3.2';
22
-
23
- /**
24
- * Unique identifier for the plugin.
25
- *
26
- * @since 2.0.0
27
- *
28
- * @var string
29
- */
30
- protected $plugin_slug = 'google-calendar-events';
31
-
32
- /**
33
- * Instance of this class.
34
- *
35
- * @since 2.0.0
36
- *
37
- * @var object
38
- */
39
- protected static $instance = null;
40
-
41
- /**
42
- * Return an instance of this class.
43
- *
44
- * @since 1.0.0
45
- *
46
- * @return object A single instance of this class.
47
- */
48
- public static function get_instance() {
49
-
50
- // If the single instance hasn't been set, set it now.
51
- if ( null == self::$instance ) {
52
- self::$instance = new self;
53
- }
54
-
55
- return self::$instance;
56
- }
57
-
58
- /**
59
- * Initialize the plugin by setting localization and loading public scripts
60
- * and styles.
61
- *
62
- * @since 2.0.0
63
- */
64
- private function __construct() {
65
-
66
- // Load files.
67
- $this->includes();
68
-
69
- $old = get_option( 'gce_version' );
70
-
71
- if( version_compare( $old, $this->version, '<' ) ) {
72
- delete_option( 'gce_upgrade_has_run' );
73
- }
74
-
75
- if( false === get_option( 'gce_upgrade_has_run' ) ) {
76
- $this->upgrade();
77
- }
78
-
79
- // Init plugin.
80
- $this->setup_constants();
81
- $this->plugin_textdomain();
82
-
83
- // Register scripts.
84
- add_action( 'init', array( $this, 'register_public_scripts' ) );
85
- add_action( 'init', array( $this, 'register_public_styles' ) );
86
-
87
- // Load scripts when posts load so we know if we need to include them or not
88
- add_action( 'wp_enqueue_scripts', array( $this, 'load_scripts' ) );
89
- add_action( 'wp_enqueue_scripts', array( $this, 'theme_compatibility' ), 1000 );
90
- }
91
-
92
- /**
93
- * Load the plugin text domain for translation.
94
- *
95
- * @since 2.0.0
96
- */
97
- public function plugin_textdomain() {
98
- load_plugin_textdomain(
99
- 'gce',
100
- false,
101
- dirname( plugin_basename( GCE_MAIN_FILE ) ) . '/languages/'
102
- );
103
- }
104
-
105
- /**
106
- * Load public facing scripts
107
- *
108
- * @since 2.0.0
109
- */
110
- public function register_public_scripts() {
111
-
112
- // DON'T include ImagesLoaded JS library recommended by qTip2 yet since we don't use "complex content that contains images" (yet).
113
- // http://qtip2.com/guides#gettingstarted.imagesloaded
114
- // We WERE doing this between 2.1.6 & 2.2.5 (taken out as of 2.2.6).
115
- // AND this was probably causing issues with themes including the Isotope jQuery library.
116
- // http://qtip2.com/guides#integration.isotope
117
-
118
- $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
119
-
120
- wp_register_script( $this->plugin_slug . '-qtip', plugins_url( 'js/jquery.qtip' . $min . '.js', __FILE__ ), array( 'jquery' ), $this->version, true );
121
- wp_register_script( $this->plugin_slug . '-public', plugins_url( 'js/gce-script.js', __FILE__ ), array( 'jquery', $this->plugin_slug . '-qtip' ), $this->version, true );
122
- }
123
-
124
- /*
125
- * Load public facing styles
126
- *
127
- * @since 2.0.0
128
- */
129
- public function register_public_styles() {
130
-
131
- $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
132
-
133
- wp_register_style( $this->plugin_slug . '-qtip', plugins_url( 'css/jquery.qtip' . $min . '.css', __FILE__ ), array(), $this->version );
134
- wp_register_style( $this->plugin_slug . '-public', plugins_url( 'css/gce-style.css', __FILE__ ), array( $this->plugin_slug . '-qtip' ), $this->version );
135
- }
136
-
137
- /**
138
- * Load scripts conditionally.
139
- */
140
- public function load_scripts() {
141
-
142
- global $gce_options, $post;
143
- $post_type = isset( $post->post_type ) ? $post->post_type : null;
144
- $content = isset( $post->post_content ) ? $post->post_content : '';
145
-
146
- $conditions = array(
147
- has_shortcode( $content, 'gcal' ),
148
- 'gce_feed' == $post_type,
149
- isset( $gce_options['always_enqueue'] ),
150
- is_active_widget( false, false, 'gce_widget', true )
151
- );
152
-
153
- if ( in_array( true, $conditions ) ) {
154
-
155
- if ( ! isset( $gce_options['disable_css'] ) ) {
156
- wp_enqueue_style( $this->plugin_slug . '-public' );
157
- }
158
-
159
- wp_enqueue_script( $this->plugin_slug . '-public' );
160
- wp_localize_script( $this->plugin_slug . '-public', 'gce', array(
161
- 'ajaxurl' => admin_url( 'admin-ajax.php' ),
162
- 'loadingText' => __( 'Loading...', 'gce' ),
163
- ) );
164
- }
165
-
166
- }
167
-
168
- public function theme_compatibility() {
169
- if ( wp_script_is( $this->plugin_slug . '-public', 'enqueued' ) ) {
170
- $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
171
- wp_enqueue_script(
172
- 'gce-imagesloaded',
173
- plugins_url( 'js/imagesloaded.pkgd' . $min . '.js', __FILE__ ),
174
- array( $this->plugin_slug . '-qtip' ),
175
- '3.1.8',
176
- true
177
- );
178
- }
179
- }
180
-
181
- /**
182
- * Load the upgrade file
183
- *
184
- * @since 2.0.0
185
- */
186
- public function upgrade() {
187
- include_once( 'includes/admin/upgrade.php' );
188
- }
189
-
190
- /**
191
- * Setup public constants
192
- *
193
- * @since 2.0.0
194
- */
195
- public function setup_constants() {
196
- if( ! defined( 'GCE_DIR' ) ) {
197
- define( 'GCE_DIR', dirname( __FILE__ ) );
198
- }
199
-
200
- if( ! defined( 'GCE_PLUGIN_SLUG' ) ) {
201
- define( 'GCE_PLUGIN_SLUG', $this->plugin_slug );
202
- }
203
- }
204
-
205
- /**
206
- * Include all necessary files
207
- *
208
- * @since 2.0.0
209
- */
210
- public static function includes() {
211
-
212
- global $gce_options;
213
-
214
- // Front facing side.
215
- include_once( 'includes/misc-functions.php' );
216
- include_once( 'includes/gce-feed-cpt.php' );
217
- include_once( 'includes/class-gce-display.php' );
218
- include_once( 'includes/class-gce-event.php' );
219
- include_once( 'includes/class-gce-feed.php' );
220
- include_once( 'includes/shortcodes.php' );
221
- include_once( 'views/widgets.php' );
222
-
223
- // Admin.
224
- if ( is_admin() ) {
225
- include_once( 'includes/admin/admin-functions.php' );
226
- }
227
-
228
- // Setup our main settings options.
229
- include_once( 'includes/register-settings.php' );
230
-
231
- $gce_options = gce_get_settings();
232
- }
233
-
234
- /**
235
- * Return the plugin slug.
236
- *
237
- * @since 2.0.0
238
- *
239
- * @return string Plugin version variable.
240
- */
241
- public function get_plugin_slug() {
242
- return $this->plugin_slug;
243
- }
244
-
245
- /**
246
- * Return the plugin version.
247
- *
248
- * @since 1.0.0
249
- *
250
- * @return string Plugin slug variable.
251
- */
252
- public function get_plugin_version() {
253
- return $this->version;
254
- }
255
-
256
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/admin-shortcode.css DELETED
@@ -1,35 +0,0 @@
1
-
2
- body.post-type-gce_feed #gce-insert-shortcode-button {
3
- display: none;
4
- }
5
-
6
- /* Add shortcode media button and panel */
7
- .wp-media-buttons a.add_calendar span.wp-media-buttons-icon {
8
- background: none;
9
- }
10
- .wp-media-buttons a.add_calendar span.wp-media-buttons-icon:before {
11
- content: '\f508';
12
- font: normal 18px/1 'dashicons';
13
- -webkit-font-smoothing: antialiased;
14
- -moz-osx-font-smoothing: grayscale;
15
- speak: none;
16
- }
17
-
18
- /* Add shortcode panel contents */
19
- .gce-insert-shortcode-panel select {
20
- display: block;
21
- max-width: 470px;
22
- width: 100%;
23
- }
24
- /* Hack to make ThickBox look right */
25
- /* see: https://core.trac.wordpress.org/ticket/17249 */
26
- body .gce-insert-shortcode-modal {
27
- height: 220px !important;
28
- margin-left: -250px !important;
29
- width: 500px !important;
30
- }
31
- body .gce-insert-shortcode-modal-title {
32
- background: #fff !important;
33
- border-bottom: none !important;
34
- height: 0 !important;
35
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/admin.css DELETED
@@ -1,67 +0,0 @@
1
- /**
2
- * Admin CSS file
3
- *
4
- * @package GCE
5
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
6
- * @license GPL-2.0+
7
- * @copyright 2014 Phil Derksen
8
- */
9
-
10
- .gce-meta-control {
11
- margin-bottom: 15px;
12
- }
13
-
14
- .gce-meta-control label {
15
- display: block;
16
- font-weight: bold;
17
- }
18
-
19
- #gce-display-options-wrap .gce-meta-control label {
20
- font-weight: normal;
21
- margin-top: 10px;
22
- margin-bottom: 5px;
23
- }
24
-
25
- #gce-display-options-wrap .gce-meta-control p label {
26
- display: inline;
27
- }
28
-
29
- #gce-display-options-wrap .gce-meta-control label#gce-simple-display-label {
30
- font-weight: bold;
31
- color: #cb4d4d;
32
- }
33
-
34
- .gce-admin-hidden {
35
- display: none;
36
- }
37
-
38
- #gce-display-options-wrap .gce-meta-control span.description {
39
- display: block;
40
- margin-top: 5px;
41
- }
42
-
43
- #gce-display-options-wrap .gce-meta-control span.description p {
44
- margin-top: 5px;
45
- }
46
-
47
- #gce-admin-promo {
48
- background-color: #fcf8e3;
49
- border: 1px solid #faebbe;
50
- color: #8a6d3b;
51
- padding: 15px;
52
- }
53
-
54
- #gce_feed_meta .inside code {
55
- word-break: break-all;
56
- }
57
-
58
- .gce-custom-range input {
59
- width: 120px;
60
- }
61
-
62
- input.gce-shortcode {
63
- border: none;
64
- color: rgba(0,0,0,.58);
65
- font-family: monospace;
66
- font-weight: bold;
67
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/gce-style.css DELETED
@@ -1,217 +0,0 @@
1
- /**
2
- * Public facing CSS file
3
- *
4
- * @package GCE
5
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
6
- * @license GPL-2.0+
7
- * @copyright 2014 Phil Derksen
8
- */
9
-
10
- /* Base line-height for all views & tooltip */
11
-
12
- .gce-page-grid,
13
- .gce-page-list,
14
- .gce-widget-grid,
15
- .gce-widget-list,
16
- .gce-event-info {
17
- line-height: 1.5;
18
- }
19
-
20
- /* Month & list titles for all views */
21
-
22
- .gce-month-title,
23
- .gce-list-title {
24
- font-weight: bold;
25
- padding-bottom: 5px;
26
- }
27
-
28
- /* Event titles with shaded background for all views */
29
-
30
- .gce-list-event,
31
- .gce-tooltip-event {
32
- background-color: #ddd;
33
- padding: 0 5px;
34
- }
35
-
36
- /* PAGE GRID */
37
-
38
- .gce-page-grid .gce-calendar .gce-caption{ /* Caption at top of calendar */
39
- color:#333333;
40
- text-align:center;
41
- }
42
-
43
- .gce-page-grid .gce-calendar{ /* Main calendar table */
44
- width:100%;
45
- border-collapse:collapse;
46
- border:1px solid #CCCCCC;
47
- color:#CCCCCC;
48
- }
49
-
50
- .gce-page-grid .gce-calendar th{ /* Day headings (S, M etc.) */
51
- border:1px solid #CCCCCC;
52
- text-align:center;
53
- width:14.29%;
54
- padding:0;
55
- }
56
-
57
- .gce-page-grid .gce-calendar td{ /* Day table cells */
58
- border:1px solid #CCCCCC;
59
- text-align:center;
60
- height:80px;
61
- vertical-align:middle;
62
- padding:0;
63
- }
64
-
65
- .gce-page-grid .gce-calendar .gce-has-events{ /* Table cells with events */
66
- color:#333333;
67
- cursor:pointer;
68
- }
69
-
70
- .gce-page-grid .gce-calendar .gce-event-info{ /* Event information */
71
- display:none; /* Important! */
72
- }
73
-
74
- .gce-page-grid .gce-calendar .gce-day-number{ /* Day number span */
75
- font-size:2em;
76
- }
77
-
78
- .gce-page-grid .gce-calendar .gce-today{ /* Table cell that represents today */
79
- background-color:#DDDDDD;
80
- }
81
-
82
- .gce-page-grid .gce-calendar th abbr{ /* Day letter abbreviation */
83
- border-bottom:none;
84
- }
85
-
86
- /* PAGE LIST */
87
-
88
- .gce-page-list .gce-feed {
89
- padding-bottom: 10px;
90
- }
91
-
92
- .gce-page-list .gce-list p{ /* Each piece of information in the list */
93
- margin:0;
94
- }
95
-
96
- .gce-page-list .gce-list p span,
97
- .gce-page-list .gce-list div span{ /* The text displayed before each piece of info, 'Starts:' for example */
98
- color:#999999;
99
- }
100
-
101
- /* WIDGET GRID */
102
-
103
- .gce-widget-grid .gce-calendar .gce-caption{
104
- text-align:center;
105
- }
106
-
107
- .gce-widget-grid .gce-calendar{ /* Main calendar table */
108
- width:100%;
109
- border:1px solid #CCCCCC;
110
- border-collapse:collapse;
111
- }
112
-
113
- .gce-widget-grid .gce-calendar th{ /* Day headings (S, M etc.) */
114
- width:14.29%;
115
- border:1px solid #CCCCCC;
116
- text-align:center;
117
- }
118
-
119
- .gce-widget-grid .gce-calendar td{ /* Day table cells */
120
- color:#CCCCCC;
121
- width:14.29%;
122
- border:1px solid #CCCCCC;
123
- text-align:center;
124
- }
125
-
126
- .gce-widget-grid .gce-calendar .gce-has-events{ /* Table cells with events */
127
- cursor:pointer;
128
- color:#666666;
129
- }
130
-
131
- .gce-widget-grid .gce-calendar .gce-today{ /* Table cell that represents today */
132
- background-color:#DDDDDD;
133
- }
134
-
135
- .gce-widget-grid .gce-calendar .gce-event-info{ /* Event information */
136
- display:none; /* Important! */
137
- }
138
-
139
- .gce-widget-grid .gce-calendar th abbr{ /* Day name abbreviations */
140
- border-bottom:none;
141
- }
142
-
143
- /* WIDGET LIST */
144
-
145
- .gce-widget-list .gce-list p{ /* Each piece of information in the list */
146
- margin:0;
147
- }
148
-
149
- .gce-widget-list .gce-list p span,
150
- .gce-widget-list .gce-list div span{ /* The text displayed before each piece of info, 'Starts:' for example */
151
- color:#999999;
152
- }
153
-
154
- /* TOOLTIP */
155
-
156
- .gce-event-info{ /* Tooltip container */
157
- background-color:#FFFFFF;
158
- }
159
-
160
- .gce-event-info .gce-tooltip-title{ /* 'Events on...' text */
161
- margin:5px;
162
- font-weight:bold;
163
- font-size:1.2em;
164
- }
165
-
166
- .gce-event-info ul{ /* Events list */
167
- padding:0;
168
- margin:5px;
169
- list-style-type:none;
170
- }
171
-
172
- .gce-event-info ul li{ /* Event list item */
173
- margin:10px 0 0 0;
174
- }
175
-
176
- .gce-event-info ul li p{ /* Each piece of information */
177
- margin:0;
178
- }
179
-
180
- .gce-event-info ul li p span,
181
- .gce-event-info ul li div span{ /* The text displayed before each piece of info, 'Starts:' for example */
182
- color:#999999;
183
- }
184
-
185
- /** Calendar navigation bar, Prev/Next links & month title. **/
186
-
187
- .gce-navbar {
188
- width: 100%;
189
- text-align: center;
190
- clear: both;
191
- overflow: hidden;
192
- }
193
-
194
- .gce-next,
195
- .gce-prev {
196
- white-space: nowrap;
197
- }
198
-
199
- .gce-prev {
200
- float: left;
201
- }
202
-
203
- .gce-next {
204
- float: right;
205
- }
206
-
207
- .gce-month-title {
208
- display: inline-block;
209
- margin: 0 auto;
210
- white-space: nowrap;
211
- }
212
-
213
- /* Grouped List */
214
-
215
- .gce-list-grouped .gce-feed {
216
- margin-left: 5%;
217
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/jquery.qtip.css DELETED
@@ -1,589 +0,0 @@
1
- /*
2
- * qTip2 - Pretty powerful tooltips - v2.2.1
3
- * http://qtip2.com
4
- *
5
- * Copyright (c) 2014
6
- * Released under the MIT licenses
7
- * http://jquery.org/license
8
- *
9
- * Date: Sat Sep 6 2014 09:55 EDT-0400
10
- * Plugins: tips viewport
11
- * Styles: core basic css3
12
- */
13
- .qtip{
14
- position: absolute;
15
- left: -28000px;
16
- top: -28000px;
17
- display: none;
18
-
19
- max-width: 280px;
20
- min-width: 50px;
21
-
22
- font-size: 10.5px;
23
- line-height: 12px;
24
-
25
- direction: ltr;
26
-
27
- box-shadow: none;
28
- padding: 0;
29
- }
30
-
31
- .qtip-content{
32
- position: relative;
33
- padding: 5px 9px;
34
- overflow: hidden;
35
-
36
- text-align: left;
37
- word-wrap: break-word;
38
- }
39
-
40
- .qtip-titlebar{
41
- position: relative;
42
- padding: 5px 35px 5px 10px;
43
- overflow: hidden;
44
-
45
- border-width: 0 0 1px;
46
- font-weight: bold;
47
- }
48
-
49
- .qtip-titlebar + .qtip-content{ border-top-width: 0 !important; }
50
-
51
- /* Default close button class */
52
- .qtip-close{
53
- position: absolute;
54
- right: -9px; top: -9px;
55
- z-index: 11; /* Overlap .qtip-tip */
56
-
57
- cursor: pointer;
58
- outline: medium none;
59
-
60
- border: 1px solid transparent;
61
- }
62
-
63
- .qtip-titlebar .qtip-close{
64
- right: 4px; top: 50%;
65
- margin-top: -9px;
66
- }
67
-
68
- * html .qtip-titlebar .qtip-close{ top: 16px; } /* IE fix */
69
-
70
- .qtip-titlebar .ui-icon,
71
- .qtip-icon .ui-icon{
72
- display: block;
73
- text-indent: -1000em;
74
- direction: ltr;
75
- }
76
-
77
- .qtip-icon, .qtip-icon .ui-icon{
78
- -moz-border-radius: 3px;
79
- -webkit-border-radius: 3px;
80
- border-radius: 3px;
81
- text-decoration: none;
82
- }
83
-
84
- .qtip-icon .ui-icon{
85
- width: 18px;
86
- height: 14px;
87
-
88
- line-height: 14px;
89
- text-align: center;
90
- text-indent: 0;
91
- font: normal bold 10px/13px Tahoma,sans-serif;
92
-
93
- color: inherit;
94
- background: transparent none no-repeat -100em -100em;
95
- }
96
-
97
- /* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */
98
- .qtip-focus{}
99
-
100
- /* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */
101
- .qtip-hover{}
102
-
103
- /* Default tooltip style */
104
- .qtip-default{
105
- border: 1px solid #F1D031;
106
-
107
- background-color: #FFFFA3;
108
- color: #555;
109
- }
110
-
111
- .qtip-default .qtip-titlebar{
112
- background-color: #FFEF93;
113
- }
114
-
115
- .qtip-default .qtip-icon{
116
- border-color: #CCC;
117
- background: #F1F1F1;
118
- color: #777;
119
- }
120
-
121
- .qtip-default .qtip-titlebar .qtip-close{
122
- border-color: #AAA;
123
- color: #111;
124
- }
125
-
126
-
127
- /*! Light tooltip style */
128
- .qtip-light{
129
- background-color: white;
130
- border-color: #E2E2E2;
131
- color: #454545;
132
- }
133
-
134
- .qtip-light .qtip-titlebar{
135
- background-color: #f1f1f1;
136
- }
137
-
138
-
139
- /*! Dark tooltip style */
140
- .qtip-dark{
141
- background-color: #505050;
142
- border-color: #303030;
143
- color: #f3f3f3;
144
- }
145
-
146
- .qtip-dark .qtip-titlebar{
147
- background-color: #404040;
148
- }
149
-
150
- .qtip-dark .qtip-icon{
151
- border-color: #444;
152
- }
153
-
154
- .qtip-dark .qtip-titlebar .ui-state-hover{
155
- border-color: #303030;
156
- }
157
-
158
-
159
- /*! Cream tooltip style */
160
- .qtip-cream{
161
- background-color: #FBF7AA;
162
- border-color: #F9E98E;
163
- color: #A27D35;
164
- }
165
-
166
- .qtip-cream .qtip-titlebar{
167
- background-color: #F0DE7D;
168
- }
169
-
170
- .qtip-cream .qtip-close .qtip-icon{
171
- background-position: -82px 0;
172
- }
173
-
174
-
175
- /*! Red tooltip style */
176
- .qtip-red{
177
- background-color: #F78B83;
178
- border-color: #D95252;
179
- color: #912323;
180
- }
181
-
182
- .qtip-red .qtip-titlebar{
183
- background-color: #F06D65;
184
- }
185
-
186
- .qtip-red .qtip-close .qtip-icon{
187
- background-position: -102px 0;
188
- }
189
-
190
- .qtip-red .qtip-icon{
191
- border-color: #D95252;
192
- }
193
-
194
- .qtip-red .qtip-titlebar .ui-state-hover{
195
- border-color: #D95252;
196
- }
197
-
198
-
199
- /*! Green tooltip style */
200
- .qtip-green{
201
- background-color: #CAED9E;
202
- border-color: #90D93F;
203
- color: #3F6219;
204
- }
205
-
206
- .qtip-green .qtip-titlebar{
207
- background-color: #B0DE78;
208
- }
209
-
210
- .qtip-green .qtip-close .qtip-icon{
211
- background-position: -42px 0;
212
- }
213
-
214
-
215
- /*! Blue tooltip style */
216
- .qtip-blue{
217
- background-color: #E5F6FE;
218
- border-color: #ADD9ED;
219
- color: #5E99BD;
220
- }
221
-
222
- .qtip-blue .qtip-titlebar{
223
- background-color: #D0E9F5;
224
- }
225
-
226
- .qtip-blue .qtip-close .qtip-icon{
227
- background-position: -2px 0;
228
- }
229
-
230
-
231
- .qtip-shadow{
232
- -webkit-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
233
- -moz-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
234
- box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
235
- }
236
-
237
- /* Add rounded corners to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+ */
238
- .qtip-rounded,
239
- .qtip-tipsy,
240
- .qtip-bootstrap{
241
- -moz-border-radius: 5px;
242
- -webkit-border-radius: 5px;
243
- border-radius: 5px;
244
- }
245
-
246
- .qtip-rounded .qtip-titlebar{
247
- -moz-border-radius: 4px 4px 0 0;
248
- -webkit-border-radius: 4px 4px 0 0;
249
- border-radius: 4px 4px 0 0;
250
- }
251
-
252
- /* Youtube tooltip style */
253
- .qtip-youtube{
254
- -moz-border-radius: 2px;
255
- -webkit-border-radius: 2px;
256
- border-radius: 2px;
257
-
258
- -webkit-box-shadow: 0 0 3px #333;
259
- -moz-box-shadow: 0 0 3px #333;
260
- box-shadow: 0 0 3px #333;
261
-
262
- color: white;
263
- border: 0 solid transparent;
264
-
265
- background: #4A4A4A;
266
- background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,black));
267
- background-image: -webkit-linear-gradient(top,#4A4A4A 0,black 100%);
268
- background-image: -moz-linear-gradient(top,#4A4A4A 0,black 100%);
269
- background-image: -ms-linear-gradient(top,#4A4A4A 0,black 100%);
270
- background-image: -o-linear-gradient(top,#4A4A4A 0,black 100%);
271
- }
272
-
273
- .qtip-youtube .qtip-titlebar{
274
- background-color: #4A4A4A;
275
- background-color: rgba(0,0,0,0);
276
- }
277
-
278
- .qtip-youtube .qtip-content{
279
- padding: .75em;
280
- font: 12px arial,sans-serif;
281
-
282
- filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);
283
- -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);";
284
- }
285
-
286
- .qtip-youtube .qtip-icon{
287
- border-color: #222;
288
- }
289
-
290
- .qtip-youtube .qtip-titlebar .ui-state-hover{
291
- border-color: #303030;
292
- }
293
-
294
-
295
- /* jQuery TOOLS Tooltip style */
296
- .qtip-jtools{
297
- background: #232323;
298
- background: rgba(0, 0, 0, 0.7);
299
- background-image: -webkit-gradient(linear, left top, left bottom, from(#717171), to(#232323));
300
- background-image: -moz-linear-gradient(top, #717171, #232323);
301
- background-image: -webkit-linear-gradient(top, #717171, #232323);
302
- background-image: -ms-linear-gradient(top, #717171, #232323);
303
- background-image: -o-linear-gradient(top, #717171, #232323);
304
-
305
- border: 2px solid #ddd;
306
- border: 2px solid rgba(241,241,241,1);
307
-
308
- -moz-border-radius: 2px;
309
- -webkit-border-radius: 2px;
310
- border-radius: 2px;
311
-
312
- -webkit-box-shadow: 0 0 12px #333;
313
- -moz-box-shadow: 0 0 12px #333;
314
- box-shadow: 0 0 12px #333;
315
- }
316
-
317
- /* IE Specific */
318
- .qtip-jtools .qtip-titlebar{
319
- background-color: transparent;
320
- filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A);
321
- -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)";
322
- }
323
- .qtip-jtools .qtip-content{
324
- filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323);
325
- -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)";
326
- }
327
-
328
- .qtip-jtools .qtip-titlebar,
329
- .qtip-jtools .qtip-content{
330
- background: transparent;
331
- color: white;
332
- border: 0 dashed transparent;
333
- }
334
-
335
- .qtip-jtools .qtip-icon{
336
- border-color: #555;
337
- }
338
-
339
- .qtip-jtools .qtip-titlebar .ui-state-hover{
340
- border-color: #333;
341
- }
342
-
343
-
344
- /* Cluetip style */
345
- .qtip-cluetip{
346
- -webkit-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
347
- -moz-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
348
- box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
349
-
350
- background-color: #D9D9C2;
351
- color: #111;
352
- border: 0 dashed transparent;
353
- }
354
-
355
- .qtip-cluetip .qtip-titlebar{
356
- background-color: #87876A;
357
- color: white;
358
- border: 0 dashed transparent;
359
- }
360
-
361
- .qtip-cluetip .qtip-icon{
362
- border-color: #808064;
363
- }
364
-
365
- .qtip-cluetip .qtip-titlebar .ui-state-hover{
366
- border-color: #696952;
367
- color: #696952;
368
- }
369
-
370
-
371
- /* Tipsy style */
372
- .qtip-tipsy{
373
- background: black;
374
- background: rgba(0, 0, 0, .87);
375
-
376
- color: white;
377
- border: 0 solid transparent;
378
-
379
- font-size: 11px;
380
- font-family: 'Lucida Grande', sans-serif;
381
- font-weight: bold;
382
- line-height: 16px;
383
- text-shadow: 0 1px black;
384
- }
385
-
386
- .qtip-tipsy .qtip-titlebar{
387
- padding: 6px 35px 0 10px;
388
- background-color: transparent;
389
- }
390
-
391
- .qtip-tipsy .qtip-content{
392
- padding: 6px 10px;
393
- }
394
-
395
- .qtip-tipsy .qtip-icon{
396
- border-color: #222;
397
- text-shadow: none;
398
- }
399
-
400
- .qtip-tipsy .qtip-titlebar .ui-state-hover{
401
- border-color: #303030;
402
- }
403
-
404
-
405
- /* Tipped style */
406
- .qtip-tipped{
407
- border: 3px solid #959FA9;
408
-
409
- -moz-border-radius: 3px;
410
- -webkit-border-radius: 3px;
411
- border-radius: 3px;
412
-
413
- background-color: #F9F9F9;
414
- color: #454545;
415
-
416
- font-weight: normal;
417
- font-family: serif;
418
- }
419
-
420
- .qtip-tipped .qtip-titlebar{
421
- border-bottom-width: 0;
422
-
423
- color: white;
424
- background: #3A79B8;
425
- background-image: -webkit-gradient(linear, left top, left bottom, from(#3A79B8), to(#2E629D));
426
- background-image: -webkit-linear-gradient(top, #3A79B8, #2E629D);
427
- background-image: -moz-linear-gradient(top, #3A79B8, #2E629D);
428
- background-image: -ms-linear-gradient(top, #3A79B8, #2E629D);
429
- background-image: -o-linear-gradient(top, #3A79B8, #2E629D);
430
- filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D);
431
- -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)";
432
- }
433
-
434
- .qtip-tipped .qtip-icon{
435
- border: 2px solid #285589;
436
- background: #285589;
437
- }
438
-
439
- .qtip-tipped .qtip-icon .ui-icon{
440
- background-color: #FBFBFB;
441
- color: #555;
442
- }
443
-
444
-
445
- /**
446
- * Twitter Bootstrap style.
447
- *
448
- * Tested with IE 8, IE 9, Chrome 18, Firefox 9, Opera 11.
449
- * Does not work with IE 7.
450
- */
451
- .qtip-bootstrap{
452
- /** Taken from Bootstrap body */
453
- font-size: 14px;
454
- line-height: 20px;
455
- color: #333333;
456
-
457
- /** Taken from Bootstrap .popover */
458
- padding: 1px;
459
- background-color: #ffffff;
460
- border: 1px solid #ccc;
461
- border: 1px solid rgba(0, 0, 0, 0.2);
462
- -webkit-border-radius: 6px;
463
- -moz-border-radius: 6px;
464
- border-radius: 6px;
465
- -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
466
- -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
467
- box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
468
- -webkit-background-clip: padding-box;
469
- -moz-background-clip: padding;
470
- background-clip: padding-box;
471
- }
472
-
473
- .qtip-bootstrap .qtip-titlebar{
474
- /** Taken from Bootstrap .popover-title */
475
- padding: 8px 14px;
476
- margin: 0;
477
- font-size: 14px;
478
- font-weight: normal;
479
- line-height: 18px;
480
- background-color: #f7f7f7;
481
- border-bottom: 1px solid #ebebeb;
482
- -webkit-border-radius: 5px 5px 0 0;
483
- -moz-border-radius: 5px 5px 0 0;
484
- border-radius: 5px 5px 0 0;
485
- }
486
-
487
- .qtip-bootstrap .qtip-titlebar .qtip-close{
488
- /**
489
- * Overrides qTip2:
490
- * .qtip-titlebar .qtip-close{
491
- * [...]
492
- * right: 4px;
493
- * top: 50%;
494
- * [...]
495
- * border-style: solid;
496
- * }
497
- */
498
- right: 11px;
499
- top: 45%;
500
- border-style: none;
501
- }
502
-
503
- .qtip-bootstrap .qtip-content{
504
- /** Taken from Bootstrap .popover-content */
505
- padding: 9px 14px;
506
- }
507
-
508
- .qtip-bootstrap .qtip-icon{
509
- /**
510
- * Overrides qTip2:
511
- * .qtip-default .qtip-icon {
512
- * border-color: #CCC;
513
- * background: #F1F1F1;
514
- * color: #777;
515
- * }
516
- */
517
- background: transparent;
518
- }
519
-
520
- .qtip-bootstrap .qtip-icon .ui-icon{
521
- /**
522
- * Overrides qTip2:
523
- * .qtip-icon .ui-icon{
524
- * width: 18px;
525
- * height: 14px;
526
- * }
527
- */
528
- width: auto;
529
- height: auto;
530
-
531
- /* Taken from Bootstrap .close */
532
- float: right;
533
- font-size: 20px;
534
- font-weight: bold;
535
- line-height: 18px;
536
- color: #000000;
537
- text-shadow: 0 1px 0 #ffffff;
538
- opacity: 0.2;
539
- filter: alpha(opacity=20);
540
- }
541
-
542
- .qtip-bootstrap .qtip-icon .ui-icon:hover{
543
- /* Taken from Bootstrap .close:hover */
544
- color: #000000;
545
- text-decoration: none;
546
- cursor: pointer;
547
- opacity: 0.4;
548
- filter: alpha(opacity=40);
549
- }
550
-
551
-
552
- /* IE9 fix - removes all filters */
553
- .qtip:not(.ie9haxors) div.qtip-content,
554
- .qtip:not(.ie9haxors) div.qtip-titlebar{
555
- filter: none;
556
- -ms-filter: none;
557
- }
558
-
559
-
560
- .qtip .qtip-tip{
561
- margin: 0 auto;
562
- overflow: hidden;
563
- z-index: 10;
564
-
565
- }
566
-
567
- /* Opera bug #357 - Incorrect tip position
568
- https://github.com/Craga89/qTip2/issues/367 */
569
- x:-o-prefocus, .qtip .qtip-tip{
570
- visibility: hidden;
571
- }
572
-
573
- .qtip .qtip-tip,
574
- .qtip .qtip-tip .qtip-vml,
575
- .qtip .qtip-tip canvas{
576
- position: absolute;
577
-
578
- color: #123456;
579
- background: transparent;
580
- border: 0 dashed transparent;
581
- }
582
-
583
- .qtip .qtip-tip canvas{ top: 0; left: 0; }
584
-
585
- .qtip .qtip-tip .qtip-vml{
586
- behavior: url(#default#VML);
587
- display: inline-block;
588
- visibility: visible;
589
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/jquery.qtip.min.css DELETED
@@ -1,3 +0,0 @@
1
- /* qTip2 v2.2.1 | Plugins: tips viewport | Styles: core basic css3 | qtip2.com | Licensed MIT | Sat Sep 06 2014 21:55:19 */
2
-
3
- .qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;direction:ltr;box-shadow:none;padding:0}.qtip-content{position:relative;padding:5px 9px;overflow:hidden;text-align:left;word-wrap:break-word}.qtip-titlebar{position:relative;padding:5px 35px 5px 10px;overflow:hidden;border-width:0 0 1px;font-weight:700}.qtip-titlebar+.qtip-content{border-top-width:0!important}.qtip-close{position:absolute;right:-9px;top:-9px;z-index:11;cursor:pointer;outline:0;border:1px solid transparent}.qtip-titlebar .qtip-close{right:4px;top:50%;margin-top:-9px}* html .qtip-titlebar .qtip-close{top:16px}.qtip-icon .ui-icon,.qtip-titlebar .ui-icon{display:block;text-indent:-1000em;direction:ltr}.qtip-icon,.qtip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none}.qtip-icon .ui-icon{width:18px;height:14px;line-height:14px;text-align:center;text-indent:0;font:400 bold 10px/13px Tahoma,sans-serif;color:inherit;background:-100em -100em no-repeat}.qtip-default{border:1px solid #F1D031;background-color:#FFFFA3;color:#555}.qtip-default .qtip-titlebar{background-color:#FFEF93}.qtip-default .qtip-icon{border-color:#CCC;background:#F1F1F1;color:#777}.qtip-default .qtip-titlebar .qtip-close{border-color:#AAA;color:#111}.qtip-light{background-color:#fff;border-color:#E2E2E2;color:#454545}.qtip-light .qtip-titlebar{background-color:#f1f1f1}.qtip-dark{background-color:#505050;border-color:#303030;color:#f3f3f3}.qtip-dark .qtip-titlebar{background-color:#404040}.qtip-dark .qtip-icon{border-color:#444}.qtip-dark .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-cream{background-color:#FBF7AA;border-color:#F9E98E;color:#A27D35}.qtip-cream .qtip-titlebar{background-color:#F0DE7D}.qtip-cream .qtip-close .qtip-icon{background-position:-82px 0}.qtip-red{background-color:#F78B83;border-color:#D95252;color:#912323}.qtip-red .qtip-titlebar{background-color:#F06D65}.qtip-red .qtip-close .qtip-icon{background-position:-102px 0}.qtip-red .qtip-icon,.qtip-red .qtip-titlebar .ui-state-hover{border-color:#D95252}.qtip-green{background-color:#CAED9E;border-color:#90D93F;color:#3F6219}.qtip-green .qtip-titlebar{background-color:#B0DE78}.qtip-green .qtip-close .qtip-icon{background-position:-42px 0}.qtip-blue{background-color:#E5F6FE;border-color:#ADD9ED;color:#5E99BD}.qtip-blue .qtip-titlebar{background-color:#D0E9F5}.qtip-blue .qtip-close .qtip-icon{background-position:-2px 0}.qtip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);-moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,.15);box-shadow:1px 1px 3px 1px rgba(0,0,0,.15)}.qtip-bootstrap,.qtip-rounded,.qtip-tipsy{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.qtip-rounded .qtip-titlebar{-moz-border-radius:4px 4px 0 0;-webkit-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0}.qtip-youtube{-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 3px #333;-moz-box-shadow:0 0 3px #333;box-shadow:0 0 3px #333;color:#fff;border:0 solid transparent;background:#4A4A4A;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,#000));background-image:-webkit-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-moz-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-ms-linear-gradient(top,#4A4A4A 0,#000 100%);background-image:-o-linear-gradient(top,#4A4A4A 0,#000 100%)}.qtip-youtube .qtip-titlebar{background-color:transparent}.qtip-youtube .qtip-content{padding:.75em;font:12px arial,sans-serif;filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#4a4a4a, EndColorStr=#000000);-ms-filter:"progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);"}.qtip-youtube .qtip-icon{border-color:#222}.qtip-youtube .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-jtools{background:#232323;background:rgba(0,0,0,.7);background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323));background-image:-moz-linear-gradient(top,#717171,#232323);background-image:-webkit-linear-gradient(top,#717171,#232323);background-image:-ms-linear-gradient(top,#717171,#232323);background-image:-o-linear-gradient(top,#717171,#232323);border:2px solid #ddd;border:2px solid rgba(241,241,241,1);-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 12px #333;-moz-box-shadow:0 0 12px #333;box-shadow:0 0 12px #333}.qtip-jtools .qtip-titlebar{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171, endColorstr=#4A4A4A);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)"}.qtip-jtools .qtip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A, endColorstr=#232323);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)"}.qtip-jtools .qtip-content,.qtip-jtools .qtip-titlebar{background:0 0;color:#fff;border:0 dashed transparent}.qtip-jtools .qtip-icon{border-color:#555}.qtip-jtools .qtip-titlebar .ui-state-hover{border-color:#333}.qtip-cluetip{-webkit-box-shadow:4px 4px 5px rgba(0,0,0,.4);-moz-box-shadow:4px 4px 5px rgba(0,0,0,.4);box-shadow:4px 4px 5px rgba(0,0,0,.4);background-color:#D9D9C2;color:#111;border:0 dashed transparent}.qtip-cluetip .qtip-titlebar{background-color:#87876A;color:#fff;border:0 dashed transparent}.qtip-cluetip .qtip-icon{border-color:#808064}.qtip-cluetip .qtip-titlebar .ui-state-hover{border-color:#696952;color:#696952}.qtip-tipsy{background:#000;background:rgba(0,0,0,.87);color:#fff;border:0 solid transparent;font-size:11px;font-family:'Lucida Grande',sans-serif;font-weight:700;line-height:16px;text-shadow:0 1px #000}.qtip-tipsy .qtip-titlebar{padding:6px 35px 0 10px;background-color:transparent}.qtip-tipsy .qtip-content{padding:6px 10px}.qtip-tipsy .qtip-icon{border-color:#222;text-shadow:none}.qtip-tipsy .qtip-titlebar .ui-state-hover{border-color:#303030}.qtip-tipped{border:3px solid #959FA9;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-color:#F9F9F9;color:#454545;font-weight:400;font-family:serif}.qtip-tipped .qtip-titlebar{border-bottom-width:0;color:#fff;background:#3A79B8;background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D));background-image:-webkit-linear-gradient(top,#3A79B8,#2E629D);background-image:-moz-linear-gradient(top,#3A79B8,#2E629D);background-image:-ms-linear-gradient(top,#3A79B8,#2E629D);background-image:-o-linear-gradient(top,#3A79B8,#2E629D);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8, endColorstr=#2E629D);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)"}.qtip-tipped .qtip-icon{border:2px solid #285589;background:#285589}.qtip-tipped .qtip-icon .ui-icon{background-color:#FBFBFB;color:#555}.qtip-bootstrap{font-size:14px;line-height:20px;color:#333;padding:1px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.qtip-bootstrap .qtip-titlebar{padding:8px 14px;margin:0;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.qtip-bootstrap .qtip-titlebar .qtip-close{right:11px;top:45%;border-style:none}.qtip-bootstrap .qtip-content{padding:9px 14px}.qtip-bootstrap .qtip-icon{background:0 0}.qtip-bootstrap .qtip-icon .ui-icon{width:auto;height:auto;float:right;font-size:20px;font-weight:700;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.qtip-bootstrap .qtip-icon .ui-icon:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}.qtip:not(.ie9haxors) div.qtip-content,.qtip:not(.ie9haxors) div.qtip-titlebar{filter:none;-ms-filter:none}.qtip .qtip-tip{margin:0 auto;overflow:hidden;z-index:10}.qtip .qtip-tip,x:-o-prefocus{visibility:hidden}.qtip .qtip-tip,.qtip .qtip-tip .qtip-vml,.qtip .qtip-tip canvas{position:absolute;color:#123456;background:0 0;border:0 dashed transparent}.qtip .qtip-tip canvas{top:0;left:0}.qtip .qtip-tip .qtip-vml{behavior:url(#default#VML);display:inline-block;visibility:visible}
 
 
 
gce-requirements.php DELETED
@@ -1,135 +0,0 @@
1
- <?php
2
- /**
3
- * WP Requirements
4
- *
5
- * Utility to check current PHP version, WordPress version and PHP extensions.
6
- *
7
- * @package WP_Requirements
8
- * @version 1.0.0
9
- * @author Fulvio Notarstefano <fulvio.notarstefano@gmail.com>
10
- * @link https://github.com/nekojira/wp-requirements
11
- * @license GPL2+
12
- */
13
-
14
- if ( ! class_exists( 'GCE_Requirements' ) ) {
15
-
16
- class GCE_Requirements {
17
-
18
- /**
19
- * WordPress.
20
- *
21
- * @access private
22
- * @var bool
23
- */
24
- private $wp = true;
25
-
26
- /**
27
- * PHP.
28
- *
29
- * @access private
30
- * @var bool
31
- */
32
- private $php = true;
33
-
34
- /**
35
- * PHP Extensions.
36
- *
37
- * @access private
38
- * @var bool
39
- */
40
- private $extensions = true;
41
-
42
- /**
43
- * Results failures.
44
- *
45
- * Associative array with requirements results.
46
- *
47
- * @access private
48
- * @var array
49
- */
50
- private $failures = array();
51
-
52
- /**
53
- * Constructor.
54
- *
55
- * @param array $requirements Associative array with requirements.
56
- */
57
- public function __construct( $requirements ) {
58
-
59
- if ( $requirements && is_array( $requirements ) ) {
60
-
61
- $errors = array();
62
- $requirements = array_merge( array( 'wp' => '', 'php' => '', 'extensions' => '' ), (array) $requirements );
63
- // Check for WordPress version.
64
- if ( $requirements['wp'] && is_string( $requirements['wp'] ) ) {
65
- global $wp_version;
66
- // If $wp_version isn't found or valid probably you are not running WordPress (properly)?
67
- $wp_ver = $wp_version && is_string( $wp_version ) ? $wp_version : $requirements['wp'];
68
- $wp_ver = version_compare( $wp_ver, $requirements['wp'] );
69
- if ( $wp_ver === -1 ) {
70
- $errors['wp'] = $wp_version;
71
- $this->wp = false;
72
- }
73
- }
74
-
75
- // Check fo PHP version.
76
- if ( $requirements['php'] && is_string( $requirements['php'] ) ) {
77
- $php_ver = version_compare( PHP_VERSION, $requirements['php'] );
78
- if ( $php_ver === -1 ) {
79
- $errors['php'] = PHP_VERSION;
80
- $this->php = false;
81
- }
82
- }
83
-
84
- // Check fo PHP Extensions.
85
- if ( $requirements['extensions'] && is_array( $requirements['extensions'] ) ) {
86
- $extensions = array();
87
- foreach ( $requirements['extensions'] as $extension ) {
88
- if ( $extension && is_string( $extension ) ) {
89
- $extensions[ $extension ] = extension_loaded( $extension );
90
- }
91
- }
92
- if ( in_array( false, $extensions ) ) {
93
- foreach ( $extensions as $extension_name => $found ) {
94
- if ( $found === false ) {
95
- $errors['extensions'][ $extension_name ] = $extension_name;
96
- }
97
- }
98
- $this->extensions = false;
99
- }
100
- }
101
-
102
- $this->failures = $errors;
103
-
104
- } else {
105
-
106
- trigger_error( 'WP Requirements: the requirements are invalid.', E_USER_ERROR );
107
-
108
- }
109
-
110
- }
111
-
112
- /**
113
- * Get requirements results.
114
- *
115
- * @return array
116
- */
117
- public function failures() {
118
- return $this->failures;
119
- }
120
-
121
- /**
122
- * Check if versions check pass.
123
- *
124
- * @return bool
125
- */
126
- public function pass() {
127
- if ( in_array( false, array( $this->wp, $this->php, $this->extensions ) ) ) {
128
- return false;
129
- }
130
- return true;
131
- }
132
-
133
- }
134
-
135
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
google-calendar-events.php CHANGED
@@ -1,86 +1,63 @@
1
  <?php
2
  /**
3
- * Plugin Name: Google Calendar Events
4
- * Plugin URI: https://wordpress.org/plugins/google-calendar-events/
5
  * Description: Show off your Google calendar in grid (month) or list view, in a post, page or widget, and in a style that matches your site.
6
- * Author: Moonstone Media
7
- * Author URI: http://moonstonemediagroup.com
8
- * Version: 2.4.0
9
- * Text Domain: gce
10
- * Domain Path: /languages/
11
  *
12
- * Copyright 2014 Moonstone Media/Phil Derksen. All rights reserved.
13
- */
14
-
15
- // If this file is called directly, abort.
16
- if ( ! defined( 'WPINC' ) ) {
17
- die();
18
- }
19
-
20
- // Set the plugin PHP and WP requirements.
21
- $gce_requires = array( 'wp' => '3.9.0', 'php' => '5.2.4' );
22
- // Constants before PHP 5.6 can't store arrays.
23
- define( 'GCE_REQUIREMENTS', serialize( $gce_requires ) );
24
- // Checks if the requirements are met.
25
- require_once dirname( __FILE__ ) . '/gce-requirements.php';
26
- $gce_requirements = new GCE_Requirements( $gce_requires );
27
- if ( $gce_requirements->pass() === false ) {
28
-
29
- // Display an admin notice explaining why the plugin can't work.
30
- function gce_plugin_requirements() {
31
- $required = unserialize( GCE_REQUIREMENTS );
32
- if ( isset( $required['wp'] ) && isset( $required['php'] ) ) {
33
- global $wp_version;
34
- echo '<div class="error"><p>' . sprintf( __( 'Google Events Calendar requires PHP %1$s and WordPress %2$s to function properly. PHP version found: %3$s. WordPress installed version: %4$s. Please upgrade to meet the minimum requirements.', 'gce' ), $required['php'], $required['wp'], PHP_VERSION, $wp_version ) . '</p></div>';
35
- }
36
- }
37
- add_action( 'admin_notices', 'gce_plugin_requirements' );
38
-
39
- $gce_fails = $gce_requirements->failures();
40
- if ( isset( $gce_fails['php'] ) ) {
41
- // Halt the rest of the plugin execution if PHP check fails.
42
- return;
43
- }
44
-
45
- }
46
-
47
- /*
48
- * Include the main plugin file
49
  *
50
- * @since 2.0.0
51
- */
52
- require_once( 'class-google-calendar-events.php' );
53
-
54
- /**
55
- * Define constant pointing to this file
56
  *
57
- * @since 2.0.0
58
- */
59
- if( ! defined( 'GCE_MAIN_FILE' ) ) {
60
- define( 'GCE_MAIN_FILE', __FILE__ );
61
- }
62
-
63
- /*
64
- * Get instance of our plugin
65
  *
66
- * @since 2.0.0
 
67
  */
68
- add_action( 'plugins_loaded', array( 'Google_Calendar_Events', 'get_instance' ) );
69
 
70
- /*
71
- * If we are in admin then load the Admin class
72
- *
73
- * @since 2.0.0
74
- */
75
- if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) {
76
- require_once( 'class-google-calendar-events-admin.php' );
77
 
78
- // Register hooks that are fired when the plugin is activated, deactivated, and uninstalled, respectively.
79
- register_activation_hook( __FILE__, array( 'Google_Calendar_Events_Admin', 'activate' ) );
80
- register_deactivation_hook( __FILE__, array( 'Google_Calendar_Events_Admin', 'deactivate' ) );
 
 
 
81
 
82
- // Get plugin admin class instance
83
- add_action( 'plugins_loaded', array( 'Google_Calendar_Events_Admin', 'get_instance' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
 
 
1
  <?php
2
  /**
3
+ * Plugin Name: Simple Calendar
4
+ * Plugin URI: https://wordpress.org/plugins/google-calendar-events/
5
  * Description: Show off your Google calendar in grid (month) or list view, in a post, page or widget, and in a style that matches your site.
 
 
 
 
 
6
  *
7
+ * Version: 3.0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  *
9
+ * Author: Moonstone Media
10
+ * Author URI: http://moonstonemediagroup.com
 
 
 
 
11
  *
12
+ * Text Domain: google-calendar-events
13
+ * Domain Path: /languages
 
 
 
 
 
 
14
  *
15
+ * @package SimpleCalendar
16
+ * @copyright 2014-2015 Moonstone Media/Phil Derksen. All rights reserved.
17
  */
 
18
 
19
+ // Exit if accessed directly.
20
+ if ( ! defined( 'ABSPATH' ) ) {
21
+ exit;
22
+ }
 
 
 
23
 
24
+ // Composer fallback for PHP < 5.3.0.
25
+ if ( version_compare( PHP_VERSION, '5.3.0' ) === -1 ) {
26
+ include_once 'vendor/autoload_52.php';
27
+ } else {
28
+ include_once 'vendor/autoload.php';
29
+ }
30
 
31
+ // Plugin constants.
32
+ $this_plugin_path = trailingslashit( dirname( __FILE__ ) );
33
+ $this_plugin_dir = plugin_dir_url( __FILE__ );
34
+ $this_plugin_constants = array(
35
+ 'SIMPLE_CALENDAR_VERSION' => '3.0.0',
36
+ 'SIMPLE_CALENDAR_MAIN_FILE' => __FILE__,
37
+ 'SIMPLE_CALENDAR_URL' => $this_plugin_dir,
38
+ 'SIMPLE_CALENDAR_ASSETS' => $this_plugin_dir . 'assets/',
39
+ 'SIMPLE_CALENDAR_PATH' => $this_plugin_path,
40
+ 'SIMPLE_CALENDAR_INC' => $this_plugin_path . 'includes/',
41
+ );
42
+ foreach ( $this_plugin_constants as $constant => $value ) {
43
+ if ( ! defined( $constant ) ) {
44
+ define( $constant, $value );
45
+ }
46
  }
47
 
48
+ // Check plugin requirements before loading plugin.
49
+ $this_plugin_checks = new WP_Requirements(
50
+ 'Simple Calendar',
51
+ plugin_basename( __FILE__ ),
52
+ array(
53
+ 'PHP' => '5.3.2',
54
+ 'WordPress' => '4.0.0',
55
+ )
56
+ );
57
+ if ( $this_plugin_checks->pass() === false ) {
58
+ $this_plugin_checks->halt();
59
+ return;
60
+ }
61
 
62
+ // Load plugin.
63
+ include_once 'includes/main.php';
includes/abstracts/admin-page.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Settings Page
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Abstracts;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * The Admin Page.
15
+ *
16
+ * @since 3.0.0
17
+ */
18
+ abstract class Admin_Page {
19
+
20
+ /**
21
+ * Admin page ID.
22
+ *
23
+ * @access public
24
+ * @var string
25
+ */
26
+ public $id = '';
27
+
28
+ /**
29
+ * Option group.
30
+ *
31
+ * @access public
32
+ * @var string
33
+ */
34
+ public $option_group = '';
35
+
36
+ /**
37
+ * Admin Page label.
38
+ *
39
+ * @access public
40
+ * @var string
41
+ */
42
+ public $label = '';
43
+
44
+ /**
45
+ * Admin Page description.
46
+ *
47
+ * @access public
48
+ * @var string
49
+ */
50
+ public $description = '';
51
+
52
+ /**
53
+ * Amdin Page settings sections.
54
+ *
55
+ * @access public
56
+ * @var array Associative array with section id (key) and section name (value)
57
+ */
58
+ public $sections;
59
+
60
+ /**
61
+ * Admin Page settings fields.
62
+ *
63
+ * @access public
64
+ * @var array
65
+ */
66
+ public $fields;
67
+
68
+ /**
69
+ * Saved values.
70
+ *
71
+ * @access protected
72
+ * @var array
73
+ */
74
+ protected $values = array();
75
+
76
+ /**
77
+ * Get admin page settings.
78
+ *
79
+ * @since 3.0.0
80
+ *
81
+ * @return array
82
+ */
83
+ public function get_settings() {
84
+
85
+ $settings = array();
86
+
87
+ $settings[ $this->id ] = array(
88
+ 'label' => $this->label,
89
+ 'description' => $this->description,
90
+ );
91
+
92
+ if ( ! empty( $this->sections ) && is_array( $this->sections ) ) {
93
+
94
+ foreach ( $this->sections as $section => $content ) {
95
+
96
+ $settings[ $this->id ]['sections'][ $section ] = array(
97
+ 'title' => isset( $content['title'] ) ? $content['title'] : '',
98
+ 'description' => isset( $content['description'] ) ? $content['description'] : '',
99
+ 'callback' => array( $this, 'add_settings_section_callback' ),
100
+ 'fields' => isset( $this->fields[ $section ] ) ? $this->fields[ $section ] : '',
101
+ );
102
+
103
+ }
104
+
105
+ }
106
+
107
+ return apply_filters( 'simcal_get_' . $this->option_group . '_' . $this->id , $settings );
108
+ }
109
+
110
+ /**
111
+ * Get option value.
112
+ *
113
+ * @since 3.0.0
114
+ * @access protected
115
+ *
116
+ * @param string $section
117
+ * @param string $setting
118
+ *
119
+ * @return string
120
+ */
121
+ protected function get_option_value( $section, $setting ) {
122
+
123
+ $option = $this->values;
124
+
125
+ if ( ! empty( $option ) && is_array( $option ) ) {
126
+ return isset( $option[ $section ][ $setting ] ) ? $option[ $section ][ $setting ] : '';
127
+ }
128
+
129
+ return '';
130
+ }
131
+
132
+ /**
133
+ * Add sections for this page.
134
+ *
135
+ * @since 3.0.0
136
+ *
137
+ * @return array
138
+ */
139
+ abstract public function add_sections();
140
+
141
+ /**
142
+ * Get settings fields.
143
+ *
144
+ * @since 3.0.0
145
+ *
146
+ * @return array
147
+ */
148
+ abstract public function add_fields();
149
+
150
+ /**
151
+ * Default basic callback for page sections.
152
+ *
153
+ * @since 3.0.0
154
+ *
155
+ * @param array $section
156
+ *
157
+ * @return string
158
+ */
159
+ public function add_settings_section_callback( $section ) {
160
+
161
+ $callback = isset( $section['callback'][0] ) ? $section['callback'][0] : '';
162
+ $sections = isset( $callback->sections ) ? $callback->sections : '';
163
+ $description = isset( $sections[ $section['id'] ]['description'] ) ? $sections[ $section['id'] ]['description'] : '';
164
+ $default = $description ? '<p>' . $description . '</p>' : '';
165
+
166
+ echo apply_filters( 'simcal_' . $this->option_group . '_' . $this->id . '_sections_callback', $default );
167
+ }
168
+
169
+ /**
170
+ * Register setting callback.
171
+ *
172
+ * Callback function for sanitizing and validating options before they are updated.
173
+ *
174
+ * @since 3.0.0
175
+ *
176
+ * @param array $settings Settings inputs.
177
+ *
178
+ * @return array Sanitized settings.
179
+ */
180
+ public function validate( $settings ) {
181
+
182
+ $sanitized = '';
183
+
184
+ if ( is_array( $settings ) ) {
185
+ foreach ( $settings as $k => $v ) {
186
+ $sanitized[ $k ] = simcal_sanitize_input( $v );
187
+ }
188
+ } else {
189
+ $sanitized = simcal_sanitize_input( $settings );
190
+ }
191
+
192
+ return $sanitized;
193
+ }
194
+
195
+ }
includes/abstracts/calendar-view.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Calendar View
4
+ *
5
+ * @package SimpleCalendar/Calendars
6
+ */
7
+ namespace SimpleCalendar\Abstracts;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * The Calendar View.
15
+ *
16
+ * An individual view, handling assets and markup, of a specific Calendar.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ interface Calendar_View {
21
+
22
+ /**
23
+ * Constructor.
24
+ *
25
+ * @since 3.0.0
26
+ *
27
+ * @param mixed $args
28
+ */
29
+ public function __construct( $args = '' );
30
+
31
+ /**
32
+ * Return the parent calendar type.
33
+ *
34
+ * @since 3.0.0
35
+ *
36
+ * @return string
37
+ */
38
+ public function get_parent();
39
+
40
+ /**
41
+ * Return the view type.
42
+ *
43
+ * @since 3.0.0
44
+ *
45
+ * @return string
46
+ */
47
+ public function get_type();
48
+
49
+ /**
50
+ * Return the view name.
51
+ *
52
+ * @since 3.0.0
53
+ *
54
+ * @return string
55
+ */
56
+ public function get_name();
57
+
58
+ /**
59
+ * Add ajax actions.
60
+ *
61
+ * @since 3.0.0
62
+ *
63
+ * @return void
64
+ */
65
+ public function add_ajax_actions();
66
+
67
+ /**
68
+ * Scripts.
69
+ *
70
+ * Returns the view scripts as associative array.
71
+ *
72
+ * @since 3.0.0
73
+ *
74
+ * @param string $min
75
+ *
76
+ * @return array
77
+ */
78
+ public function scripts( $min = '' );
79
+
80
+ /**
81
+ * Styles.
82
+ *
83
+ * Returns the view styles as associative array.
84
+ *
85
+ * @since 3.0.0
86
+ *
87
+ * @param string $min
88
+ *
89
+ * @return array
90
+ */
91
+ public function styles( $min = '' );
92
+
93
+ /**
94
+ * Print HTML.
95
+ *
96
+ * Echoes the view markup.
97
+ *
98
+ * @since 3.0.0
99
+ *
100
+ * @return void
101
+ */
102
+ public function html();
103
+
104
+ }
includes/abstracts/calendar.php ADDED
@@ -0,0 +1,781 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Calendar
4
+ *
5
+ * @package SimpleCalendar/Calendars
6
+ */
7
+ namespace SimpleCalendar\Abstracts;
8
+
9
+ use Carbon\Carbon;
10
+ use SimpleCalendar\Events\Event;
11
+ use SimpleCalendar\Events\Event_Builder;
12
+ use SimpleCalendar\Events\Events;
13
+
14
+ if ( ! defined( 'ABSPATH' ) ) {
15
+ exit;
16
+ }
17
+
18
+ /**
19
+ * The Calendar.
20
+ *
21
+ * Displays events from events feed.
22
+ */
23
+ abstract class Calendar {
24
+
25
+ /**
26
+ * Calendar Id.
27
+ *
28
+ * @access public
29
+ * @var int
30
+ */
31
+ public $id = 0;
32
+
33
+ /**
34
+ * Calendar post object.
35
+ *
36
+ * @access public
37
+ * @var \WP_Post
38
+ */
39
+ public $post = null;
40
+
41
+ /**
42
+ * Calendar Type.
43
+ *
44
+ * @access public
45
+ * @var string
46
+ */
47
+ public $type = '';
48
+
49
+ /**
50
+ * Calendar Name.
51
+ *
52
+ * @access public
53
+ * @var string
54
+ */
55
+ public $name = '';
56
+
57
+ /**
58
+ * Feed type.
59
+ *
60
+ * @access public
61
+ * @var string
62
+ */
63
+ public $feed = '';
64
+
65
+ /**
66
+ * Calendar start.
67
+ *
68
+ * @access public
69
+ * @var int
70
+ */
71
+ public $start = 0;
72
+
73
+ /**
74
+ * Calendar end.
75
+ *
76
+ * @access public
77
+ * @var int
78
+ */
79
+ public $end = 0;
80
+
81
+ /**
82
+ * Static calendar.
83
+ *
84
+ * @access public
85
+ * @var bool
86
+ */
87
+ public $static = false;
88
+
89
+ /**
90
+ * Today.
91
+ *
92
+ * @access public
93
+ * @var int
94
+ */
95
+ public $today = 0;
96
+
97
+ /**
98
+ * Time now.
99
+ *
100
+ * @access public
101
+ * @var int
102
+ */
103
+ public $now = 0;
104
+
105
+ /**
106
+ * Timezone offset.
107
+ *
108
+ * @access public
109
+ * @var int
110
+ */
111
+ public $offset = 0;
112
+
113
+ /**
114
+ * Timezone
115
+ *
116
+ * @access public
117
+ * @var string
118
+ */
119
+ public $timezone = 'UTC';
120
+
121
+ /**
122
+ * Site timezone.
123
+ *
124
+ * @access public
125
+ * @var string
126
+ */
127
+ public $site_timezone = 'UTC';
128
+
129
+ /**
130
+ * Date format.
131
+ *
132
+ * @access public
133
+ * @var string
134
+ */
135
+ public $date_format = '';
136
+
137
+ /**
138
+ * Time format.
139
+ *
140
+ * @access public
141
+ * @var string
142
+ */
143
+ public $time_format = '';
144
+
145
+ /**
146
+ * Date-time separator.
147
+ *
148
+ * @access public
149
+ * @var string
150
+ */
151
+ public $datetime_separator = '@';
152
+
153
+ /**
154
+ * First day of the week.
155
+ *
156
+ * @access public
157
+ * @var int
158
+ */
159
+ public $week_starts = 0;
160
+
161
+ /**
162
+ * Events to display.
163
+ *
164
+ * @access public
165
+ * @var array
166
+ */
167
+ public $events = array();
168
+
169
+ /**
170
+ * Errors.
171
+ *
172
+ * @access public
173
+ * @var array
174
+ */
175
+ protected $errors = array();
176
+
177
+ /**
178
+ * Earliest event.
179
+ *
180
+ * @access public
181
+ * @var int
182
+ */
183
+ public $earliest_event = 0;
184
+
185
+ /**
186
+ * Latest event.
187
+ *
188
+ * @access public
189
+ * @var int
190
+ */
191
+ public $latest_event = 0;
192
+
193
+ /**
194
+ * Event builder content.
195
+ *
196
+ * @access public
197
+ * @var string
198
+ */
199
+ public $events_template = '';
200
+
201
+ /**
202
+ * Calendar Views.
203
+ *
204
+ * The calendar available views.
205
+ *
206
+ * @access public
207
+ * @var array
208
+ */
209
+ public $views = array();
210
+
211
+ /**
212
+ * Calendar View.
213
+ *
214
+ * The current view.
215
+ *
216
+ * @access public
217
+ * @var Calendar_View
218
+ */
219
+ public $view = null;
220
+
221
+ /**
222
+ * Calendar settings.
223
+ *
224
+ * @access protected
225
+ * @var array
226
+ */
227
+ protected $settings = array();
228
+
229
+ /**
230
+ * Constructor.
231
+ *
232
+ * @since 3.0.0
233
+ *
234
+ * @param int|object|\WP_Post|Calendar $calendar
235
+ * @param string $view
236
+ */
237
+ public function __construct( $calendar, $view = '' ) {
238
+
239
+ // Set the post object.
240
+ $this->set_post_object( $calendar );
241
+
242
+ if ( ! is_null( $this->post ) ) {
243
+
244
+ // Set calendar type and events source.
245
+ $this->set_taxonomies();
246
+
247
+ // Set calendar default datetime properties.
248
+ $this->set_timezone();
249
+ $this->set_start_of_week();
250
+ $this->set_static();
251
+
252
+ // Set calendar start.
253
+ $this->set_start();
254
+
255
+ // Set the events template.
256
+ $this->set_events_template();
257
+
258
+ // Get events source data.
259
+ $feed = simcal_get_feed( $this );
260
+ if ( $feed instanceof Feed ) {
261
+ if ( ! empty( $feed->events ) ) {
262
+ if ( is_array( $feed->events ) ) {
263
+ $this->set_events( $feed->events );
264
+ if ( 'use_calendar' == get_post_meta( $this->id, '_feed_timezone_setting', true ) ) {
265
+ $this->timezone = $feed->timezone;
266
+ $this->set_start( $feed->timezone );
267
+ }
268
+ } elseif ( is_string( $feed->events ) ) {
269
+ $this->errors[] = $feed->events;
270
+ }
271
+ }
272
+ }
273
+
274
+ // Set general purpose timestamps.
275
+ $now = Carbon::now( $this->timezone );
276
+ $this->now = $now->getTimestamp();
277
+ $this->today = $now->startOfDay()->getTimestamp();
278
+ $this->offset = $now->getOffset();
279
+
280
+ // Set date time formatting.
281
+ $this->set_date_format();
282
+ $this->set_time_format();
283
+ $this->set_datetime_separator();
284
+
285
+ // Set earliest and latest event timestamps.
286
+ if ( $this->events && is_array( $this->events ) ) {
287
+ $this->earliest_event = intval( current( array_keys( $this->events ) ) );
288
+ $this->latest_event = intval( key( array_slice( $this->events, -1, 1, true ) ) );
289
+ }
290
+
291
+ // Set calendar end.
292
+ $this->set_end();
293
+
294
+ // Set view.
295
+ if ( ! $view ) {
296
+
297
+ $calendar_view = get_post_meta( $this->id, '_calendar_view', true );
298
+ $calendar_view = isset( $calendar_view[ $this->type ] ) ? $calendar_view[ $this->type ] : '';
299
+
300
+ $view = esc_attr( $calendar_view );
301
+ }
302
+ }
303
+
304
+ // Get view.
305
+ $this->view = $this->get_view( $view );
306
+ }
307
+
308
+ /**
309
+ * Overloading __isset function with post meta.
310
+ *
311
+ * @since 3.0.0
312
+ *
313
+ * @param mixed $key Post meta key.
314
+ *
315
+ * @return bool
316
+ */
317
+ public function __isset( $key ) {
318
+ return metadata_exists( 'post', $this->id, '_' . $key );
319
+ }
320
+
321
+ /**
322
+ * Overloading __get function with post meta.
323
+ *
324
+ * @since 3.0.0
325
+ *
326
+ * @param string $key Post meta key.
327
+ *
328
+ * @return mixed
329
+ */
330
+ public function __get( $key ) {
331
+ $value = get_post_meta( $this->id, '_' . $key, true );
332
+ if ( ! empty( $value ) ) {
333
+ $this->$key = $value;
334
+ }
335
+ return $value;
336
+ }
337
+
338
+ /**
339
+ * Set post object and id.
340
+ *
341
+ * @since 3.0.0
342
+ *
343
+ * @param int|object|\WP_Post|Calendar $calendar
344
+ */
345
+ public function set_post_object( $calendar ) {
346
+ if ( is_numeric( $calendar ) ) {
347
+ $this->id = absint( $calendar );
348
+ $this->post = get_post( $this->id );
349
+ } elseif ( $calendar instanceof Calendar ) {
350
+ $this->id = absint( $calendar->id );
351
+ $this->post = $calendar->post;
352
+ } elseif ( $calendar instanceof \WP_Post ) {
353
+ $this->id = absint( $calendar->ID );
354
+ $this->post = $calendar;
355
+ } elseif ( isset( $calendar->id ) && isset( $calendar->post ) ) {
356
+ $this->id = $calendar->id;
357
+ $this->post = $calendar->post;
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Return the calendar title.
363
+ *
364
+ * @since 3.0.0
365
+ *
366
+ * @return string
367
+ */
368
+ public function get_title() {
369
+ $title = isset( $this->post->post_title ) ? $this->post->post_title : '';
370
+ return apply_filters( 'simcal_calendar_title', $title );
371
+ }
372
+
373
+ /**
374
+ * Get the calendar post data.
375
+ *
376
+ * @since 3.0.0
377
+ *
378
+ * @return \WP_Post
379
+ */
380
+ public function get_post_data() {
381
+ return $this->post;
382
+ }
383
+
384
+ /**
385
+ * Set taxonomies.
386
+ *
387
+ * @since 3.0.0
388
+ * @access protected
389
+ */
390
+ protected function set_taxonomies() {
391
+ // Set calendar type.
392
+ if ( $type = wp_get_object_terms( $this->id, 'calendar_type' ) ) {
393
+ $this->type = sanitize_title( current( $type )->name );
394
+ } else {
395
+ $this->type = apply_filters( 'simcal_calendar_default_type', 'default-calendar' );
396
+ }
397
+ // Set feed type.
398
+ if ( $feed_type = wp_get_object_terms( $this->id, 'calendar_feed' ) ) {
399
+ $this->feed = sanitize_title( current( $feed_type )->name );
400
+ } else {
401
+ $this->feed = apply_filters( 'simcal_calendar_default_feed', 'google' );
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Get events.
407
+ *
408
+ * @since 3.0.0
409
+ *
410
+ * @return Events
411
+ */
412
+ public function get_events() {
413
+ return new Events( $this->events, $this->timezone );
414
+ }
415
+
416
+ /**
417
+ * Set events.
418
+ *
419
+ * @since 3.0.0
420
+ *
421
+ * @param array $array
422
+ */
423
+ public function set_events( array $array ) {
424
+
425
+ $events = array();
426
+
427
+ if ( ! empty( $array ) ) {
428
+ foreach ( $array as $tz => $e ) {
429
+ foreach ( $e as $event ) {
430
+ $events[ $tz ][] = $event instanceof Event ? $event : new Event( $event );
431
+ }
432
+ }
433
+ }
434
+
435
+ $this->events = $events;
436
+ }
437
+
438
+ /**
439
+ * Get the event builder template.
440
+ *
441
+ * @since 3.0.0
442
+ *
443
+ * @param string $template
444
+ *
445
+ * @return string
446
+ */
447
+ public function set_events_template( $template = '' ) {
448
+ if ( empty( $template ) ) {
449
+ $template = isset( $this->post->post_content ) ? $this->post->post_content : '';
450
+ }
451
+ $this->events_template = wpautop( wp_kses_post( trim( $template ) ) );
452
+ }
453
+
454
+ /**
455
+ * Set the timezone.
456
+ *
457
+ * @since 3.0.0
458
+ *
459
+ * @param string $tz Timezone.
460
+ */
461
+ public function set_timezone( $tz = '' ) {
462
+
463
+ $site_tz = esc_attr( simcal_get_wp_timezone() );
464
+
465
+ if ( empty( $tz ) ) {
466
+
467
+ $timezone_setting = get_post_meta( $this->id, '_feed_timezone_setting', true );
468
+
469
+ if ( 'use_site' == $timezone_setting ) {
470
+ $tz = $site_tz;
471
+ } elseif ( 'use_custom' == $timezone_setting ) {
472
+ $custom_timezone = esc_attr( get_post_meta( $this->id, '_feed_timezone', true ) );
473
+ // One may be using a non standard timezone in GMT (UTC) offset format.
474
+ if ( ( strpos( $custom_timezone, 'UTC+' ) === 0 ) || ( strpos( $custom_timezone, 'UTC-' ) === 0 ) ) {
475
+ $tz = simcal_get_timezone_from_gmt_offset( substr( $custom_timezone, 3 ) );
476
+ } else {
477
+ $tz = ! empty( $custom_timezone ) ? $custom_timezone : 'UTC';
478
+ }
479
+ }
480
+
481
+ $this->timezone = empty( $tz ) ? 'UTC' : $tz;
482
+ return;
483
+ }
484
+
485
+ $this->site_timezone = $site_tz;
486
+ $this->timezone = simcal_esc_timezone( $tz, $this->timezone );
487
+ }
488
+
489
+ /**
490
+ * Set date format.
491
+ *
492
+ * @since 3.0.0
493
+ *
494
+ * @param string $format PHP datetime format.
495
+ */
496
+ public function set_date_format( $format = '' ) {
497
+
498
+ $date_format_custom = $date_format_default = $format;
499
+
500
+ if ( empty( $date_format_custom ) ) {
501
+
502
+ $date_format_option = esc_attr( get_post_meta( $this->id, '_calendar_date_format_setting', true ) );
503
+ $date_format_default = esc_attr( get_option( 'date_format' ) );
504
+ $date_format_custom = '';
505
+
506
+ if ( 'use_custom' == $date_format_option ) {
507
+ $date_format_custom = esc_attr( get_post_meta( $this->id, '_calendar_date_format', true ) );
508
+ } elseif ( 'use_custom_php' ) {
509
+ $date_format_custom = esc_attr( get_post_meta( $this->id, '_calendar_date_format_php', true ) );
510
+ }
511
+ }
512
+
513
+ $this->date_format = $date_format_custom ? $date_format_custom : $date_format_default;
514
+ }
515
+
516
+ /**
517
+ * Set time format.
518
+ *
519
+ * @since 3.0.0
520
+ *
521
+ * @param string $format PHP datetime format.
522
+ */
523
+ public function set_time_format( $format = '' ) {
524
+
525
+ $time_format_custom = $time_format_default = $format;
526
+
527
+ if ( empty( $time_format_custom ) ) {
528
+
529
+ $time_format_option = esc_attr( get_post_meta( $this->id, '_calendar_time_format_setting', true ) );
530
+ $time_format_default = esc_attr( get_option( 'time_format' ) );
531
+ $time_format_custom = '';
532
+
533
+ if ( 'use_custom' == $time_format_option ) {
534
+ $time_format_custom = esc_attr( get_post_meta( $this->id, '_calendar_time_format', true ) );
535
+ } elseif ( 'use_custom_php' ) {
536
+ $time_format_custom = esc_attr( get_post_meta( $this->id, '_calendar_time_format_php', true ) );
537
+ }
538
+ }
539
+
540
+ $this->time_format = $time_format_custom ? $time_format_custom : $time_format_default;
541
+ }
542
+
543
+ /**
544
+ * Set date-time separator.
545
+ *
546
+ * @since 3.0.0
547
+ *
548
+ * @param string $separator A UTF8 character used as separator.
549
+ */
550
+ public function set_datetime_separator( $separator = '' ) {
551
+
552
+ if ( empty( $separator ) ) {
553
+ $separator = get_post_meta( $this->id, '_calendar_datetime_separator', true );
554
+ }
555
+
556
+ $this->datetime_separator = esc_attr( $separator );
557
+ }
558
+
559
+ /**
560
+ * Set start of week.
561
+ *
562
+ * @since 3.0.0
563
+ *
564
+ * @param int $weekday From 0 (Sunday) to 6 (Friday).
565
+ */
566
+ public function set_start_of_week( $weekday = -1 ) {
567
+
568
+ $week_starts = is_int( $weekday ) ? $weekday : -1;
569
+
570
+ if ( $week_starts < 0 || $week_starts > 6 ) {
571
+
572
+ $week_starts_setting = get_post_meta( $this->id, '_calendar_week_starts_on_setting', true );
573
+ $week_starts = intval( get_option( 'start_of_week' ) );
574
+
575
+ if ( 'use_custom' == $week_starts_setting ) {
576
+ $week_starts_on = get_post_meta( $this->id, '_calendar_week_starts_on', true );
577
+ $week_starts = is_numeric( $week_starts_on ) ? intval( $week_starts_on ) : $week_starts;
578
+ }
579
+ }
580
+
581
+ $this->week_starts = $week_starts;
582
+ }
583
+
584
+ /**
585
+ * Set calendar start.
586
+ *
587
+ * @since 3.0.0
588
+ *
589
+ * @param int $timestamp
590
+ */
591
+ public function set_start( $timestamp = 0 ) {
592
+
593
+ if ( is_int( $timestamp ) && $timestamp !== 0 ) {
594
+ $this->start = $timestamp;
595
+ return;
596
+ }
597
+
598
+ $this->start = Carbon::now( $this->timezone )->getTimestamp();
599
+
600
+ $calendar_begins = esc_attr( get_post_meta( $this->id, '_calendar_begins', true ) );
601
+ $nth = max( absint( get_post_meta( $this->id, '_calendar_begins_nth' ) ), 1 );
602
+
603
+ if ( 'today' == $calendar_begins ) {
604
+ $this->start = Carbon::today( $this->timezone )->getTimestamp();
605
+ } elseif ( 'days_before' == $calendar_begins ) {
606
+ $this->start = Carbon::today( $this->timezone )->subDays( $nth )->getTimestamp();
607
+ } elseif ( 'days_after' == $calendar_begins ) {
608
+ $this->start = Carbon::today( $this->timezone )->addDays( $nth )->getTimestamp();
609
+ } elseif ( 'this_week' == $calendar_begins ) {
610
+ $week = new Carbon( 'now', $this->timezone );
611
+ $week->setWeekStartsAt( $this->week_starts );
612
+ $this->start = $week->startOfWeek()->getTimestamp();
613
+ } elseif ( 'weeks_before' == $calendar_begins ) {
614
+ $week = new Carbon( 'now', $this->timezone );
615
+ $week->setWeekStartsAt( $this->week_starts );
616
+ $this->start = $week->startOfWeek()->subWeeks( $nth )->getTimestamp();
617
+ } elseif ( 'weeks_after' == $calendar_begins ) {
618
+ $week = new Carbon( 'now', $this->timezone );
619
+ $week->setWeekStartsAt( $this->week_starts );
620
+ $this->start = $week->startOfWeek()->addWeeks( $nth )->getTimestamp();
621
+ } elseif ( 'this_month' == $calendar_begins ) {
622
+ $this->start = Carbon::today( $this->timezone )->startOfMonth()->getTimeStamp();
623
+ } elseif ( 'months_before' == $calendar_begins ) {
624
+ $this->start = Carbon::today( $this->timezone )->subMonths( $nth )->startOfMonth()->getTimeStamp();
625
+ } elseif ( 'months_after' == $calendar_begins ) {
626
+ $this->start = Carbon::today( $this->timezone )->addMonths( $nth )->startOfMonth()->getTimeStamp();
627
+ } elseif ( 'this_year' == $calendar_begins ) {
628
+ $this->start = Carbon::today( $this->timezone )->startOfYear()->getTimestamp();
629
+ } elseif ( 'years_before' == $calendar_begins ) {
630
+ $this->start = Carbon::today( $this->timezone )->subYears( $nth )->startOfYear()->getTimeStamp();
631
+ } elseif ( 'years_after' == $calendar_begins ) {
632
+ $this->start = Carbon::today( $this->timezone )->addYears( $nth )->startOfYear()->getTimeStamp();
633
+ } elseif ( 'custom_date' == $calendar_begins ) {
634
+ if ( $date = get_post_meta( $this->id, '_calendar_begins_custom_date', true ) ) {
635
+ $this->start = Carbon::createFromFormat( 'Y-m-d', esc_attr( $date ) )->setTimezone( $this->timezone )->getTimestamp();
636
+ }
637
+ }
638
+ }
639
+
640
+ /**
641
+ * Set calendar end.
642
+ *
643
+ * @since 3.0.0
644
+ *
645
+ * @param int $timestamp
646
+ */
647
+ public function set_end( $timestamp = 0 ) {
648
+ $latest = is_int( $timestamp ) && $timestamp !== 0 ? $timestamp : $this->latest_event;
649
+ $this->end = $latest > $this->start ? $latest : $this->start;
650
+ }
651
+
652
+ /**
653
+ * Set the calendar to static.
654
+ *
655
+ * @since 3.0.0
656
+ *
657
+ * @param string|bool $static
658
+ */
659
+ public function set_static( $static = '' ) {
660
+
661
+ if ( ! empty( $static ) && is_bool( $static ) ) {
662
+ $this->static = $static;
663
+ return;
664
+ }
665
+
666
+ if ( 'yes' == get_post_meta( $this->id, '_calendar_is_static', true ) ) {
667
+ $this->static = true;
668
+ return;
669
+ }
670
+
671
+ $this->static = false;
672
+ }
673
+
674
+ /**
675
+ * Input fields for settings page.
676
+ *
677
+ * @since 3.0.0
678
+ *
679
+ * @return array
680
+ */
681
+ public function settings_fields() {
682
+ return $this->settings;
683
+ }
684
+
685
+ /**
686
+ * Get a calendar view.
687
+ *
688
+ * @since 3.0.0
689
+ *
690
+ * @param string $view
691
+ *
692
+ * @return Calendar_View
693
+ */
694
+ abstract public function get_view( $view = '' );
695
+
696
+ /**
697
+ * Get event HTML parsed by template.
698
+ *
699
+ * @since 3.0.0
700
+ *
701
+ * @param Event $event Event object to be parsed.
702
+ * @param string $template (optional) To use another template or a partial.
703
+ *
704
+ * @return string
705
+ */
706
+ public function get_event_html( Event $event, $template = '' ) {
707
+ $event_builder = new Event_Builder( $event, $this );
708
+ // Use the event template to parse tags; if empty, fallback to calendar post content.
709
+ $template = empty( $template ) ? ( empty( $event->template ) ? $this->events_template : $event->template ) : $template;
710
+ return $event_builder->parse_event_template_tags( $template );
711
+ }
712
+
713
+ /**
714
+ * Output the calendar markup.
715
+ *
716
+ * @since 3.0.0
717
+ *
718
+ * @param string $view The calendar view to display.
719
+ */
720
+ public function html( $view = '' ) {
721
+
722
+ $view = empty( $view ) ? $this->view : $this->get_view( $view );
723
+
724
+ if ( $view instanceof Calendar_View ) {
725
+
726
+ if ( ! empty( $this->errors ) ) {
727
+
728
+ if ( current_user_can( 'manage_options' ) ) {
729
+ echo '<pre><code>';
730
+ foreach ( $this->errors as $error ) { echo $error; }
731
+ echo '</code></pre>';
732
+ }
733
+
734
+ } else {
735
+
736
+ // Get a CSS class from the class name of the calendar view (minus namespace part).
737
+ $view_name = implode( '-', array_map( 'lcfirst', explode( '_', strtolower( get_class( $view ) ) ) ) );
738
+ $view_class = substr( $view_name, strrpos( $view_name, '\\' ) + 1 );
739
+
740
+ $calendar_class = trim( implode( ' simcal-', apply_filters( 'simcal_calendar_class', array(
741
+ 'simcal-calendar',
742
+ $this->type,
743
+ $view_class,
744
+ ), $this->id ) ) );
745
+
746
+ echo '<div class="' . $calendar_class . '" '
747
+ . 'data-calendar-id="' . $this->id . '" '
748
+ . 'data-timezone="' . $this->timezone . '" '
749
+ . 'data-offset="' . $this->offset . '" '
750
+ . 'data-week-start="' . $this->week_starts . '" '
751
+ . 'data-calendar-start="' . $this->start .'" '
752
+ . 'data-calendar-end="' . $this->end . '" '
753
+ . 'data-events-first="' . $this->earliest_event .'" '
754
+ . 'data-events-last="' . $this->latest_event . '"'
755
+ . '>';
756
+
757
+ date_default_timezone_set( $this->timezone );
758
+ do_action( 'simcal_calendar_html_before', $this->id );
759
+
760
+ $view->html();
761
+
762
+ do_action( 'simcal_calendar_html_after', $this->id );
763
+ date_default_timezone_set( $this->site_timezone );
764
+
765
+ $settings = get_option( 'simple-calendar_settings_calendars' );
766
+ $poweredby = isset( $settings['poweredby']['opt_in'] ) ? $settings['poweredby']['opt_in'] : '';
767
+
768
+ if ( 'yes' == $poweredby ) {
769
+ $align = is_rtl() ? 'left' : 'right';
770
+ echo '<small class="simcal-powered simcal-align-' . $align .'">Powered by <a href="https://simplecalendar.io" target="_blank">Simple Calendar</a></small>';
771
+ }
772
+
773
+ echo '</div>';
774
+
775
+ }
776
+
777
+ }
778
+
779
+ }
780
+
781
+ }
includes/abstracts/feed.php ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Feed
4
+ *
5
+ * @package SimpleCalendar/Feeds
6
+ */
7
+ namespace SimpleCalendar\Abstracts;
8
+
9
+ use Carbon\Carbon;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * The Feed.
17
+ *
18
+ * Source of events supplied to calendars.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ abstract class Feed {
23
+
24
+ /**
25
+ * Feed type.
26
+ *
27
+ * @access public
28
+ * @var string
29
+ */
30
+ public $type = '';
31
+
32
+ /**
33
+ * Feed name.
34
+ *
35
+ * @access public
36
+ * @var string
37
+ */
38
+ public $name = '';
39
+
40
+ /**
41
+ * Calendar post id.
42
+ *
43
+ * @access public
44
+ * @var int
45
+ */
46
+ public $post_id = 0;
47
+
48
+ /**
49
+ * Calendar opening.
50
+ *
51
+ * @access protected
52
+ * @var int
53
+ */
54
+ protected $calendar_start = 0;
55
+
56
+ /**
57
+ * Start of week.
58
+ *
59
+ * @access protected
60
+ * @var int
61
+ */
62
+ protected $week_starts = 0;
63
+
64
+ /**
65
+ * Events.
66
+ *
67
+ * @access public
68
+ * @var array
69
+ */
70
+ public $events = array();
71
+
72
+ /**
73
+ * Events template.
74
+ *
75
+ * @access protected
76
+ * @var string
77
+ */
78
+ protected $events_template = '';
79
+
80
+ /**
81
+ * Timezone setting.
82
+ *
83
+ * @access protected
84
+ * @var string
85
+ */
86
+ protected $timezone_setting = '';
87
+
88
+ /**
89
+ * Timezone.
90
+ *
91
+ * @access public
92
+ * @var string
93
+ */
94
+ public $timezone = '';
95
+
96
+ /**
97
+ * Earliest possible event.
98
+ *
99
+ * @access public
100
+ * @var int
101
+ */
102
+ public $time_min = 0;
103
+
104
+ /**
105
+ * Latest possible event.
106
+ *
107
+ * @access public
108
+ * @var int
109
+ */
110
+ public $time_max = 0;
111
+
112
+ /**
113
+ * Feed cache interval.
114
+ *
115
+ * @access protected
116
+ * @var int
117
+ */
118
+ protected $cache = 7200;
119
+
120
+ /**
121
+ * Feed settings.
122
+ *
123
+ * @access protected
124
+ * @var array
125
+ */
126
+ protected $settings = array();
127
+
128
+ /**
129
+ * Constructor.
130
+ *
131
+ * @since 3.0.0
132
+ *
133
+ * @param string|Calendar $calendar
134
+ */
135
+ public function __construct( $calendar = '' ) {
136
+
137
+ if ( $calendar instanceof Calendar ) {
138
+
139
+ if ( isset( $calendar->id ) ) {
140
+ $this->post_id = $calendar->id;
141
+ }
142
+ if ( isset( $calendar->start ) ) {
143
+ $this->calendar_start = $calendar->start;
144
+ }
145
+ $this->week_starts = isset( $calendar->week_starts ) ? $calendar->week_starts : get_option( 'start_of_week' );
146
+ $this->events_template = ! empty( $calendar->events_template ) ? $calendar->events_template : simcal_default_event_template();
147
+
148
+ if ( $this->post_id > 0 ) {
149
+ $this->set_cache();
150
+ $this->timezone_setting = get_post_meta( $this->post_id, '_feed_timezone_setting', true );
151
+ $this->timezone = $calendar->timezone;
152
+ $this->set_earliest_event();
153
+ $this->set_latest_event();
154
+ }
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Input fields for settings page.
160
+ *
161
+ * @since 3.0.0
162
+ *
163
+ * @return false|array
164
+ */
165
+ public function settings_fields() {
166
+ return $this->settings;
167
+ }
168
+
169
+ /**
170
+ * Set earliest event.
171
+ *
172
+ * @since 3.0.0
173
+ *
174
+ * @param int $timestamp
175
+ */
176
+ public function set_earliest_event( $timestamp = 0 ) {
177
+
178
+ $earliest = intval( $timestamp );
179
+
180
+ if ( $earliest === 0 ) {
181
+
182
+ $start = Carbon::createFromTimestamp( $this->calendar_start, $this->timezone );
183
+
184
+ $earliest_date = esc_attr( get_post_meta( $this->post_id, '_feed_earliest_event_date', true ) );
185
+ $earliest_range = max( absint( get_post_meta( $this->post_id, '_feed_earliest_event_date_range', true ) ), 1 );
186
+
187
+ if ( 'days_before' == $earliest_date ) {
188
+ $earliest = $start->subDays( $earliest_range )->getTimestamp();
189
+ } elseif ( 'weeks_before' == $earliest_date ) {
190
+ $earliest = $start->subWeeks( $earliest_range )->addDay()->getTimestamp();
191
+ } elseif ( 'months_before' == $earliest_date ) {
192
+ $earliest = $start->subMonths( $earliest_range )->addDay()->getTimestamp();
193
+ } elseif ( 'years_before' == $earliest_date ) {
194
+ $earliest = $start->subYears( $earliest_range )->addDay()->getTimestamp();
195
+ } else {
196
+ $earliest = $start->getTimestamp();
197
+ }
198
+ }
199
+
200
+ $this->time_min = $earliest;
201
+ }
202
+
203
+ /**
204
+ * Set latest event.
205
+ *
206
+ * @since 3.0.0
207
+ *
208
+ * @param int $timestamp
209
+ */
210
+ public function set_latest_event( $timestamp = 0 ) {
211
+
212
+ $latest = intval( $timestamp );
213
+
214
+ if ( $latest === 0 ) {
215
+
216
+ $start = Carbon::createFromTimestamp( $this->calendar_start, $this->timezone )->endOfDay();
217
+
218
+ $latest_date = esc_attr( get_post_meta( $this->post_id, '_feed_latest_event_date', true ) );
219
+ $latest_range = max( absint( get_post_meta( $this->post_id, '_feed_latest_event_date_range', true ) ), 1 );
220
+
221
+ if ( 'days_after' == $latest_date ) {
222
+ $latest = $start->addDays( $latest_range )->getTimestamp();
223
+ } elseif ( 'weeks_after' == $latest_date ) {
224
+ $latest = $start->addWeeks( $latest_range )->subDay()->getTimestamp();
225
+ } elseif ( 'months_after' == $latest_date ) {
226
+ $latest = $start->addMonths( $latest_range )->subDay()->getTimestamp();
227
+ } elseif ( 'years_after' == $latest_date ) {
228
+ $latest = $start->addYears( $latest_range )->subDay()->getTimestamp();
229
+ } else {
230
+ $latest = $start->getTimestamp();
231
+ }
232
+
233
+ }
234
+
235
+ $this->time_max = $latest;
236
+ }
237
+
238
+ /**
239
+ * Set cache.
240
+ *
241
+ * @since 3.0.0
242
+ *
243
+ * @param int $time
244
+ */
245
+ public function set_cache( $time = 0 ) {
246
+ if ( $time === 0 || ! is_numeric( $time ) ) {
247
+ $cache = get_post_meta( $this->post_id, '_feed_cache', true );
248
+ $time = is_numeric( $cache ) && $cache > 0 ? absint( $cache ) : $this->cache;
249
+ }
250
+ $this->cache = absint( $time );
251
+ }
252
+
253
+ /**
254
+ * Get events feed.
255
+ *
256
+ * @since 3.0.0
257
+ *
258
+ * @return array
259
+ */
260
+ abstract public function get_events();
261
+
262
+ }
includes/abstracts/field.php ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Input Field
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Abstracts;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * The Field.
15
+ *
16
+ * Object for handling an input field in plugin back end.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ abstract class Field {
21
+
22
+ /**
23
+ * Field type.
24
+ *
25
+ * @access public
26
+ * @var string
27
+ */
28
+ public $type = '';
29
+
30
+ /**
31
+ * Field name.
32
+ *
33
+ * @access public
34
+ * @var string
35
+ */
36
+ public $name = '';
37
+
38
+ /**
39
+ * Field Id.
40
+ *
41
+ * @access public
42
+ * @var string
43
+ */
44
+ public $id = '';
45
+
46
+ /**
47
+ * Field title (label).
48
+ *
49
+ * @access public
50
+ * @var string
51
+ */
52
+ public $title = '';
53
+
54
+ /**
55
+ * Field type class.
56
+ *
57
+ * @access protected
58
+ * @var string
59
+ */
60
+ protected $type_class = '';
61
+
62
+ /**
63
+ * CSS classes.
64
+ *
65
+ * @access public
66
+ * @var string
67
+ */
68
+ public $class = '';
69
+
70
+ /**
71
+ * CSS styles.
72
+ *
73
+ * @access public
74
+ * @var string
75
+ */
76
+ public $style = '';
77
+
78
+ /**
79
+ * Description.
80
+ *
81
+ * @access public
82
+ * @var string
83
+ */
84
+ public $description = '';
85
+
86
+ /**
87
+ * Tooltip text.
88
+ *
89
+ * @access public
90
+ * @var string
91
+ */
92
+ public $tooltip = '';
93
+
94
+ /**
95
+ * Attributes.
96
+ *
97
+ * @access public
98
+ * @var string
99
+ */
100
+ public $attributes = '';
101
+
102
+ /**
103
+ * Placeholder.
104
+ *
105
+ * @access public
106
+ * @var string
107
+ */
108
+ public $placeholder = '';
109
+
110
+ /**
111
+ * Options.
112
+ *
113
+ * @access public
114
+ * @var array
115
+ */
116
+ public $options = array();
117
+
118
+ /**
119
+ * Value.
120
+ *
121
+ * @access public
122
+ * @var array|string
123
+ */
124
+ public $value = '';
125
+
126
+ /**
127
+ * Default value.
128
+ *
129
+ * @access public
130
+ * @var array|string
131
+ */
132
+ public $default = '';
133
+
134
+ /**
135
+ * Validation result.
136
+ *
137
+ * @access public
138
+ * @var string|true
139
+ */
140
+ public $validation = true;
141
+
142
+ /**
143
+ * Construct the field.
144
+ *
145
+ * Escapes and sets every field property.
146
+ *
147
+ * @since 3.0.0
148
+ *
149
+ * @param array $field Field data.
150
+ */
151
+ public function __construct( $field ) {
152
+
153
+ // Field properties.
154
+ if ( isset( $field['title'] ) ) {
155
+ $this->title = esc_attr( $field['title'] );
156
+ }
157
+ if ( isset( $field['description'] ) ) {
158
+ $this->description = wp_kses_post( $field['description'] );
159
+ }
160
+ if ( isset( $field['type'] ) ) {
161
+ $this->type = esc_attr( $field['type'] );
162
+ }
163
+ if ( isset( $field['name'] ) ) {
164
+ $this->name = esc_attr( $field['name'] );
165
+ }
166
+ if ( isset( $field['id'] ) ) {
167
+ $this->id = esc_attr( $field['id'] );
168
+ }
169
+ if ( isset( $field['placeholder'] ) ) {
170
+ $this->placeholder = esc_attr( $field['placeholder'] );
171
+ }
172
+ if ( isset( $field['options'] ) && is_array( $field['options'] ) ) {
173
+ $this->options = array_map( 'esc_attr', $field['options'] );
174
+ }
175
+
176
+ // Escaping.
177
+ if ( ! empty( $field['escaping'] ) && ( is_string( $field['escaping'] ) || is_array( $field['escaping'] ) ) ) {
178
+ if ( isset( $field['default'] ) ) {
179
+ $this->default = $this->escape_callback( $field['escaping'], $field['default'] );
180
+ }
181
+ if ( isset( $field['value'] ) ) {
182
+ $this->value = $this->escape_callback( $field['escaping'], $field['value'] );
183
+ }
184
+ } else {
185
+ if ( isset( $field['default'] ) ) {
186
+ $this->default = $this->escape( $field['default'] );
187
+ }
188
+ if ( isset( $field['value'] ) ) {
189
+ $this->value = $this->escape( $field['value'] );
190
+ }
191
+ }
192
+
193
+ // Validation.
194
+ if ( ! empty( $field['validation'] ) ) {
195
+ $this->validation = $this->validate( $field['validation'], $this->value );
196
+ }
197
+
198
+ // CSS classes and styles.
199
+ $classes = isset( $field['class'] ) ? $field['class'] : '';
200
+ $this->set_class( $classes );
201
+ if ( isset( $field['style'] ) ) {
202
+ $this->set_style( $field['style'] );
203
+ }
204
+
205
+ // Custom attributes.
206
+ if ( isset( $field['attributes'] ) ) {
207
+ $this->set_attributes( $field['attributes'] );
208
+ }
209
+
210
+ // Tooltip markup.
211
+ if ( isset( $field['tooltip'] ) ) {
212
+ $this->tooltip = ' <i class="simcal-icon-help simcal-help-tip" data-tip="' . esc_attr( $field['tooltip'] ) . '"></i> ' ;
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Set custom HTML attributes.
218
+ *
219
+ * @since 3.0.0
220
+ *
221
+ * @param array $attributes
222
+ *
223
+ * @return void
224
+ */
225
+ public function set_attributes( $attributes ) {
226
+
227
+ $attr = '';
228
+
229
+ if ( ! empty( $attributes ) && is_array( $attributes ) ) {
230
+ foreach ( $attributes as $k => $v ) {
231
+ $attr .= esc_attr( $k ) . '="' . esc_attr( $v ) . '" ';
232
+ }
233
+ }
234
+
235
+ $this->attributes = $attr;
236
+ }
237
+
238
+ /**
239
+ * Set CSS styles.
240
+ *
241
+ * @since 3.0.0
242
+ *
243
+ * @param array $css
244
+ *
245
+ * @return void
246
+ */
247
+ public function set_style( $css ) {
248
+
249
+ $styles = '';
250
+
251
+ if ( ! empty( $css ) && is_array( $css ) ) {
252
+ foreach ( $css as $k => $v ) {
253
+ $styles .= esc_attr( $k ) . ': ' . esc_attr( $v ) . '; ';
254
+ }
255
+ }
256
+
257
+ $this->style = $styles;
258
+ }
259
+
260
+ /**
261
+ * Set classes.
262
+ *
263
+ * @since 3.0.0
264
+ *
265
+ * @param array $class
266
+ *
267
+ * @return void
268
+ */
269
+ public function set_class( $class ) {
270
+
271
+ $classes = '';
272
+ $type_class = '';
273
+ $error = '';
274
+
275
+ if ( ! empty( $class ) && is_array( $class ) ) {
276
+ $classes = implode( ' ', array_map( 'esc_attr', $class ) );
277
+ }
278
+ if ( ! empty( $this->type_class ) ) {
279
+ $type_class = esc_attr( $this->type_class );
280
+ }
281
+ if ( true !== $this->validation && ! empty( $this->validation ) ) {
282
+ $error = 'simcal-field-error ';
283
+ }
284
+
285
+ $this->class = trim( $error . 'simcal-field ' . $type_class . ' ' . $classes );
286
+ }
287
+
288
+ /**
289
+ * Escape field value.
290
+ *
291
+ * Default escape function, a wrapper for esc_attr().
292
+ *
293
+ * @since 3.0.0
294
+ * @access protected
295
+ *
296
+ * @param array|string|int $value
297
+ *
298
+ * @return array|string
299
+ */
300
+ protected function escape( $value ) {
301
+ return ! empty( $value ) ? ( is_array( $value ) ? array_map( 'esc_attr', $value ) : esc_attr( $value ) ) : '';
302
+ }
303
+
304
+ /**
305
+ * Escape callback.
306
+ *
307
+ * Custom escaping function set in field args.
308
+ *
309
+ * @since 3.0.0
310
+ * @access protected
311
+ *
312
+ * @param array|string $callback
313
+ * @param mixed $value
314
+ *
315
+ * @return mixed
316
+ */
317
+ protected function escape_callback( $callback, $value ) {
318
+ if ( $callback && ( is_string( $callback ) || is_array( $callback ) ) ) {
319
+ return call_user_func( $callback, $value );
320
+ }
321
+ return esc_attr( $value );
322
+ }
323
+
324
+ /**
325
+ * Validation callback.
326
+ *
327
+ * Custom field validation callback set in field args.
328
+ *
329
+ * @since 3.0.0
330
+ * @access protected
331
+ *
332
+ * @param array|string $callback
333
+ * @param string $value
334
+ *
335
+ * @return true|string Expected to return bool (true) if passes, message string if not.
336
+ */
337
+ protected function validate( $callback, $value ) {
338
+ if ( $callback && ( is_string( $callback ) || is_array( $callback ) ) ) {
339
+ $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : '';
340
+ return call_user_func( $callback, $value, $screen );
341
+ }
342
+ return true;
343
+ }
344
+
345
+ /**
346
+ * Outputs the field markup.
347
+ *
348
+ * @since 3.0.0
349
+ *
350
+ * @return void
351
+ */
352
+ abstract public function html();
353
+
354
+ }
includes/abstracts/meta-box.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Meta Box
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Abstracts;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * The Meta Box.
15
+ *
16
+ * Basic interface for post meta boxes markup and post meta handling.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ interface Meta_Box {
21
+
22
+ /**
23
+ * Output the meta box markup.
24
+ *
25
+ * @since 3.0.0
26
+ *
27
+ * @param \WP_Post $post
28
+ *
29
+ * @return void
30
+ */
31
+ public static function html( $post );
32
+
33
+ /**
34
+ * Validate and save the meta box fields.
35
+ *
36
+ * @since 3.0.0
37
+ *
38
+ * @param int $post_id
39
+ * @param \WP_Post $post
40
+ *
41
+ * @return void
42
+ */
43
+ public static function save( $post_id, $post );
44
+
45
+ }
includes/abstracts/widget.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Widget
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Abstracts;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * The Widget.
15
+ *
16
+ * Basic widget interface.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ interface Widget {
21
+
22
+ /**
23
+ * Constructor.
24
+ *
25
+ * @since 3.0.0
26
+ */
27
+ public function __construct();
28
+
29
+ /**
30
+ * Print the widget content.
31
+ *
32
+ * @since 3.0.0
33
+ *
34
+ * @param array $args Display arguments.
35
+ * @param array $instance The settings for the particular instance of the widget.
36
+ *
37
+ * @return void
38
+ */
39
+ public function widget( $args, $instance );
40
+
41
+ /**
42
+ * Update a particular instance of the widget.
43
+ *
44
+ * This function should check that $new_instance is set correctly.
45
+ * The newly-calculated value of `$instance` should be returned.
46
+ * If false is returned, the instance won't be saved/updated.
47
+ *
48
+ * @since 3.0.0
49
+ *
50
+ * @param array $new_instance New settings for this instance as input by the user via
51
+ * @param array $old_instance Old settings for this instance.
52
+ *
53
+ * @return array Settings to save or bool false to cancel saving.
54
+ */
55
+ public function update( $new_instance, $old_instance );
56
+
57
+ /**
58
+ * Print the settings update form.
59
+ *
60
+ * @since 3.0.0
61
+ *
62
+ * @param array $instance Current settings.
63
+ *
64
+ * @return string
65
+ */
66
+ public function form( $instance );
67
+
68
+ }
includes/admin/admin-functions.php DELETED
@@ -1,128 +0,0 @@
1
- <?php
2
- /**
3
- * Admin helper functions
4
- *
5
- * @package GCE
6
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
7
- * @license GPL-2.0+
8
- * @copyright 2014 Phil Derksen
9
- */
10
-
11
- /**
12
- * Function to clear the cache out
13
- *
14
- * @since 2.0.0
15
- */
16
- function gce_clear_cache( $id ) {
17
-
18
- delete_transient( 'gce_feed_' . $id );
19
-
20
- add_settings_error( 'gce-notices', 'gce-cache-updated', __( 'Cache has been cleared for this feed.', 'gce' ), 'updated' );
21
- }
22
-
23
- /**
24
- * Function that runs when a new Feed CPT is made
25
- * Adds the default editor content for the event builder
26
- * Adds default post meta options
27
- *
28
- * @since 2.0.0
29
- */
30
-
31
- function gce_default_editor_content( $content, $post ) {
32
-
33
- if( $post->post_type == 'gce_feed' ) {
34
- $content = '<div class="gce-list-event gce-tooltip-event">[event-title]</div>' . "\n";
35
- $content .= '<div><span>' . __( 'Starts:', 'gce' ) . '</span> [start-time]</div>' . "\n";
36
- $content .= '<div><span>' . __( 'Ends:', 'gce' ) . '</span> [end-date] - [end-time]</div>' . "\n";
37
- $content .= '[if-location]<div><span>' . __( 'Location:', 'gce' ) . '</span> [location]</div>[/if-location]' . "\n";
38
- $content .= '[if-description]<div><span>' . __( 'Description:', 'gce' ) . '</span> [description]</div>[/if-description]' . "\n";
39
- $content .= '<div>[link newwindow="true"]' . __( 'More details...', 'gce' ) . '[/link]</div>' . "\n";
40
-
41
- // Default Post Meta Options
42
- add_post_meta( $post->ID, 'gce_expand_recurring', 1 );
43
- add_post_meta( $post->ID, 'gce_retrieve_from', 'today' );
44
- add_post_meta( $post->ID, 'gce_retrieve_until', 'end_time' );
45
- add_post_meta( $post->ID, 'gce_cache', 43200 );
46
- add_post_meta( $post->ID, 'gce_paging', 1 );
47
- add_post_meta( $post->ID, 'gce_list_start_offset_num', '0' );
48
- add_post_meta( $post->ID, 'gce_feed_end_num', 2 );
49
- add_post_meta( $post->ID, 'gce_feed_start_num', 1 );
50
- add_post_meta( $post->ID, 'gce_feed_end', 'years' );
51
- add_post_meta( $post->ID, 'gce_feed_start', 'months' );
52
- add_post_meta( $post->ID, 'gce_show_tooltips', 1 );
53
-
54
- // Default Simple Display Options
55
- add_post_meta( $post->ID, 'gce_display_start', 'time' );
56
- add_post_meta( $post->ID, 'gce_display_start_text', __( 'Starts:', 'gce' ) );
57
- add_post_meta( $post->ID, 'gce_display_end', 'time-date' );
58
- add_post_meta( $post->ID, 'gce_display_end_text', __( 'Ends:', 'gce' ) );
59
- add_post_meta( $post->ID, 'gce_display_separator', ', ' );
60
- add_post_meta( $post->ID, 'gce_display_location_text', __( 'Location:', 'gce' ) );
61
- add_post_meta( $post->ID, 'gce_display_description_text', __( 'Description:', 'gce' ) );
62
- add_post_meta( $post->ID, 'gce_display_link', 1 );
63
- add_post_meta( $post->ID, 'gce_display_link_text', __( 'More Details', 'gce' ) );
64
-
65
- }
66
-
67
- return $content;
68
- }
69
- add_filter( 'default_content', 'gce_default_editor_content', 10, 2 );
70
-
71
-
72
- function gce_add_cache_button() {
73
- global $post;
74
-
75
- if( $post->post_type == 'gce_feed' ) {
76
- $html = '<div id="gce-clear-cache">' .
77
- '<a href="' . esc_url( add_query_arg( array( 'clear_cache' => true ) ) ) . '">' . __( 'Clear Cache', 'gce' ) . '</a>' .
78
- '</div>';
79
-
80
- echo $html;
81
- }
82
- }
83
- add_action( 'post_submitbox_start', 'gce_add_cache_button' );
84
-
85
- /**
86
- * Sanitize a variable of unknown type.
87
- *
88
- * Helper function to sanitize a variable from input,
89
- * which could also be a multidimensional array of variable depth.
90
- *
91
- * @param mixed $var Variable to sanitize.
92
- * @param string $func Function to use for sanitizing text strings (default 'sanitize_text_field')
93
- *
94
- * @return array|string Sanitized variable
95
- */
96
- function gce_sanitize_input( $var, $func = 'sanitize_text_field' ) {
97
-
98
- if ( empty( $var ) || is_null( $var ) ) {
99
- return '';
100
- }
101
-
102
- if ( is_bool( $var ) ) {
103
- if ( $var === true ) {
104
- return 'yes';
105
- } else {
106
- return 'no';
107
- }
108
- }
109
-
110
- if ( is_string( $var ) || is_numeric( $var ) ) {
111
- $func = is_string( $func ) && function_exists( $func ) ? $func : 'sanitize_text_field';
112
- return call_user_func( $func, trim( strval( $var ) ) );
113
- }
114
-
115
- if ( is_object( $var ) ) {
116
- $var = (array) $var;
117
- }
118
-
119
- if ( is_array( $var ) ) {
120
- $array = array();
121
- foreach( $var as $k => $v ) {
122
- $array[$k] = gce_sanitize_input( $v );
123
- }
124
- return $array;
125
- }
126
-
127
- return '';
128
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/admin/admin-notice.php DELETED
@@ -1,37 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Show notice for API Settings
5
- *
6
- * @package GCE
7
- * @subpackage admin/views
8
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
9
- */
10
-
11
- // Exit if accessed directly.
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit;
14
- }
15
-
16
- ?>
17
-
18
- <style>
19
- #gce-install-notice .button-secondary {
20
- margin-left: 15px;
21
- }
22
- </style>
23
-
24
- <div id="gce-install-notice" class="updated">
25
- <p>
26
- <h3><?php _e( 'GCal API Key Notice', 'gce' ); ?></h3>
27
-
28
- <?php _e( 'GCal Events uses the Google Calendar API version 3. By default this plugin uses a public shared key across all plugin users.', 'gce' ); ?>
29
- <br/><br/>
30
- <?php _e( 'This key is limited to 500,000 requests per day and 5 requests per second. To avoid running into any potential limits you can use your own Google API key.', 'gce' ); ?>
31
- </p>
32
- <p>
33
- <a href="<?php echo admin_url( 'edit.php?post_type=gce_feed&page=google-calendar-events_general_settings' ); ?>" class="button-primary"><?php _e( 'Enter your GCal API key', 'gce' ); ?></a>
34
- <a href="<?php echo admin_url( 'edit.php?post_type=gce_feed' ); ?>" class="button-secondary"><?php _e( 'Configure GCal feeds', 'gce' ); ?></a>
35
- <a href="<?php echo esc_url( add_query_arg( 'gce-dismiss-install-nag', 1 ) ); ?>" class="button-secondary"><?php _e( 'Hide this', 'gce' ); ?></a>
36
- </p>
37
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/admin/ajax.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Ajax
4
+ *
5
+ * @package SimpleCalendar\Admin
6
+ */
7
+ namespace SimpleCalendar\Admin;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Admin ajax.
15
+ *
16
+ * @since 3.0.0
17
+ */
18
+ class Ajax {
19
+
20
+ /**
21
+ * Set up ajax hooks.
22
+ *
23
+ * @since 3.0.0
24
+ */
25
+ public function __construct() {
26
+
27
+ // Set an option if the user rated the plugin.
28
+ add_action( 'wp_ajax_simcal_rated', array( $this, 'rate_plugin' ) );
29
+
30
+ // Set an option if the user rated the plugin.
31
+ add_action( 'wp_ajax_simcal_clear_cache', array( $this, 'clear_cache' ) );
32
+
33
+ // Convert a datetime format.
34
+ add_action( 'wp_ajax_simcal_date_i18n_input_preview', array( $this, 'date_i18n' ) );
35
+
36
+ // Manage an add-on license activation or deactivation.
37
+ add_action( 'wp_ajax_simcal_manage_add_on_license', array( $this, 'manage_add_on_license' ) );
38
+
39
+ // Reset add-ons licenses.
40
+ add_action( 'wp_ajax_simcal_reset_add_ons_licenses', array( $this, 'reset_licenses' ) );
41
+
42
+ }
43
+
44
+ /**
45
+ * Clear transients.
46
+ *
47
+ * @since 3.0.0
48
+ */
49
+ public function clear_cache() {
50
+
51
+ $id = isset( $_POST['id'] ) ? ( is_array( $_POST['id'] ) ? array_map( 'intval', $_POST['id'] ) : intval( $_POST['id'] ) ) : '';
52
+
53
+ if ( ! empty( $id ) ) {
54
+ simcal_delete_feed_transients( $id );
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Ajax callback when a user clicks on the rate plugin link.
60
+ *
61
+ * @since 3.0.0
62
+ */
63
+ public function rate_plugin() {
64
+ update_option( 'simple-calendar_admin_footer_text_rated', date( 'Y-m-d', time() ) );
65
+ }
66
+
67
+ /**
68
+ * Ajax callback to return a formatted datetime string.
69
+ *
70
+ * @since 3.0.0
71
+ */
72
+ public function date_i18n() {
73
+
74
+ $value = isset( $_POST['value'] ) ? sanitize_text_field( $_POST['value'] ) : ' ';
75
+ $timestamp = isset( $_POST['timestamp'] ) ? absint( $_POST['timestamp'] ) : time();
76
+
77
+ wp_send_json_success( date_i18n( $value, $timestamp ) );
78
+ }
79
+
80
+ /**
81
+ * Activate add-on license.
82
+ *
83
+ * This code is run only when an add-on requiring a license is installed and active.
84
+ *
85
+ * @since 3.0.0
86
+ */
87
+ public function manage_add_on_license() {
88
+
89
+ $addon = isset( $_POST['add_on'] ) ? sanitize_key( $_POST['add_on'] ) : false;
90
+ $action = isset( $_POST['license_action'] ) ? esc_attr( $_POST['license_action'] ) : false;
91
+ $key = isset( $_POST['license_key'] ) ? esc_attr( $_POST['license_key'] ) : '';
92
+ $nonce = isset( $_POST['nonce'] ) ? esc_attr( $_POST['nonce'] ) : '';
93
+
94
+ // Verify that there are valid variables to process.
95
+ if ( false === $addon || ! in_array( $action, array( 'activate_license', 'deactivate_license' ) ) ) {
96
+ wp_send_json_error( __( 'Add-on unspecified or invalid action.', 'google-calendar-events' ) );
97
+ }
98
+
99
+ // Verify this request comes from the add-ons licenses activation settings page.
100
+ if ( ! wp_verify_nonce( $nonce, 'simcal_license_manager' ) ) {
101
+ wp_send_json_error( sprintf( __( 'An error occurred: %s', 'google-calendar-events' ), 'Nonce verification failed.' ) );
102
+ }
103
+
104
+ // Removes the prefix and converts simcal_{id_no} to {id_no}.
105
+ $id = intval( substr( $addon, 7 ) );
106
+
107
+ // Data to send in API request.
108
+ $api_request = array(
109
+ 'edd_action' => $action,
110
+ 'license' => $key,
111
+ 'item_id' => urlencode( $id ),
112
+ 'url' => home_url()
113
+ );
114
+
115
+ // Call the custom API.
116
+ $response = wp_remote_post(
117
+ defined( 'SIMPLE_CALENDAR_STORE_URL' ) ? SIMPLE_CALENDAR_STORE_URL : simcal_get_url( 'home' ),
118
+ array(
119
+ 'timeout' => 15,
120
+ 'sslverify' => false,
121
+ 'body' => $api_request
122
+ )
123
+ );
124
+
125
+ // Update license in db.
126
+ $keys = get_option( 'simple-calendar_settings_licenses', array() );
127
+ $new_keys = array_merge( (array) $keys, array( 'keys' => array( $addon => $key ) ) );
128
+ update_option( 'simple-calendar_settings_licenses', $new_keys );
129
+
130
+ // Make sure there is a response.
131
+ if ( is_wp_error( $response ) ) {
132
+ wp_send_json_error( sprintf( __( 'There was an error processing your request: %s', 'google-calendar-events' ), $response->get_error_message() ) );
133
+ }
134
+
135
+ // Decode the license data and save.
136
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
137
+ $status = simcal_get_license_status();
138
+ if ( 'deactivated' == $license_data->license ) {
139
+ unset( $status[ $addon ] );
140
+ update_option( 'simple-calendar_licenses_status', $status );
141
+ wp_send_json_success( $license_data->license );
142
+ } elseif ( in_array( $license_data->license, array( 'valid', 'invalid' ) ) ) {
143
+ $status[ $addon ] = $license_data->license;
144
+ update_option( 'simple-calendar_licenses_status', $status );
145
+ $message = 'valid' == $license_data->license ? 'valid' : __( 'License key is invalid.', 'google-calendar-events' );
146
+ wp_send_json_success( $message );
147
+ } else {
148
+ wp_send_json_error( '' );
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Reset licenses.
154
+ *
155
+ * @since 3.0.0
156
+ */
157
+ public function reset_licenses() {
158
+
159
+ $nonce = isset( $_POST['nonce'] ) ? esc_attr( $_POST['nonce'] ) : '';
160
+
161
+ // Verify this request comes from the add-ons licenses activation settings page.
162
+ if ( empty ( $nonce ) || ! wp_verify_nonce( $nonce, 'simcal_license_manager' ) ) {
163
+ wp_send_json_error( sprintf( __( 'An error occurred: %s', 'google-calendar-events' ), 'Nonce verification failed.' ) );
164
+ }
165
+
166
+ delete_option( 'simple-calendar_settings_licenses' );
167
+ delete_option( 'simple-calendar_licenses_status' );
168
+
169
+ wp_send_json_success( 'success' );
170
+ }
171
+
172
+ }
includes/admin/assets.php ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Assets
4
+ *
5
+ * @package SimpleCalendar\Admin
6
+ */
7
+ namespace SimpleCalendar\Admin;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Admin scripts and styles.
15
+ *
16
+ * Handles the plugin scripts and styles for back end dashboard pages.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Assets {
21
+
22
+ /**
23
+ * Load minified assets.
24
+ *
25
+ * @access public
26
+ * @var string
27
+ */
28
+ public $min = '.min';
29
+
30
+ /**
31
+ * Hook in tabs.
32
+ *
33
+ * @since 3.0.0
34
+ */
35
+ public function __construct() {
36
+
37
+ $this->min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG == true ) ? '' : '.min';
38
+
39
+ add_action( 'admin_enqueue_scripts', array( $this, 'load' ) );
40
+ }
41
+
42
+ /**
43
+ * Enqueue scripts and styles.
44
+ *
45
+ * @since 3.0.0
46
+ */
47
+ public function load() {
48
+
49
+ $css_path = SIMPLE_CALENDAR_ASSETS . 'css/';
50
+ $css_path_vendor = $css_path . 'vendor/';
51
+ $js_path = SIMPLE_CALENDAR_ASSETS . 'js/';
52
+ $js_path_vendor = $js_path . 'vendor/';
53
+
54
+ /* ====================== *
55
+ * Register Admin Scripts *
56
+ * ====================== */
57
+
58
+ wp_register_script(
59
+ 'simcal-tiptip',
60
+ $js_path_vendor . 'tiptip' . $this->min . '.js',
61
+ array( 'jquery' ),
62
+ '1.3',
63
+ true
64
+ );
65
+ wp_register_script(
66
+ 'simcal-select2',
67
+ $js_path_vendor . 'select2' . $this->min . '.js',
68
+ array(),
69
+ '4.0',
70
+ true
71
+ );
72
+ wp_register_script(
73
+ 'simcal-admin',
74
+ $js_path . 'admin' . $this->min . '.js',
75
+ array(
76
+ 'jquery',
77
+ 'jquery-ui-sortable',
78
+ 'jquery-ui-datepicker',
79
+ 'wp-color-picker',
80
+ 'simcal-tiptip',
81
+ 'simcal-select2',
82
+ ),
83
+ SIMPLE_CALENDAR_VERSION,
84
+ true
85
+ );
86
+ wp_register_script(
87
+ 'simcal-admin-add-calendar',
88
+ $js_path . 'admin-add-calendar' . $this->min . '.js',
89
+ array( 'simcal-select2' ),
90
+ SIMPLE_CALENDAR_VERSION,
91
+ true
92
+ );
93
+
94
+ /* ===================== *
95
+ * Register Admin Styles *
96
+ * ===================== */
97
+
98
+ wp_register_style(
99
+ 'simcal-select2',
100
+ $css_path_vendor . 'select2' . $this->min . '.css',
101
+ array(),
102
+ '4.0.0'
103
+ );
104
+ wp_register_style(
105
+ 'simcal-admin',
106
+ $css_path . 'admin' . $this->min . '.css',
107
+ array(
108
+ 'wp-color-picker',
109
+ 'simcal-select2',
110
+ ),
111
+ SIMPLE_CALENDAR_VERSION
112
+ );
113
+ wp_register_style(
114
+ 'simcal-admin-add-calendar',
115
+ $css_path . 'admin-add-calendar' . $this->min . '.css',
116
+ array( 'simcal-select2' ),
117
+ SIMPLE_CALENDAR_VERSION
118
+ );
119
+
120
+ if ( simcal_is_admin_screen() !== false ) {
121
+
122
+ wp_enqueue_script( 'simcal-admin' );
123
+ wp_localize_script(
124
+ 'simcal-admin',
125
+ 'simcal_admin',
126
+ simcal_common_scripts_variables()
127
+ );
128
+
129
+ wp_enqueue_style( 'simcal-admin' );
130
+
131
+ } else {
132
+
133
+ global $post_type;
134
+ $screen = get_current_screen();
135
+
136
+ $post_types = array();
137
+ $settings = get_option( 'simple-calendar_settings_calendars' );
138
+ if ( isset( $settings['general']['attach_calendars_posts'] ) ) {
139
+ $post_types = $settings['general']['attach_calendars_posts'];
140
+ }
141
+
142
+ $conditions = array(
143
+ in_array( $post_type, (array) $post_types ),
144
+ $screen->id == 'widgets',
145
+ );
146
+
147
+ if ( in_array( true, $conditions ) ) {
148
+
149
+ wp_enqueue_script( 'simcal-admin-add-calendar' );
150
+ wp_localize_script( 'simcal-admin-add-calendar', 'simcal_admin', array(
151
+ 'locale' => get_locale(),
152
+ 'text_dir' => is_rtl() ? 'rtl' : 'ltr',
153
+ ) );
154
+
155
+ wp_enqueue_style( 'simcal-admin-add-calendar' );
156
+ }
157
+
158
+ }
159
+
160
+ }
161
+
162
+ }
includes/admin/bulk-actions.php ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Bulk Actions
4
+ *
5
+ * Add bulk actions to post types edit screens.
6
+ * Based on https://github.com/Seravo/wp-custom-bulk-actions
7
+ *
8
+ * @package SimpleCalendar/Admin
9
+ */
10
+ namespace SimpleCalendar\Admin;
11
+
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * Admin bulk actions.
18
+ */
19
+ class Bulk_Actions {
20
+
21
+ /**
22
+ * Target post type.
23
+ *
24
+ * @access public
25
+ * @var string
26
+ */
27
+ public $bulk_action_post_type = '';
28
+
29
+ /**
30
+ * Bulk actions.
31
+ *
32
+ * @access private
33
+ * @var array
34
+ */
35
+ private $actions = array();
36
+
37
+ /**
38
+ * Constructor.
39
+ *
40
+ * @since 3.0.0
41
+ *
42
+ * @param string $post_type
43
+ */
44
+ public function __construct( $post_type ) {
45
+ $this->bulk_action_post_type = post_type_exists( $post_type ) ? $post_type : '';
46
+ }
47
+
48
+ /**
49
+ * Define all your custom bulk actions and corresponding callbacks
50
+ * Define at least $menu_text and $callback parameters
51
+ *
52
+ * @since 3.0.0
53
+ *
54
+ * @param array $args
55
+ */
56
+ public function register_bulk_action( $args ) {
57
+
58
+ $func = array();
59
+ $func['action_name'] = isset( $args['action_name'] ) ? sanitize_key( $args['action_name'] ) : '';
60
+ $func['callback'] = isset( $args['callback'] ) ? $args['callback'] : '';
61
+ $func['menu_text'] = isset( $args['menu_text'] ) ? esc_attr( $args['menu_text'] ) : '';
62
+ $func['admin_notice'] = isset( $args['admin_notice'] ) ? esc_attr( $args['admin_notice'] ) : '';
63
+
64
+ if ( $func['action_name'] && $func['callback'] ) {
65
+ $this->actions[ $func['action_name'] ] = $func;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Init.
71
+ *
72
+ * Callbacks need to be registered before add_actions.
73
+ *
74
+ * @since 3.0.0
75
+ */
76
+ public function init() {
77
+ if ( is_admin() ) {
78
+ add_action( 'admin_footer-edit.php', array( $this, 'custom_bulk_admin_footer' ) );
79
+ add_action( 'load-edit.php', array( $this, 'custom_bulk_action' ) );
80
+ add_action( 'admin_notices', array( $this, 'custom_bulk_admin_notices' ) );
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Step 1: add the custom Bulk Action to the select menus.
86
+ *
87
+ * @since 3.0.0
88
+ */
89
+ public function custom_bulk_admin_footer() {
90
+
91
+ global $post_type;
92
+
93
+ // Only permit actions with defined post type.
94
+ if ( $post_type == $this->bulk_action_post_type ) {
95
+
96
+ ?>
97
+ <script type="text/javascript">
98
+ jQuery( document ).ready( function() {
99
+ <?php foreach ( $this->actions as $action_name => $action ) : ?>
100
+ jQuery( '<option>' ).val( '<?php echo $action_name ?>' ).text( '<?php echo $action['menu_text'] ?>').appendTo( 'select[name="action"]' );
101
+ jQuery( '<option>' ).val( '<?php echo $action_name ?>' ).text( '<?php echo $action['menu_text'] ?>').appendTo( 'select[name="action2"]' );
102
+ <?php endforeach; ?>
103
+ } );
104
+ </script>
105
+ <?php
106
+
107
+ }
108
+
109
+ }
110
+
111
+ /**
112
+ * Step 2: handle the custom Bulk Action.
113
+ *
114
+ * Based on the post http://wordpress.stackexchange.com/questions/29822/custom-bulk-action
115
+ *
116
+ * @since 3.0.0
117
+ */
118
+ public function custom_bulk_action() {
119
+
120
+ global $typenow;
121
+ $post_type = $typenow;
122
+
123
+ if ( $post_type == $this->bulk_action_post_type ) {
124
+
125
+ // Get the action.
126
+ // Depending on your resource type this could be WP_Users_List_Table, WP_Comments_List_Table, etc.
127
+ $wp_list_table = _get_list_table( 'WP_Posts_List_Table' );
128
+ $action = $wp_list_table->current_action();
129
+
130
+ // Allow only defined actions.
131
+ $allowed_actions = array_keys( $this->actions );
132
+ if ( ! in_array( $action, $allowed_actions ) ) {
133
+ return;
134
+ }
135
+
136
+ // Security check.
137
+ check_admin_referer( 'bulk-posts' );
138
+
139
+ // Make sure ids are submitted.
140
+ // Depending on the resource type, this may be 'media' or 'ids'.
141
+ if ( isset( $_REQUEST['post'] ) ) {
142
+ $post_ids = array_map( 'intval', $_REQUEST['post'] );
143
+ }
144
+
145
+ if ( empty( $post_ids ) ) {
146
+ return;
147
+ }
148
+
149
+ // This is based on wp-admin/edit.php.
150
+ $sendback = remove_query_arg(
151
+ array( 'exported', 'untrashed', 'deleted', 'ids' ),
152
+ wp_get_referer()
153
+ );
154
+ if ( ! $sendback ) {
155
+ $sendback = admin_url( "edit.php?post_type=$post_type" );
156
+ }
157
+
158
+ $pagenum = $wp_list_table->get_pagenum();
159
+ $sendback = add_query_arg( 'paged', $pagenum, $sendback );
160
+
161
+ // Check that we have anonymous function as a callback.
162
+ $anon_fns = array_filter( $this->actions[ $action ], function( $el ) {
163
+ return $el instanceof \Closure;
164
+ } );
165
+
166
+ if ( count( $anon_fns ) > 0 ) {
167
+ $this->actions[ $action ]['callback']( $post_ids );
168
+ } else {
169
+ call_user_func( $this->actions[ $action ]['callback'], $post_ids );
170
+ }
171
+
172
+ $sendback = add_query_arg(
173
+ array( 'success_action' => $action, 'ids' => join( ',', $post_ids ) ),
174
+ $sendback
175
+ );
176
+ $sendback = remove_query_arg(
177
+ array( 'action', 'paged', 'mode', 'action2', 'tags_input', 'post_author', 'comment_status', 'ping_status', '_status', 'post', 'bulk_edit', 'post_view' ),
178
+ $sendback
179
+ );
180
+
181
+ wp_redirect( $sendback );
182
+
183
+ exit;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Step 3: display an admin notice after action.
189
+ *
190
+ * @since 3.0.0
191
+ */
192
+ public function custom_bulk_admin_notices() {
193
+
194
+ global $post_type, $pagenow;
195
+
196
+ if ( isset( $_REQUEST['ids'] ) ) {
197
+ $post_ids = explode( ',', $_REQUEST['ids'] );
198
+ }
199
+
200
+ // Make sure ids are submitted.
201
+ // Depending on the resource type, this may be 'media' or 'ids'.
202
+ if ( empty( $post_ids ) ) {
203
+ return;
204
+ }
205
+
206
+ $post_ids_count = is_array( $post_ids ) ? count( $post_ids ) : 1;
207
+
208
+ if ( $pagenow == 'edit.php' && $post_type == $this->bulk_action_post_type ) {
209
+
210
+ if ( isset( $_REQUEST['success_action'] ) ) {
211
+
212
+ // Print notice in admin bar.
213
+ $message = $this->actions[ $_REQUEST['success_action'] ]['admin_notice'];
214
+
215
+ if ( is_array( $message ) ) {
216
+ $message = sprintf( _n( $message['single'], $message['plural'], $post_ids_count, 'google-calendar-events' ), $post_ids_count );
217
+ }
218
+
219
+ $class = 'updated notice is-dismissible above-h2';
220
+ if ( ! empty( $message ) ) {
221
+ echo "<div class=\"{$class}\"><p>{$message}</p></div>";
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ }
includes/admin/fields/checkbox.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Checkbox Field
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Fields;
8
+
9
+ use SimpleCalendar\Abstracts\Field;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit; // Exit if accessed directly.
13
+ }
14
+
15
+ /**
16
+ * Checkbox input field.
17
+ *
18
+ * Outputs one single checkbox or a fieldset of checkboxes for multiple choices.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Checkbox extends Field {
23
+
24
+ /**
25
+ * Construct.
26
+ *
27
+ * @since 3.0.0
28
+ *
29
+ * @param array $field
30
+ */
31
+ public function __construct( $field ) {
32
+ $this->type_class = 'simcal-field-checkboxes';
33
+ parent::__construct( $field );
34
+ }
35
+
36
+ /**
37
+ * Outputs the field markup.
38
+ *
39
+ * @since 3.0.0
40
+ */
41
+ public function html() {
42
+
43
+ if ( ! empty( $this->options ) && count( (array) $this->options ) > 1 ) {
44
+
45
+ if ( ! empty( $this->description ) ) {
46
+ echo '<p class="description">' . wp_kses_post( $this->description ) . ' ' . $this->tooltip . '</p>';
47
+ }
48
+
49
+ ?>
50
+ <fieldset class="<?php echo $this->class; ?>" <?php echo ! empty( $this->style ) ? 'style="' . $this->style . '"' : ''; ?>>
51
+ <?php
52
+
53
+ if ( ! empty( $this->title ) ) {
54
+ echo '<legend class="screen-reader-text"><span>' . $this->title . '</span></legend>';
55
+ }
56
+
57
+ ?>
58
+ <ul>
59
+ <?php foreach ( $this->options as $option => $name ) : ?>
60
+ <li>
61
+ <label for="<?php echo $this->id . '-' . trim( strval( $option ) ); ?>">
62
+ <input name="<?php echo $this->name; ?>"
63
+ id="<?php echo $this->id . '-' . trim( strval( $option ) ); ?>"
64
+ class="simcal-field simcal-field-checkbox"
65
+ type="checkbox"
66
+ value="<?php echo trim( strval( $option ) ); ?>"
67
+ <?php checked( $this->value, $option, true ); ?>
68
+ <?php echo $this->attributes; ?>
69
+ /><?php echo esc_attr( $name ); ?>
70
+ </label>
71
+ </li>
72
+ <?php endforeach; ?>
73
+ </ul>
74
+ </fieldset>
75
+ <?php
76
+
77
+ } else {
78
+
79
+ ?>
80
+ <span class="simcal-field-bool" <?php echo $this->style ? 'style="' . $this->style . '"' : ''; ?>>
81
+ <?php if ( ! empty( $this->title ) ) : ?>
82
+ <span class="screen-reader-text"><?php echo $this->title; ?></span>
83
+ <?php endif; ?>
84
+ <input name="<?php echo $this->name; ?>"
85
+ type="checkbox"
86
+ id="<?php echo $this->id; ?>"
87
+ class="simcal-field simcal-field-checkbox <?php echo $this->class; ?>"
88
+ value="yes"
89
+ <?php checked( $this->value, 'yes', true ); ?>
90
+ <?php echo $this->attributes; ?>/><?php _e( 'Yes', 'google-calendar-events' ); ?>
91
+ </span>
92
+ <?php
93
+
94
+ echo $this->tooltip;
95
+
96
+ if ( ! empty( $this->description ) ) {
97
+ echo '<p class="description">' . wp_kses_post( $this->description ) . '</p>';
98
+ }
99
+
100
+ }
101
+
102
+ }
103
+
104
+ }
includes/admin/fields/date-picker.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Date Picker Field
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Fields;
8
+
9
+ use SimpleCalendar\Abstracts\Field;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Date Picker input field.
17
+ *
18
+ * A special field to choose dates or date ranges.
19
+ * Holds a date value in 'yy-mm-dd' format.
20
+ *
21
+ * @since 3.0.0
22
+ */
23
+ class Date_Picker extends Field {
24
+
25
+ /**
26
+ * Select a date range.
27
+ *
28
+ * @access public
29
+ * @var bool
30
+ */
31
+ public $range = false;
32
+
33
+ /**
34
+ * Use an inline picker.
35
+ *
36
+ * @access public
37
+ * @var bool
38
+ */
39
+ public $inline = true;
40
+
41
+ /**
42
+ * Construct.
43
+ *
44
+ * @since 3.0.0
45
+ *
46
+ * @param array $field
47
+ */
48
+ public function __construct( $field ) {
49
+
50
+ $this->range = isset( $field['range'] ) ? ( $field['range'] === true ? true : false ) : false;
51
+ $this->inline = isset( $field['inline'] ) ? ( $field['inline'] === true ? true : false ) : true;
52
+
53
+ $subtype = $this->range === true ? 'simcal-field-date-picker-range ' : '';
54
+ $this->type_class = 'simcal-field-date-picker ' . $subtype;
55
+
56
+ $data = array(
57
+ 'data-inline' => $this->inline === true ? 'true' : 'false',
58
+ );
59
+ $field['attributes'] = isset( $field['attributes'] ) ? array_merge( $field['attributes'], $data ) : $data;
60
+
61
+ parent::__construct( $field );
62
+ }
63
+
64
+ /**
65
+ * Output the field markup
66
+ *
67
+ * @since 3.0.0
68
+ */
69
+ public function html() {
70
+
71
+ if ( ! empty( $this->description ) ) {
72
+ echo '<p class="description">' . wp_kses_post( $this->description ) . '</p>';
73
+ }
74
+
75
+ ?>
76
+ <div id="<?php echo $this->id; ?>"
77
+ class="<?php echo $this->class; ?>"
78
+ <?php echo $this->style ? 'style="' . $this->style . '"' : ''; ?>
79
+ <?php echo $this->attributes ?>>
80
+ <?php
81
+
82
+ if ( false === $this->range ) {
83
+
84
+ ?>
85
+ <i class="simcal-icon-calendar"></i>
86
+ <input type="<?php echo $this->inline === true ? 'text' : 'hidden'; ?>"
87
+ name="<?php echo $this->name; ?>"
88
+ value="<?php echo $this->value; ?>"
89
+ placeholder="..."
90
+ readonly="readonly" />
91
+ <?php
92
+
93
+ if ( true === $this->inline ) {
94
+ echo $this->tooltip;
95
+ }
96
+
97
+ } else {
98
+ // @todo eventually if a date range picker is needed, this can be extended
99
+ }
100
+
101
+ ?>
102
+ </div>
103
+ <?php
104
+
105
+ }
106
+
107
+ }
includes/admin/fields/datetime-format.php ADDED
@@ -0,0 +1,379 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Datetime Formatter Field
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Fields;
8
+
9
+ use SimpleCalendar\Abstracts\Field;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Datetime formatter field.
17
+ *
18
+ * A special field to choose a format for date and time.
19
+ */
20
+ class Datetime_Format extends Field {
21
+
22
+ /**
23
+ * Datetime controls.
24
+ *
25
+ * @access public
26
+ * @var string 'date' or 'time'
27
+ */
28
+ public $subtype = '';
29
+
30
+ /**
31
+ * Timestamp.
32
+ *
33
+ * @access private
34
+ * @var int
35
+ */
36
+ private $timestamp = 0;
37
+
38
+ /**
39
+ * Constructor.
40
+ *
41
+ * @since 3.0.0
42
+ *
43
+ * @param array $field
44
+ */
45
+ public function __construct( $field ) {
46
+
47
+ $this->subtype = isset( $field['subtype'] ) ? $field['subtype'] : '';
48
+ $this->type_class = 'simcal-field-datetime-format simcal-field-' . $this->subtype . '-format-field';
49
+ $this->timestamp = mktime( 13, 10, 00, 1, 1, intval( date( 'Y', time() ) ) + 1 );
50
+
51
+ parent::__construct( $field );
52
+
53
+ if ( empty( $this->value ) ) {
54
+ if ( 'date' == $this->subtype ) {
55
+ $this->value = 'l, d F Y';
56
+ }
57
+ if ( 'time' == $this->subtype ) {
58
+ $this->value = 'G:i a';
59
+ }
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Output field markup.
65
+ *
66
+ * @since 3.0.0
67
+ */
68
+ public function html() {
69
+
70
+ $id = $this->id ? ' id="' . $this->id . '" ' : '';
71
+ $class = $this->class ? ' class="' . $this->class . '" ' : '';
72
+ $style = $this->style ? ' style="' . $this->style . '" ' : '';
73
+ $attr = $this->attributes;
74
+
75
+ ?>
76
+ <div <?php echo $id . $class . $style . $attr; ?>>
77
+ <?php
78
+
79
+ if ( ! empty( $this->description ) ) {
80
+ echo '<p class="description">' . $this->description . '</p>';
81
+ }
82
+
83
+ $matches = array_unique( str_split( $this->value ) );
84
+
85
+ if ( 'date' == $this->subtype ) {
86
+ $this->print_date( $matches );
87
+ }
88
+
89
+ if ( 'time' == $this->subtype ) {
90
+ $this->print_time( $matches );
91
+ }
92
+
93
+ ?>
94
+ <input type="hidden"
95
+ name="<?php echo $this->name; ?>"
96
+ value="<?php echo trim( $this->value ); ?>" />
97
+ <span>
98
+ <em><?php _e( 'Preview', 'google-calendar-events' ); ?>:</em>&nbsp;&nbsp;
99
+ <code><?php echo date_i18n( $this->value, $this->timestamp ); ?></code>
100
+ </span>
101
+ </div>
102
+ <?php
103
+
104
+ }
105
+
106
+ /**
107
+ * Print date input.
108
+ *
109
+ * @since 3.0.0
110
+ * @access private
111
+ *
112
+ * @param array $matches
113
+ */
114
+ private function print_date( $matches ) {
115
+
116
+ $date = array( 'weekday' => '', 'divider' => '', 'day' => '', 'month' => '', 'year' => '' );
117
+
118
+ foreach ( $matches as $match ) {
119
+ if ( in_array( $match, array( 'D', 'l' ) ) ) {
120
+ $this->weekday();
121
+ unset( $date['weekday'] );
122
+ } elseif ( in_array( $match, array( 'd', 'j' ) ) ) {
123
+ $this->day();
124
+ unset( $date['day'] );
125
+ } elseif ( in_array( $match, array( 'F', 'M', 'm', 'n' ) ) ) {
126
+ $this->month();
127
+ unset( $date['month'] );
128
+ } elseif ( in_array( $match, array( 'y', 'Y' ) ) ) {
129
+ $this->year();
130
+ unset( $date['year'] );
131
+ } elseif ( in_array( $match, array( '.', ',', ':', '/', '-' ) ) ) {
132
+ $this->divider();
133
+ unset( $date['divider'] );
134
+ }
135
+ }
136
+
137
+ $this->print_fields( $date );
138
+ }
139
+
140
+ /**
141
+ * Print time input.
142
+ *
143
+ * @since 3.0.0
144
+ * @access private
145
+ *
146
+ * @param array $matches
147
+ */
148
+ private function print_time( $matches ) {
149
+
150
+ $time = array( 'hours' => '', 'divider' => '', 'minutes' => '', 'meridiem' => '' );
151
+
152
+ foreach ( $matches as $match ) {
153
+ if ( in_array( $match, array( 'h', 'H', 'g', 'G' ) ) ) {
154
+ $this->hours();
155
+ unset( $time['hours'] );
156
+ } elseif ( in_array( $match, array( 'i' ) ) ) {
157
+ $this->minutes();
158
+ unset( $time['minutes'] );
159
+ } elseif ( in_array( $match, array( 'A', 'a' ) ) ) {
160
+ $this->meridiem();
161
+ unset( $time['meridiem'] );
162
+ } elseif ( in_array( $match, array( '.', ',', ':', '/', '-' ) ) ) {
163
+ $this->divider();
164
+ unset( $time['divider'] );
165
+ }
166
+ }
167
+
168
+ $this->print_fields( $time );
169
+ }
170
+
171
+ /**
172
+ * Print input fields.
173
+ *
174
+ * @since 3.0.0
175
+ * @access private
176
+ *
177
+ * @param $fields
178
+ */
179
+ private function print_fields( $fields ) {
180
+ if ( ! empty( $fields ) && is_array( $fields ) ) {
181
+ foreach ( $fields as $func => $v ) {
182
+ if ( method_exists( $this, $func ) ) {
183
+ $this->$func();
184
+ };
185
+ }
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Print weekday input.
191
+ *
192
+ * @since 3.0.0
193
+ * @access private
194
+ */
195
+ private function weekday() {
196
+
197
+ ?>
198
+ <div>
199
+ <label for="<?php echo $this->id; ?>-weekday">
200
+ <?php _e( 'Weekday', 'google-calendar-events' ); ?>
201
+ <select name="" id="<?php echo $this->id; ?>-weekday">
202
+ <option value="" data-preview=""></option>
203
+ <option value="D" <?php selected( 'D', strpbrk( 'D', $this->value ) ) ?> data-preview="<?php echo date_i18n( 'D', $this->timestamp ); ?>"><?php echo date_i18n( 'D', $this->timestamp ); ?></option>
204
+ <option value="l" <?php selected( 'l', strpbrk( 'l', $this->value ) ) ?> data-preview="<?php echo date_i18n( 'l', $this->timestamp ); ?>"><?php echo date_i18n( 'l', $this->timestamp ); ?></option>
205
+ </select>
206
+ </label>
207
+ </div>
208
+ <?php
209
+
210
+ }
211
+
212
+ /**
213
+ * Print day input.
214
+ *
215
+ * @since 3.0.0
216
+ * @access private
217
+ */
218
+ private function day() {
219
+
220
+ ?>
221
+ <div>
222
+ <label for="<?php echo $this->id; ?>-day">
223
+ <?php _e( 'Day', 'google-calendar-events' ); ?>
224
+ <select name="" id="<?php echo $this->id; ?>-day">
225
+ <option value="" data-preview=""></option>
226
+ <option value="j" <?php selected( 'j', strpbrk( 'j', $this->value ) ) ?> data-preview="<?php echo date( 'j', $this->timestamp ); ?>"><?php echo date( 'j', $this->timestamp ); ?></option>
227
+ <option value="d" <?php selected( 'd', strpbrk( 'd', $this->value ) ) ?> data-preview="<?php echo date( 'd', $this->timestamp ); ?>"><?php echo date( 'd', $this->timestamp ); ?></option>
228
+ </select>
229
+ </label>
230
+ </div>
231
+ <?php
232
+
233
+ }
234
+
235
+ /**
236
+ * Print month input.
237
+ *
238
+ * @since 3.0.0
239
+ * @access private
240
+ */
241
+ private function month() {
242
+
243
+ ?>
244
+ <div>
245
+ <label for="<?php echo $this->id; ?>-month">
246
+ <?php _e( 'Month', 'google-calendar-events' ); ?>
247
+ <select name="" id="<?php echo $this->id; ?>-month">
248
+ <option value="" data-preview=""></option>
249
+ <option value="F" <?php selected( 'F', strpbrk( 'F', $this->value ) ) ?> data-preview="<?php echo date_i18n( 'F', $this->timestamp ); ?>"><?php echo date_i18n( 'F', $this->timestamp ); ?></option>
250
+ <option value="M" <?php selected( 'M', strpbrk( 'M', $this->value ) ) ?> data-preview="<?php echo date_i18n( 'M', $this->timestamp ); ?>"><?php echo date_i18n( 'M', $this->timestamp ); ?></option>
251
+ <option value="m" <?php selected( 'm', strpbrk( 'm', $this->value ) ) ?> data-preview="<?php echo date( 'm', $this->timestamp ); ?>"><?php echo date( 'm', $this->timestamp ); ?></option>
252
+ <option value="n" <?php selected( 'n', strpbrk( 'n', $this->value ) ) ?> data-preview="<?php echo date( 'n', $this->timestamp ); ?>"><?php echo date( 'n', $this->timestamp ); ?></option>
253
+ </select>
254
+ </label>
255
+ </div>
256
+ <?php
257
+
258
+ }
259
+
260
+ /**
261
+ * Print year input.
262
+ *
263
+ * @since 3.0.0
264
+ * @access private
265
+ */
266
+ private function year() {
267
+
268
+ ?>
269
+ <div>
270
+ <label for="<?php echo $this->id; ?>-year">
271
+ <?php _e( 'Year', 'google-calendar-events' ); ?>
272
+ <select name="" id="<?php echo $this->id; ?>-year">
273
+ <option value="" data-preview=""></option>
274
+ <option value="Y" <?php selected( 'Y', strpbrk( 'Y', $this->value ) ) ?> data-preview="<?php echo date( 'Y', $this->timestamp ); ?>"><?php echo date( 'Y', $this->timestamp ); ?></option>
275
+ <option value="y" <?php selected( 'y', strpbrk( 'y', $this->value ) ) ?> data-preview="<?php echo date( 'y', $this->timestamp ); ?>"><?php echo date( 'y', $this->timestamp ); ?></option>
276
+ </select>
277
+ </label>
278
+ </div>
279
+ <?php
280
+
281
+ }
282
+
283
+ /**
284
+ * Print hours input.
285
+ *
286
+ * @since 3.0.0
287
+ * @access private
288
+ */
289
+ private function hours() {
290
+
291
+ ?>
292
+ <div>
293
+ <label for="<?php echo $this->id; ?>-hours">
294
+ <?php _e( 'Hours', 'google-calendar-events' ) ?>
295
+ <select name="" id="<?php echo $this->id; ?>-hours">
296
+ <option value="" data-preview=""></option>
297
+ <option value="g" <?php selected( 'g', strpbrk( 'g', $this->value ) ); ?> data-preview="<?php echo date( 'g', $this->timestamp ); ?>"><?php echo date( 'g', $this->timestamp ) . ' (12h)'; ?></option>
298
+ <option value="G" <?php selected( 'G', strpbrk( 'G', $this->value ) ); ?> data-preview="<?php echo date( 'G', $this->timestamp - 43200 ); ?>"><?php echo date( 'G', $this->timestamp - 43200 ) . ' (24h)'; ?></option>
299
+ <option value="h" <?php selected( 'h', strpbrk( 'h', $this->value ) ); ?> data-preview="<?php echo date( 'h', $this->timestamp ); ?>"><?php echo date( 'h', $this->timestamp ) . ' (12h)'; ?></option>
300
+ <option value="H" <?php selected( 'H', strpbrk( 'H', $this->value ) ); ?> data-preview="<?php echo date( 'H', $this->timestamp - 43200 ); ?>"><?php echo date( 'H', $this->timestamp - 43200 ) . ' (24h)'; ?></option>
301
+ </select>
302
+ </label>
303
+ </div>
304
+ <?php
305
+
306
+ }
307
+
308
+ /**
309
+ * Print minutes input.
310
+ *
311
+ * @since 3.0.0
312
+ * @access private
313
+ */
314
+ private function minutes() {
315
+
316
+ ?>
317
+ <div>
318
+ <label for="<?php echo $this->id; ?>-minutes">
319
+ <?php _e( 'Minutes', 'google-calendar-events' ); ?>
320
+ <select name="" id="<?php echo $this->id; ?>-minutes">
321
+ <option value="" data-preview=""></option>
322
+ <option value="i" <?php selected( 'i', strpbrk( 'i', $this->value ) ); ?> data-preview="<?php echo date( 'i', $this->timestamp ); ?>"><?php echo date( 'i', $this->timestamp ); ?></option>
323
+ </select>
324
+ </label>
325
+ </div>
326
+ <?php
327
+
328
+ }
329
+
330
+ /**
331
+ * Print meridiem input.
332
+ *
333
+ * @since 3.0.0
334
+ * @access private
335
+ */
336
+ private function meridiem() {
337
+
338
+ ?>
339
+ <div>
340
+ <label for="<?php echo $this->id; ?>-meridiem">
341
+ <?php _e( 'Meridiem', 'google-calendar-events' ); ?>
342
+ <select name="" id="<?php echo $this->id; ?>-meridiem">
343
+ <option value="" data-preview=""></option>
344
+ <option value="a" <?php selected( 'a', strpbrk( 'a', $this->value ) ); ?> data-preview="<?php echo date( 'a', $this->timestamp ); ?>"><?php echo date( 'a', $this->timestamp ); ?></option>
345
+ <option value="A" <?php selected( 'A', strpbrk( 'A', $this->value ) ); ?> data-preview="<?php echo date( 'A', $this->timestamp ); ?>"><?php echo date( 'A', $this->timestamp ); ?></option>
346
+ </select>
347
+ </label>
348
+ </div>
349
+ <?php
350
+
351
+ }
352
+
353
+ /**
354
+ * Print divider input.
355
+ *
356
+ * @since 3.0.0
357
+ * @access private
358
+ */
359
+ private function divider() {
360
+
361
+ ?>
362
+ <div>
363
+ <label for="<?php echo $this->id; ?>-divider">
364
+ <?php _ex( 'Divider', 'A character to separate two elements', 'google-calendar-events' ); ?>
365
+ <select name="" id="<?php echo $this->id; ?>-divider">
366
+ <option value="" data-preview=""></option>
367
+ <option value="." <?php selected( '.', strpbrk( '.', $this->value ) ); ?> data-preview="." data-trim="true">.</option>
368
+ <option value=", " <?php selected( ',', strpbrk( ',', $this->value ) ); ?> data-preview=", " data-trim="true">,</option>
369
+ <option value=":" <?php selected( ':', strpbrk( ':', $this->value ) ); ?> data-preview=":" data-trim="true">:</option>
370
+ <option value="-" <?php selected( '-', strpbrk( '-', $this->value ) ); ?> data-preview="-" data-trim="true">-</option>
371
+ <option value="/" <?php selected( '/', strpbrk( '/', $this->value ) ); ?> data-preview="/" data-trim="true">/</option>
372
+ </select>
373
+ </label>
374
+ </div>
375
+ <?php
376
+
377
+ }
378
+
379
+ }
includes/admin/fields/license.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * License Field
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Fields;
8
+
9
+ use SimpleCalendar\Abstracts\Field;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * License input field.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class License extends Field {
21
+
22
+ /**
23
+ * Add-on.
24
+ *
25
+ * @access private
26
+ * @var string
27
+ */
28
+ private $addon = '';
29
+
30
+ /**
31
+ * Construct.
32
+ *
33
+ * @since 3.0.0
34
+ *
35
+ * @param array $field
36
+ */
37
+ public function __construct( $field ) {
38
+ $this->addon = isset( $field['addon'] ) ? esc_attr( $field['addon'] ) : '';
39
+ $this->type_class = 'simcal-field-license';
40
+ parent::__construct( $field );
41
+ }
42
+
43
+ /**
44
+ * Outputs the field markup.
45
+ *
46
+ * @since 3.0.0
47
+ */
48
+ public function html() {
49
+
50
+ if ( ! empty( $this->addon ) ) {
51
+
52
+ $status = simcal_get_license_status( $this->addon );
53
+
54
+ if ( empty( $status ) || in_array( $status, array( 'valid', 'invalid', 'deactivated' ) ) ) {
55
+ $display_activate = 'display: inline-block';
56
+ $display_deactivate = 'display: none';
57
+ $active = 'valid' == $status ? 'display: block' : 'display: none';
58
+ $disabled = '';
59
+ } else {
60
+ $display_activate = $active = 'display: none';
61
+ $display_deactivate = 'display: inline-block';
62
+ $disabled = empty( $this->value ) ? '' : 'disabled="disabled"';
63
+ }
64
+
65
+ ?>
66
+ <div class="simcal-addon-manage-license-field" data-addon="<?php echo $this->addon; ?>">
67
+
68
+ <input type="text" <?php echo $disabled; ?>
69
+ name="<?php echo $this->name; ?>"
70
+ id="<?php echo $this->id; ?>"
71
+ value="<?php echo $this->value; ?>"
72
+ class="<?php echo $this->class; ?>" />
73
+
74
+ <span class="simcal-addon-manage-license-buttons">
75
+
76
+ <button class="button-secondary simcal-addon-manage-license deactivate" data-add-on="<?php echo $this->addon; ?>" style="<?php echo $display_deactivate; ?>">
77
+ <i class="simcal-icon-spinner simcal-icon-spin" style="display: none;"></i><?php _e( 'Deactivate', 'google-calendar-events' ); ?>
78
+ </button>
79
+
80
+ <button class="button-secondary simcal-addon-manage-license activate" data-add-on="<?php echo $this->addon; ?>" style="<?php echo $display_activate; ?>">
81
+ <i class="simcal-icon-spinner simcal-icon-spin" style="display: none;"></i><?php _e( 'Activate', 'google-calendar-events' ); ?>
82
+ </button>
83
+
84
+ <span class="error" style="color: red; display: none"> </span>
85
+
86
+ <strong class="label" style="color:green; <?php echo $active; ?>"> <?php _e( '(active)', 'google-calendar-events' ); ?></strong>
87
+
88
+ </span>
89
+
90
+ </div>
91
+ <?php
92
+
93
+ }
94
+
95
+ }
96
+
97
+ }
includes/admin/fields/nonce.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Nonce Field
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Fields;
8
+
9
+ use SimpleCalendar\Abstracts\Field;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Nonce field.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Nonce extends Field {
21
+
22
+ /**
23
+ * Construct.
24
+ *
25
+ * @since 3.0.0
26
+ *
27
+ * @param array $field
28
+ */
29
+ public function __construct( $field ) {
30
+ parent::__construct( $field );
31
+ }
32
+
33
+ /**
34
+ * Outputs the field markup.
35
+ *
36
+ * @since 3.0.0
37
+ */
38
+ public function html() {
39
+ wp_nonce_field( $this->name, $this->value );
40
+ }
41
+
42
+ }
includes/admin/fields/radio.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Radio Field
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Fields;
8
+
9
+ use SimpleCalendar\Abstracts\Field;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Radio input field.
17
+ *
18
+ * Outputs a fieldset of radio inputs for multiple choices.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Radio extends Field {
23
+
24
+ /**
25
+ * Inline radios.
26
+ *
27
+ * @access private
28
+ * @var bool
29
+ */
30
+ private $inline = false;
31
+
32
+ /**
33
+ * Construct.
34
+ *
35
+ * @since 3.0.0
36
+ *
37
+ * @param array $field
38
+ */
39
+ public function __construct( $field ) {
40
+
41
+ $this->type_class = 'simcal-field-radios';
42
+ $this->inline = isset( $field['inline'] ) ? ( 'inline' == $field['inline'] ? true : false ) : false;
43
+
44
+ parent::__construct( $field );
45
+ }
46
+
47
+ /**
48
+ * Outputs the field markup.
49
+ *
50
+ * @since 3.0.0
51
+ */
52
+ public function html() {
53
+
54
+ ?>
55
+ <fieldset id="<?php echo $this->id; ?>"
56
+ class="<?php echo $this->class; ?>"
57
+ <?php echo $this->style ? 'style="' . $this->style .'"' : ''; ?>>
58
+ <?php
59
+
60
+ echo $this->description ? '<p class="description">' . wp_kses_post( $this->description ) . '</p>' : '';
61
+
62
+ if ( ! empty( $this->title ) ) :
63
+
64
+ ?>
65
+ <legend class="screen-reader-text">
66
+ <span><?php echo $this->title; ?></span>
67
+ </legend>
68
+ <?php
69
+
70
+ endif;
71
+
72
+ ?>
73
+ <ul <?php echo $this->inline === true ? 'class="simcal-field-radios-inline"' : ''; ?>>
74
+ <?php foreach ( $this->options as $option => $name ) : ?>
75
+ <li>
76
+ <label for="<?php echo $this->id . '-' . trim( strval( $option ) ); ?>">
77
+ <input name="<?php echo $this->name; ?>"
78
+ id="<?php echo $this->id . '-' . trim( strval( $option ) ); ?>"
79
+ class="simcal-field simcal-field-radio"
80
+ type="radio"
81
+ value="<?php echo trim( strval( $option ) ); ?>"
82
+ <?php echo $this->attributes; ?>
83
+ <?php checked( $option, $this->value, true ); ?>
84
+ />
85
+ <?php echo esc_attr( $name ); ?>
86
+ </label>
87
+ </li>
88
+ <?php endforeach; ?>
89
+ </ul>
90
+
91
+ <?php echo $this->tooltip; ?>
92
+
93
+ </fieldset>
94
+ <?php
95
+
96
+ }
97
+
98
+ }
includes/admin/fields/select.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Select Field
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Fields;
8
+
9
+ use SimpleCalendar\Abstracts\Field;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Select input field.
17
+ *
18
+ * A standard dropdown or a multiselect field.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Select extends Field {
23
+
24
+ /**
25
+ * Enhanced select.
26
+ *
27
+ * @access public
28
+ * @var bool
29
+ */
30
+ public $enhanced = false;
31
+
32
+ /**
33
+ * Multiselect.
34
+ *
35
+ * @var bool
36
+ */
37
+ public $multiselect = false;
38
+
39
+ /**
40
+ * Allow void option.
41
+ *
42
+ * @access private
43
+ * @var bool
44
+ */
45
+ private $allow_void = false;
46
+
47
+ /**
48
+ * Construct.
49
+ *
50
+ * @since 3.0.0
51
+ *
52
+ * @param array $field
53
+ */
54
+ public function __construct( $field ) {
55
+
56
+ $class = 'simcal-field-select';
57
+
58
+ $enhanced = isset( $field['enhanced'] ) ? $field['enhanced'] : '';
59
+ if ( 'enhanced' == $enhanced ) {
60
+ $this->enhanced = true;
61
+ $class .= ' simcal-field-select-enhanced';
62
+ }
63
+
64
+ $multiselect = isset( $field['multiselect'] ) ? $field['multiselect'] : '';
65
+ if ( 'multiselect' == $multiselect ) {
66
+ $this->multiselect = true;
67
+ $class .= ' simcal-field-multiselect';
68
+ }
69
+
70
+ $this->type_class = $class;
71
+
72
+ $allow_void = isset( $field['allow_void'] ) ? $field['allow_void'] : '';
73
+ $this->allow_void = 'allow_void' == $allow_void ? true : false;
74
+
75
+ parent::__construct( $field );
76
+ }
77
+
78
+ /**
79
+ * Outputs the field markup.
80
+ *
81
+ * @since 3.0.0
82
+ */
83
+ public function html() {
84
+
85
+ if ( $this->multiselect === true && ! is_array( $this->value ) ) {
86
+ $this->value = explode( ',', $this->value );
87
+ }
88
+
89
+ ?>
90
+ <select name="<?php echo $this->name; ?><?php if ( $this->multiselect === true ) { echo '[]'; } ?>"
91
+ id="<?php echo $this->id; ?>"
92
+ style="<?php echo $this->style; ?>"
93
+ class="<?php echo $this->class; ?>"
94
+ <?php echo $this->attributes; ?>
95
+ <?php echo ( $this->multiselect === true ) ? ' multiple="multiple"' : ''; ?>>
96
+ <?php
97
+
98
+ if ( $this->allow_void === true ) {
99
+ echo '<option value=""' . selected( '', $this->value, false ) . '></option>';
100
+ }
101
+
102
+ foreach ( $this->options as $option => $name ) {
103
+ if ( is_array( $this->value ) ) {
104
+ $selected = selected( in_array( $option, $this->value ), true, false );
105
+ } else {
106
+ $selected = selected( $this->value, trim( strval( $option ) ), false );
107
+ }
108
+ echo '<option value="' . $option . '" ' . $selected . '>' . esc_attr( $name ) . '</option>';
109
+ }
110
+
111
+ ?>
112
+ </select>
113
+ <?php
114
+
115
+ echo $this->tooltip;
116
+
117
+ if ( ! empty( $this->description ) ) {
118
+ echo '<p class="description">' . wp_kses_post( $this->description ) . '</p>';
119
+ }
120
+
121
+ }
122
+
123
+ }
includes/admin/fields/standard.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Standard Field
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Fields;
8
+
9
+ use SimpleCalendar\Abstracts\Field;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Standard input field.
17
+ *
18
+ * For standard text inputs and subtypes (e.g. number, password, email...).
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Standard extends Field {
23
+
24
+ /**
25
+ * Field subtype.
26
+ *
27
+ * @var string
28
+ */
29
+ public $subtype = '';
30
+
31
+ /**
32
+ * Construct.
33
+ *
34
+ * @since 3.0.0
35
+ *
36
+ * @param array $field
37
+ */
38
+ public function __construct( $field ) {
39
+
40
+ $this->subtype = isset( $field['subtype'] ) ? esc_attr( $field['subtype'] ) : 'text';
41
+ $this->type_class = 'simcal-field-' . $this->subtype;
42
+
43
+ parent::__construct( $field );
44
+ }
45
+
46
+ /**
47
+ * Outputs the field markup.
48
+ *
49
+ * @since 3.0.0
50
+ */
51
+ public function html() {
52
+
53
+ ?>
54
+ <input type="<?php echo $this->subtype; ?>"
55
+ name="<?php echo $this->name; ?>"
56
+ id="<?php echo $this->id; ?>"
57
+ value="<?php echo $this->value; ?>"
58
+ class="<?php echo $this->class; ?>"<?php
59
+ echo $this->style ? 'style="' . $this->style . '" ' : ' ';
60
+ echo $this->placeholder ? 'placeholder="' . $this->placeholder . '"' : ' ';
61
+ echo $this->attributes; ?>/>
62
+ <?php
63
+
64
+ echo $this->tooltip;
65
+
66
+ if ( ! empty( $this->description ) ) {
67
+ echo '<p class="description">' . wp_kses_post( $this->description ) . '</p>';
68
+ }
69
+
70
+ if ( is_string( $this->validation ) && ! empty ( $this->validation ) ) {
71
+ echo $this->validation;
72
+ }
73
+
74
+ }
75
+
76
+ }
includes/admin/fields/textarea.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Textarea Field
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Fields;
8
+
9
+ use SimpleCalendar\Abstracts\Field;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Textarea input field.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Textarea extends Field {
21
+
22
+ /**
23
+ * Construct.
24
+ *
25
+ * @since 3.0.0
26
+ *
27
+ * @param array $field
28
+ */
29
+ public function __construct( $field ) {
30
+
31
+ $this->type_class = 'simcal-field-textarea';
32
+
33
+ parent::__construct( $field );
34
+
35
+ if ( ! empty( $field['value'] ) ) {
36
+ $this->value = esc_textarea( $field['value'] );
37
+ }
38
+ if ( ! empty( $field['default'] ) ) {
39
+ $this->default = esc_textarea( $field['default'] );
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Outputs the field markup.
45
+ *
46
+ * @since 3.0.0
47
+ */
48
+ public function html() {
49
+
50
+ ?>
51
+ <textarea
52
+ name="<?php echo $this->name; ?>"
53
+ id="<?php echo $this->id; ?>"
54
+ <?php
55
+ echo $this->class ? 'class="' . $this->class . '" ' : '';
56
+ echo $this->placeholder ? 'placeholder="' . $this->placeholder . '" ' : '';
57
+ echo $this->style ? 'style="' . $this->style . '" ' : '';
58
+ echo $this->attributes;
59
+ ?>><?php echo $this->value; ?></textarea>
60
+ <?php
61
+
62
+ echo $this->tooltip;
63
+
64
+ if ( ! empty( $this->description ) ) {
65
+ echo '<p class="description">' . wp_kses_post( $this->description ) . '</p>';
66
+ }
67
+
68
+ }
69
+
70
+ }
includes/admin/menus.php ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Menus
4
+ *
5
+ * @package SimpleCalendar\Admin
6
+ */
7
+ namespace SimpleCalendar\Admin;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Admin Menus.
15
+ *
16
+ * Handles the plugin admin dashboard menus.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Menus {
21
+
22
+ /**
23
+ * The main menu screen hook.
24
+ *
25
+ * @access public
26
+ * @var string
27
+ */
28
+ public static $main_menu = '';
29
+
30
+ /**
31
+ * Plugin basename.
32
+ *
33
+ * @access private
34
+ * @var string
35
+ */
36
+ private static $plugin = '';
37
+
38
+ /**
39
+ * Set properties.
40
+ *
41
+ * @since 3.0.0
42
+ */
43
+ public function __construct() {
44
+
45
+ self::$main_menu = 'edit.php?post_type=calendar';
46
+
47
+ add_action( 'admin_menu', array( __CLASS__, 'add_menu_items' ) );
48
+
49
+ self::$plugin = plugin_basename( SIMPLE_CALENDAR_MAIN_FILE );
50
+
51
+ new Welcome();
52
+
53
+ // Conditional redirect to welcome page on activation.
54
+ add_action( 'admin_init', array( $this, 'admin_redirects' ) );
55
+ // Links and meta content in plugins page.
56
+ add_filter( 'plugin_action_links_' . self::$plugin, array( __CLASS__, 'plugin_action_links' ), 10, 5 );
57
+ add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
58
+ // Custom text in admin footer.
59
+ add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 );
60
+ }
61
+
62
+ /**
63
+ * Add menu items.
64
+ *
65
+ * @since 3.0.0
66
+ */
67
+ public static function add_menu_items() {
68
+
69
+ add_submenu_page(
70
+ self::$main_menu,
71
+ __( 'Settings', 'google-calendar-events' ),
72
+ __( 'Settings', 'google-calendar-events' ),
73
+ 'manage_options',
74
+ 'simple-calendar_settings',
75
+ function () {
76
+ $page = new Pages( 'settings' );
77
+ $page->html();
78
+ }
79
+ );
80
+
81
+ add_submenu_page(
82
+ self::$main_menu,
83
+ __( 'Add-ons', 'google-calendar-events' ),
84
+ __( 'Add-ons', 'google-calendar-events' ),
85
+ 'manage_options',
86
+ 'simple-calendar_add_ons',
87
+ function() {
88
+ $page = new Pages( 'add-ons' );
89
+ $page->html();
90
+ }
91
+ );
92
+
93
+ add_submenu_page(
94
+ self::$main_menu,
95
+ __( 'Tools', 'google-calendar-events' ),
96
+ __( 'Tools', 'google-calendar-events' ),
97
+ 'manage_options',
98
+ 'simple-calendar_tools',
99
+ function () {
100
+ $page = new Pages( 'tools' );
101
+ $page->html();
102
+ }
103
+ );
104
+
105
+ do_action( 'simcal_admin_add_menu_items' );
106
+ }
107
+
108
+ /**
109
+ * Action links in plugins page.
110
+ *
111
+ * @since 3.0.0
112
+ *
113
+ * @param array $action_links
114
+ * @param string $file
115
+ *
116
+ * @return array
117
+ */
118
+ public static function plugin_action_links( $action_links, $file ) {
119
+
120
+ if ( self::$plugin == $file ) {
121
+
122
+ $links = array();
123
+ $links['settings'] = '<a href="' . admin_url( 'edit.php?post_type=calendar&page=simple-calendar_settings' ) . '">' . __( 'Settings', 'google-calendar-events' ) . '</a>';
124
+ $links['feeds'] = '<a href="' . admin_url( 'edit.php?post_type=calendar' ) . '">' . __( 'Calendars', 'google-calendar-events' ) . '</a>';
125
+
126
+ return apply_filters( 'simcal_plugin_action_links', array_merge( $links, $action_links ) );
127
+ }
128
+
129
+ return $action_links;
130
+ }
131
+
132
+ /**
133
+ * Links in plugin meta in plugins page.
134
+ *
135
+ * @since 3.0.0
136
+ *
137
+ * @param array $meta_links
138
+ * @param string $file
139
+ *
140
+ * @return array
141
+ */
142
+ public static function plugin_row_meta( $meta_links, $file ) {
143
+
144
+ if ( self::$plugin == $file ) {
145
+
146
+ $links = array();
147
+ $links['github'] = '<a href="' . simcal_get_url( 'github' ) . '" target="_blank" >GitHub</a>';
148
+ $links['documentation'] = '<a href="' . simcal_ga_campaign_url( simcal_get_url( 'docs' ), 'core-plugin', 'plugin-listing' ) . '" target="_blank" >' .
149
+ __( 'Documentation', 'google-calendar-events' ) . '</a>';
150
+ $links['support'] = '<a href="' . simcal_get_url( 'support' ) . '" target="_blank" >' .
151
+ __( 'Support', 'google-calendar-events' ) . '</a>';
152
+ $links['add-ons'] = '<a href="' . simcal_ga_campaign_url( simcal_get_url( 'add-ons' ), 'core-plugin', 'plugin-listing' ) . '" target="_blank" >' .
153
+ __( 'Add-ons', 'google-calendar-events' ) . '</a>';
154
+
155
+ return apply_filters( 'simcal_plugin_action_links', array_merge( $meta_links, $links ) );
156
+ }
157
+
158
+ return $meta_links;
159
+ }
160
+
161
+ /**
162
+ * Handle redirects to welcome page after install and updates.
163
+ *
164
+ * Transient must be present, the user must have access rights, and we must ignore the network/bulk plugin updaters.
165
+ *
166
+ * @since 3.0.0
167
+ */
168
+ public function admin_redirects() {
169
+
170
+ $transient = get_transient( '_simple-calendar_activation_redirect' );
171
+
172
+ if ( ! $transient || is_network_admin() || isset( $_GET['activate-multi'] ) || ! current_user_can( 'manage_options' ) ) {
173
+ return;
174
+ }
175
+
176
+ delete_transient( '_simple-calendar_activation_redirect' );
177
+
178
+ // Do not redirect if already on welcome page screen.
179
+ if ( ! empty( $_GET['page'] ) && in_array( $_GET['page'], array( 'simple-calendar_about' ) ) ) {
180
+ return;
181
+ }
182
+
183
+ $url = add_query_arg(
184
+ 'simcal_install',
185
+ esc_attr( $transient ),
186
+ admin_url( 'index.php?page=simple-calendar_about' )
187
+ );
188
+ wp_safe_redirect( $url );
189
+ exit;
190
+ }
191
+
192
+ /**
193
+ * Admin footer text filter callback.
194
+ *
195
+ * Change this plugin screens admin footer text.
196
+ *
197
+ * @since 3.0.0
198
+ *
199
+ * @param $footer_text
200
+ *
201
+ * @return string|void
202
+ */
203
+ public function admin_footer_text( $footer_text ) {
204
+
205
+ // Check to make sure we're on a SimpleCal admin page
206
+ $screen = simcal_is_admin_screen();
207
+ if ( $screen !== false ) {
208
+
209
+ if ( 'calendar' == $screen ) {
210
+
211
+ // Add Drip promo signup form (@see Newsletter meta box).
212
+
213
+ $drip_form_id = '9817628';
214
+
215
+ ?>
216
+ <form id="simcal-drip-form"
217
+ method="post"
218
+ target="_blank"
219
+ action="https://www.getdrip.com/forms/<?php echo $drip_form_id; ?>/submissions/"
220
+ data-drip-embedded-form="<?php echo $drip_form_id; ?>">
221
+ <input type="hidden"
222
+ id="simcal-drip-real-field-first_name"
223
+ name="fields[first_name]"
224
+ value="" />
225
+ <input type="hidden"
226
+ id="simcal-drip-real-field-email"
227
+ name="fields[email]"
228
+ value="" />
229
+ <input type="submit"
230
+ class="hidden"/>
231
+ </form>
232
+ <?php
233
+
234
+ }
235
+
236
+ // Change the footer text
237
+ if ( ! get_option( 'simple-calendar_admin_footer_text_rated' ) ) {
238
+
239
+ $footer_text = sprintf(
240
+ __( 'If you like <strong>Simple Calendar</strong> please leave us a %s&#9733;&#9733;&#9733;&#9733;&#9733; rating on WordPress.org%s. A huge thank you in advance!', 'google-calendar-events' ),
241
+ '<a href="https://wordpress.org/support/view/plugin-reviews/google-calendar-events?filter=5#postform" target="_blank" class="simcal-rating-link" data-rated="' . esc_attr__( 'Thanks :)', 'google-calendar-events' ) . '">', '</a>'
242
+ );
243
+
244
+ $footer_text .= '<script type="text/javascript">';
245
+ $footer_text .= "jQuery( 'a.simcal-rating-link' ).click( function() {
246
+ jQuery.post( '" . \SimpleCalendar\plugin()->ajax_url() . "', { action: 'simcal_rated' } );
247
+ jQuery( this ).parent().text( jQuery( this ).data( 'rated' ) );
248
+ });";
249
+ $footer_text .= '</script>';
250
+
251
+ } else {
252
+
253
+ $footer_text = __( 'Thank you for using Simple Calendar!', 'google-calendar-events' );
254
+
255
+ }
256
+
257
+ }
258
+
259
+ return $footer_text;
260
+ }
261
+
262
+ }
includes/admin/meta-boxes.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Meta Boxes
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin;
8
+
9
+ use SimpleCalendar\Admin\Metaboxes as Metabox;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Meta boxes class.
17
+ *
18
+ * Handles write panels in post types and post meta.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Meta_Boxes {
23
+
24
+ /**
25
+ * Saved meta boxes status.
26
+ *
27
+ * @access private
28
+ * @var bool
29
+ */
30
+ private static $saved_meta_boxes = false;
31
+
32
+ /**
33
+ * Post types to attach calendars.
34
+ *
35
+ * @access private
36
+ * @var array
37
+ */
38
+ private $post_types = array();
39
+
40
+ /**
41
+ * Hook in tabs.
42
+ *
43
+ * @since 3.0.0
44
+ */
45
+ public function __construct() {
46
+
47
+ $settings = get_option( 'simple-calendar_settings_calendars' );
48
+ if ( isset( $settings['general']['attach_calendars_posts'] ) ) {
49
+ $this->post_types = $settings['general']['attach_calendars_posts'];
50
+ }
51
+
52
+ // Load meta boxes to save settings.
53
+ new Metabox\Settings();
54
+ new Metabox\Attach_Calendar();
55
+ new Metabox\Newsletter();
56
+ do_action( 'simcal_load_meta_boxes' );
57
+
58
+ // Add meta boxes.
59
+ add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 30 );
60
+
61
+ // Process meta boxes.
62
+ add_action( 'simcal_save_settings_meta','\SimpleCalendar\Admin\Metaboxes\Settings::save', 10, 2 );
63
+ add_action( 'simcal_save_attach_calendar_meta','\SimpleCalendar\Admin\Metaboxes\Attach_Calendar::save', 10, 2 );
64
+
65
+ // Save meta boxes data.
66
+ add_action( 'save_post', array( $this, 'save_meta_boxes' ), 1, 2 );
67
+
68
+ // Uncomment this for debugging $_POST while saving a meta box.
69
+ // add_action( 'save_post', function() { echo '<pre>'; print_r( $_POST ); echo '</pre>'; die(); } );
70
+ }
71
+
72
+ /**
73
+ * Add meta boxes.
74
+ *
75
+ * @since 3.0.0
76
+ */
77
+ public function add_meta_boxes() {
78
+
79
+ add_meta_box(
80
+ 'simcal-calendar-settings',
81
+ __( 'Calendar Settings', 'google-calendar-events' ),
82
+ '\SimpleCalendar\Admin\Metaboxes\Settings::html',
83
+ 'calendar',
84
+ 'normal',
85
+ 'core'
86
+ );
87
+
88
+ $addons = apply_filters( 'simcal_installed_addons', array() );
89
+ if ( empty( $addons ) ) {
90
+
91
+ add_meta_box(
92
+ 'simcal-newsletter',
93
+ __( 'Get 20% off Google Calendar Pro!', 'google-calendar-events' ),
94
+ '\SimpleCalendar\Admin\Metaboxes\Newsletter::html',
95
+ 'calendar',
96
+ 'side',
97
+ 'default'
98
+ );
99
+
100
+ }
101
+
102
+ add_meta_box(
103
+ 'simcal-get-shortcode',
104
+ __( 'Calendar Shortcode', 'google-calendar-events' ),
105
+ '\SimpleCalendar\Admin\Metaboxes\Get_Shortcode::html',
106
+ 'calendar',
107
+ 'side',
108
+ 'default'
109
+ );
110
+
111
+ // Add meta box if there are calendars.
112
+ if ( ( true == simcal_get_calendars() ) && ! empty( $this->post_types ) ) {
113
+ foreach ( $this->post_types as $post_type ) {
114
+ add_meta_box(
115
+ 'simcal-attach-calendar',
116
+ __( 'Attach Calendar', 'google-calendar-events' ),
117
+ '\SimpleCalendar\Admin\Metaboxes\Attach_Calendar::html',
118
+ $post_type,
119
+ 'side',
120
+ 'low'
121
+ );
122
+ }
123
+ }
124
+
125
+ do_action( 'simcal_add_meta_boxes' );
126
+ }
127
+
128
+ /**
129
+ * Check if we're saving, then trigger action.
130
+ *
131
+ * @since 3.0.0
132
+ *
133
+ * @param int $post_id
134
+ * @param object $post
135
+ *
136
+ * @return void
137
+ */
138
+ public function save_meta_boxes( $post_id, $post ) {
139
+
140
+ // $post_id and $post are required.
141
+ if ( empty( $post_id ) || empty( $post ) || self::$saved_meta_boxes ) {
142
+ return;
143
+ }
144
+
145
+ // Don't save meta boxes for revisions or autosaves.
146
+ if ( defined( 'DOING_AUTOSAVE' ) || is_int( wp_is_post_revision( $post ) ) || is_int( wp_is_post_autosave( $post ) ) ) {
147
+ return;
148
+ }
149
+
150
+ // Check the nonce.
151
+ if ( empty( $_POST['simcal_meta_nonce'] ) || ! wp_verify_nonce( $_POST['simcal_meta_nonce'], 'simcal_save_data' ) ) {
152
+ return;
153
+ }
154
+
155
+ // Check the post being saved == the $post_id to prevent triggering this call for other save_post events.
156
+ if ( empty( $_POST['post_ID'] ) || $_POST['post_ID'] != $post_id ) {
157
+ return;
158
+ }
159
+
160
+ // Check user has permission to edit
161
+ if ( ! current_user_can( 'edit_post', $post_id ) ) {
162
+ return;
163
+ }
164
+
165
+ // We need this save event to run once to avoid potential endless loops.
166
+ // This would have been perfect:
167
+ // `remove_action( current_filter(), __METHOD__ );`
168
+ // But cannot be used due to a WordPress bug:
169
+ // @link https://core.trac.wordpress.org/ticket/17817
170
+ // @see also https://github.com/woothemes/woocommerce/issues/6485
171
+ self::$saved_meta_boxes = true;
172
+
173
+ // Check the post type.
174
+ if ( 'calendar' == $post->post_type ) {
175
+ do_action( 'simcal_save_settings_meta', $post_id, $post );
176
+ } elseif ( in_array( $post->post_type, $this->post_types ) ) {
177
+ do_action( 'simcal_save_attach_calendar_meta', $post_id, $post );
178
+ }
179
+
180
+ do_action( 'simcal_save_meta_boxes', $post_id, $post );
181
+ }
182
+
183
+ }
includes/admin/metaboxes/attach-calendar.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Add Calendar Meta Box
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Metaboxes;
8
+
9
+ use SimpleCalendar\Abstracts\Meta_Box;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Attach a calendar to a post.
17
+ *
18
+ * Meta box for attaching calendars to WordPress posts.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Attach_Calendar implements Meta_Box {
23
+
24
+ /**
25
+ * Output the meta box markup.
26
+ *
27
+ * @since 3.0.0
28
+ *
29
+ * @param \WP_Post $post
30
+ */
31
+ public static function html( $post ) {
32
+
33
+ // @see Meta_Boxes::save_meta_boxes()
34
+ wp_nonce_field( 'simcal_save_data', 'simcal_meta_nonce' );
35
+
36
+ $calendars = simcal_get_calendars();
37
+
38
+ simcal_print_field( array(
39
+ 'type' => 'select',
40
+ 'id' => '_simcal_attach_calendar_id',
41
+ 'name' => '_simcal_attach_calendar_id',
42
+ 'enhanced' => count( $calendars ) > 15 ? 'enhanced' : '',
43
+ 'allow_void' => 'allow_void',
44
+ 'value' => absint( get_post_meta( $post->ID, '_simcal_attach_calendar_id', true ) ),
45
+ 'options' => $calendars,
46
+ 'attributes' => array(
47
+ 'data-allowclear' => 'true',
48
+ )
49
+ ) );
50
+
51
+ $position = get_post_meta( $post->ID, '_simcal_attach_calendar_position', true );
52
+
53
+ simcal_print_field( array(
54
+ 'type' => 'radio',
55
+ 'id' => '_simcal_attach_calendar_position',
56
+ 'name' => '_simcal_attach_calendar_position',
57
+ 'value' => $position ? $position : 'after',
58
+ 'options' => array(
59
+ 'after' => __( 'After content', 'google-calendar-events' ),
60
+ 'before' => __( 'Before content', 'google-calendar-events' ),
61
+ ),
62
+ ) );
63
+
64
+ }
65
+
66
+ /**
67
+ * Validate and save the meta box fields.
68
+ *
69
+ * @since 3.0.0
70
+ *
71
+ * @param int $post_id
72
+ * @param \WP_Post $post
73
+ */
74
+ public static function save( $post_id, $post ) {
75
+
76
+ $id = isset( $_POST['_simcal_attach_calendar_id'] ) ? absint( $_POST['_simcal_attach_calendar_id'] ) : '';
77
+ update_post_meta( $post_id, '_simcal_attach_calendar_id', $id );
78
+
79
+ $position = isset( $_POST['_simcal_attach_calendar_position'] ) ? sanitize_title( $_POST['_simcal_attach_calendar_position'] ) : 'after';
80
+ update_post_meta( $post_id, '_simcal_attach_calendar_position', $position );
81
+
82
+ }
83
+
84
+ }
includes/admin/metaboxes/get-shortcode.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Get Shortcode Meta Box
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Metaboxes;
8
+
9
+ use SimpleCalendar\Abstracts\Meta_Box;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Get a shortcode for the current calendar.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Get_Shortcode implements Meta_Box {
21
+
22
+ /**
23
+ * Output HTML.
24
+ *
25
+ * @since 3.0.0
26
+ *
27
+ * @param \WP_Post $post
28
+ */
29
+ public static function html( $post ) {
30
+ simcal_print_shortcode_tip( $post->ID );
31
+ }
32
+
33
+ /**
34
+ * Save settings.
35
+ *
36
+ * @since 3.0.0
37
+ *
38
+ * @param int $post_id
39
+ * @param \WP_Post $post
40
+ */
41
+ public static function save( $post_id, $post ) {
42
+ // This Meta Box does not have settings.
43
+ }
44
+
45
+ }
includes/admin/metaboxes/newsletter.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Newsletter Sign Up Meta Box
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Metaboxes;
8
+
9
+ use SimpleCalendar\Abstracts\Meta_Box;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Sign up to Simple Calendar newsletter.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Newsletter implements Meta_Box {
21
+
22
+ /**
23
+ * Output HTML.
24
+ *
25
+ * @since 3.0.0
26
+ *
27
+ * @param \WP_Post $post
28
+ */
29
+ public static function html( $post ) {
30
+ simcal_newsletter_signup();
31
+ }
32
+
33
+ /**
34
+ * Save settings.
35
+ *
36
+ * @since 3.0.0
37
+ *
38
+ * @param int $post_id
39
+ * @param \WP_Post $post
40
+ */
41
+ public static function save( $post_id, $post ) {
42
+ // This meta box has no persistent settings.
43
+ }
44
+
45
+ }
includes/admin/metaboxes/settings.php ADDED
@@ -0,0 +1,905 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Calendar Feed Settings Meta Box
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Metaboxes;
8
+
9
+ use SimpleCalendar\Abstracts\Meta_Box;
10
+ use SimpleCalendar\Abstracts\Calendar;
11
+ use SimpleCalendar\Abstracts\Feed;
12
+ use SimpleCalendar\Abstracts\Field;
13
+
14
+ if ( ! defined( 'ABSPATH' ) ) {
15
+ exit;
16
+ }
17
+
18
+ /**
19
+ * Calendar feed settings.
20
+ *
21
+ * Meta box for handling an individual feed settings.
22
+ *
23
+ * @since 3.0.0
24
+ */
25
+ class Settings implements Meta_Box {
26
+
27
+ /**
28
+ * Output the meta box markup.
29
+ *
30
+ * @since 3.0.0
31
+ *
32
+ * @param \WP_Post $post
33
+ */
34
+ public static function html( $post ) {
35
+
36
+ // @see Meta_Boxes::save_meta_boxes()
37
+ wp_nonce_field( 'simcal_save_data', 'simcal_meta_nonce' );
38
+
39
+ ?>
40
+ <div class="simcal-panels-wrap">
41
+
42
+ <span class="simcal-box-handle">
43
+ <?php self::settings_handle( $post ); ?>
44
+ </span>
45
+
46
+ <ul class="simcal-tabs">
47
+ <?php self::settings_tabs( $post ); ?>
48
+ <?php do_action( 'simcal_settings_meta_tabs' ); ?>
49
+ </ul>
50
+
51
+ <div class="simcal-panels">
52
+ <div id="events-settings-panel" class="simcal-panel">
53
+ <?php self::events_settings_panel( $post ); ?>
54
+ <?php do_action( 'simcal_settings_meta_events_panel', $post->ID ); ?>
55
+ </div>
56
+ <div id="calendar-settings-panel" class="simcal-panel">
57
+ <?php do_action( 'simcal_settings_meta_calendar_panel', $post->ID ); ?>
58
+ <?php self::calendar_settings_panel( $post ); ?>
59
+ </div>
60
+ <?php
61
+ // Hook for additional settings panels.
62
+ do_action( 'simcal_settings_meta_panels', $post->ID );
63
+ // Thus advanced panel is always the last one:
64
+ ?>
65
+ <div id="advanced-settings-panel" class="simcal-panel">
66
+ <?php self::advanced_settings_panel( $post ) ?>
67
+ <?php do_action( 'simcal_settings_meta_advanced_panel', $post->ID ); ?>
68
+ </div>
69
+ </div>
70
+
71
+ <div class="clear">
72
+ </div>
73
+
74
+ </div>
75
+ <?php
76
+
77
+ }
78
+
79
+ /**
80
+ * Print the meta box settings handle.
81
+ *
82
+ * @since 3.0.0
83
+ * @access private
84
+ *
85
+ * @param \WP_Post $post
86
+ */
87
+ private static function settings_handle( $post ) {
88
+
89
+ $feed_options = $calendar_options = $calendar_views = array();
90
+
91
+ $feed_types = simcal_get_feed_types();
92
+ foreach ( $feed_types as $feed_type ) {
93
+
94
+ $feed = simcal_get_feed( $feed_type );
95
+
96
+ if ( $feed instanceof Feed ) {
97
+ $feed_options[ $feed_type ] = $feed->name;
98
+ }
99
+ }
100
+
101
+ $calendar_types = simcal_get_calendar_types();
102
+ foreach ( $calendar_types as $calendar_type => $views ) {
103
+
104
+ $calendar = simcal_get_calendar( $calendar_type );
105
+
106
+ if ( $calendar instanceof Calendar ) {
107
+ $calendar_options[ $calendar_type ] = $calendar->name;
108
+ $calendar_views[ $calendar_type ] = $calendar->views;
109
+ }
110
+ }
111
+
112
+ if ( $feed_options ) {
113
+
114
+ if ( $feed_types = wp_get_object_terms( $post->ID, 'calendar_feed' ) ) {
115
+ $feed_type = sanitize_title( current( $feed_types )->name );
116
+ } else {
117
+ $feed_type = apply_filters( 'simcal_default_feed_type', 'google' );
118
+ }
119
+
120
+ ?>
121
+ <label for="_feed_type"><span><?php _e( 'Event Source', 'google-calendar-events' ); ?></span>
122
+ <select name="_feed_type" id="_feed_type">
123
+ <optgroup label="<?php _ex( 'Get events from', 'From which calendar source to load events from', 'google-calendar-events' ) ?>">
124
+ <?php foreach ( $feed_options as $feed => $name ) { ?>
125
+ <option value="<?php echo $feed; ?>" <?php selected( $feed, $feed_type, true ); ?>><?php echo $name; ?></option>
126
+ <?php } ?>
127
+ </optgroup>
128
+ </select>
129
+ </label>
130
+ <?php
131
+
132
+ }
133
+
134
+ if ( $calendar_options ) {
135
+
136
+ if ( $calendar_types = wp_get_object_terms( $post->ID, 'calendar_type' ) ) {
137
+ $calendar_type = sanitize_title( current( $calendar_types )->name );
138
+ } else {
139
+ $calendar_type = apply_filters( 'simcal_default_calendar_type', 'default-calendar' );
140
+ }
141
+
142
+ ?>
143
+ <label for="_calendar_type"><span><?php _e( 'Calendar', 'google-calendar-events' ); ?></span>
144
+ <select name="_calendar_type" id="_calendar_type">
145
+ <optgroup label="<?php _e( 'Calendar to use', 'google-calendar-events' ); ?>">
146
+ <?php foreach ( $calendar_options as $calendar => $name ) { ?>
147
+ <option value="<?php echo $calendar; ?>" <?php selected( $calendar, $calendar_type, true ); ?>><?php echo $name; ?></option>
148
+ <?php } ?>
149
+ </optgroup>
150
+ </select>
151
+ </label>
152
+ <?php
153
+
154
+ if ( $calendar_views ) {
155
+
156
+ $calendar_view = get_post_meta( $post->ID, '_calendar_view', true );
157
+
158
+ foreach ( $calendar_views as $calendar => $views ) {
159
+
160
+ $calendar_type_view = isset( $calendar_view[ $calendar ] ) ? $calendar_view[ $calendar ] : '';
161
+
162
+ ?>
163
+ <label for="_calendar_view_<?php echo $calendar; ?>"><span><?php _e( 'View', 'google-calendar-events' ); ?></span>
164
+ <select name="_calendar_view[<?php echo $calendar; ?>]" id="_calendar_view_<?php echo $calendar; ?>">
165
+ <optgroup label="<?php _e( 'View to display', 'google-calendar-events' ); ?>">
166
+ <?php foreach ( $views as $view => $name ) { ?>
167
+ <option value="<?php echo $view; ?>" <?php selected( $view, $calendar_type_view, true ); ?>><?php echo $name; ?></option>
168
+ <?php } ?>
169
+ </optgroup>
170
+ </select>
171
+ </label>
172
+ <?php
173
+
174
+ }
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Print settings tabs.
181
+ *
182
+ * @since 3.0.0
183
+ * @access private
184
+ *
185
+ * @param \WP_Post $post
186
+ */
187
+ private static function settings_tabs( $post ) {
188
+
189
+ // Hook to add more tabs.
190
+ $tabs = apply_filters( 'simcal_settings_meta_tabs_li', array(
191
+ 'events' => array(
192
+ 'label' => __( 'Events', 'google-calendar-events' ),
193
+ 'target' => 'events-settings-panel',
194
+ 'class' => array( 'active' ),
195
+ 'icon' => 'simcal-icon-event',
196
+ ),
197
+ 'calendar' => array(
198
+ 'label' => __( 'Appearance', 'google-calendar-events' ),
199
+ 'target' => 'calendar-settings-panel',
200
+ 'class' => array(),
201
+ 'icon' => 'simcal-icon-calendar',
202
+ ),
203
+ ), $post->ID );
204
+
205
+ // Always keep advanced tab as the last one.
206
+ $tabs['advanced'] = array(
207
+ 'label' => __( 'Advanced', 'google-calendar-events' ),
208
+ 'target' => 'advanced-settings-panel',
209
+ 'class' => array(),
210
+ 'icon' => 'simcal-icon-settings',
211
+ );
212
+
213
+ // Output the tabs as list items.
214
+ foreach ( $tabs as $key => $tab ) {
215
+
216
+ if ( isset( $tab['target'] ) && isset( $tab['label'] ) ) {
217
+
218
+ $icon = $tab['icon'] ? $tab['icon'] : 'simcal-icon-panel';
219
+ $class = $tab['class'] ? $tab['class'] : array();
220
+
221
+ echo '<li class="' . $key . '-settings ' . $key . '-tab ' . implode( ' ', $class ) . '" data-tab="' . $key . '">';
222
+ echo '<a href="#' . $tab['target'] . '"><i class="' . $icon . '" ></i> <span>' . esc_html( $tab['label'] ) . '</span></a>';
223
+ echo '</li>';
224
+ }
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Print events settings panel.
230
+ *
231
+ * @since 3.0.0
232
+ * @access private
233
+ *
234
+ * @param \WP_Post $post
235
+ */
236
+ private static function events_settings_panel( $post ) {
237
+
238
+ ?>
239
+ <table>
240
+ <thead>
241
+ <tr><th colspan="2"><?php _e( 'Events settings', 'google-calendar-events' ); ?></th></tr>
242
+ </thead>
243
+ <tbody class="simcal-panel-section simcal-panel-section-events-range">
244
+ <tr class="simcal-panel-field">
245
+ <th><label for="_calendar_begins"><?php _e( 'Calendar start', 'google-calendar-events' ); ?></label></th>
246
+ <td>
247
+ <?php
248
+
249
+ $calendar_begins = esc_attr( get_post_meta( $post->ID, '_calendar_begins', true ) );
250
+ $calendar_begins_nth = max( absint( get_post_meta( $post->ID, '_calendar_begins_nth', true ) ), 1 );
251
+ $calendar_begins_nth_show = in_array( $calendar_begins, array(
252
+ 'days_before',
253
+ 'days_after',
254
+ 'weeks_before',
255
+ 'weeks_after',
256
+ 'months_before',
257
+ 'months_after',
258
+ 'years_before',
259
+ 'years_after',
260
+ ) );
261
+
262
+ simcal_print_field( array(
263
+ 'type' => 'standard',
264
+ 'subtype' => 'number',
265
+ 'name' => '_calendar_begins_nth',
266
+ 'id' => '_calendar_begins_nth',
267
+ 'value' => strval( $calendar_begins_nth ),
268
+ 'attributes' => array(
269
+ 'min' => '1',
270
+ ),
271
+ 'class' => array(
272
+ 'simcal-field-inline',
273
+ 'simcal-field-tiny',
274
+ ),
275
+ 'style' => ! $calendar_begins_nth_show ? array( 'display' => 'none' ) : '',
276
+ ) );
277
+
278
+ ?>
279
+ <select name="_calendar_begins"
280
+ id="_calendar_begins"
281
+ class="simcal-field simcal-field-select simcal-field-inline simcal-field-switch-other">
282
+ <optgroup label="<?php _e( 'Days range', 'google-calendar-events' ); ?>">
283
+ <option value="today"
284
+ data-hide-fields="_calendar_begins_custom_date,_calendar_begins_nth"
285
+ <?php selected( 'today', $calendar_begins, true ); ?>><?php _e( 'Today', 'google-calendar-events' ); ?></option>
286
+ <option value="now"
287
+ data-hide-fields="_calendar_begins_custom_date,_calendar_begins_nth"
288
+ <?php selected( 'now', $calendar_begins, true ); ?>><?php _e( 'Now', 'google-calendar-events' ); ?></option>
289
+ <option value="days_before"
290
+ data-hide-field="_calendar_begins_custom_date"
291
+ data-show-field="_calendar_begins_nth" <?php selected( 'days_before', $calendar_begins, true ); ?>><?php _e( 'Day(s) before today', 'google-calendar-events' ); ?></option>
292
+ <option value="days_after"
293
+ data-hide-field="_calendar_begins_custom_date"
294
+ data-show-field="_calendar_begins_nth" <?php selected( 'days_after', $calendar_begins, true ); ?>><?php _e( 'Day(s) after today', 'google-calendar-events' ); ?></option>
295
+ </optgroup>
296
+ <optgroup label="<?php _e( 'Weeks range', 'google-calendar-events' ); ?>">
297
+ <option value="this_week"
298
+ data-hide-fields="_calendar_begins_custom_date,_calendar_begins_nth"
299
+ <?php selected( 'this_week', $calendar_begins, true ); ?>><?php _e( 'This week', 'google-calendar-events' ); ?></option>
300
+ <option value="weeks_before"
301
+ data-hide-field="_calendar_begins_custom_date"
302
+ data-show-field="_calendar_begins_nth" <?php selected( 'weeks_before', $calendar_begins, true ); ?>><?php _e( 'Week(s) before current', 'google-calendar-events' ); ?></option>
303
+ <option value="weeks_after"
304
+ data-hide-field="_calendar_begins_custom_date"
305
+ data-show-field="_calendar_begins_nth" <?php selected( 'weeks_after', $calendar_begins, true ); ?>><?php _e( 'Week(s) after current', 'google-calendar-events' ); ?></option>
306
+ </optgroup>
307
+ <optgroup label="<?php _e( 'Months range', 'google-calendar-events' ); ?>">
308
+ <option value="this_month"
309
+ data-hide-fields="_calendar_begins_custom_date,_calendar_begins_nth"
310
+ <?php selected( 'this_month', $calendar_begins, true ); ?>><?php _e( 'This month', 'google-calendar-events' ); ?></option>
311
+ <option value="months_before"
312
+ data-hide-field="_calendar_begins_custom_date"
313
+ data-show-field="_calendar_begins_nth" <?php selected( 'months_before', $calendar_begins, true ); ?>><?php _e( 'Month(s) before current', 'google-calendar-events' ); ?></option>
314
+ <option value="months_after"
315
+ data-hide-field="_calendar_begins_custom_date"
316
+ data-show-field="_calendar_begins_nth" <?php selected( 'months_after', $calendar_begins, true ); ?>><?php _e( 'Month(s) after current', 'google-calendar-events' ); ?></option>
317
+ </optgroup>
318
+ <optgroup label="<?php _e( 'Years range', 'google-calendar-events' ); ?>">
319
+ <option value="this_year"
320
+ data-hide-fields="_calendar_begins_custom_date,_calendar_begins_nth"
321
+ <?php selected( 'this_year', $calendar_begins, true ); ?>><?php _e( 'This year', 'google-calendar-events' ); ?></option>
322
+ <option value="years_before"
323
+ data-show-field="_calendar_begins_nth" <?php selected( 'years_before', $calendar_begins, true ); ?>><?php _e( 'Year(s) before current', 'google-calendar-events' ); ?></option>
324
+ <option value="years_after"
325
+ data-hide-field="_calendar_begins_custom_date"
326
+ data-show-field="_calendar_begins_nth" <?php selected( 'years_after', $calendar_begins, true ); ?>><?php _e( 'Year(s) after current', 'google-calendar-events' ); ?></option>
327
+ </optgroup>
328
+ <optgroup label="<?php _e( 'Other', 'google-calendar-events' ); ?>">
329
+ <option value="custom_date"
330
+ data-hide-field="_calendar_begins_nth"
331
+ data-show-field="_calendar_begins_custom_date" <?php selected( 'custom_date', $calendar_begins, true ); ?>><?php _e( 'Specific date', 'google-calendar-events' ); ?></option>
332
+ </optgroup>
333
+ </select>
334
+ <?php
335
+
336
+ simcal_print_field( array(
337
+ 'type' => 'date-picker',
338
+ 'name' => '_calendar_begins_custom_date',
339
+ 'id' => '_calendar_begins_custom_date',
340
+ 'value' => get_post_meta( $post->ID, '_calendar_begins_custom_date', true ),
341
+ 'class' => array(
342
+ 'simcal-field-inline',
343
+ ),
344
+ 'style' => 'custom_date' != $calendar_begins ? array( 'display' => 'none' ) : '',
345
+ ) );
346
+
347
+ ?>
348
+ <i class="simcal-icon-help simcal-help-tip"
349
+ data-tip="<?php _e( 'The calendar default opening date. It will automatically adapt based on the chosen calendar type.', 'google-calendar-events' ); ?>"></i>
350
+ </td>
351
+ </tr>
352
+ <tr class="simcal-panel-field">
353
+ <th><label for="_feed_earliest_event_date"><?php _e( 'Earliest event', 'google-calendar-events' ); ?></label></th>
354
+ <td>
355
+ <?php
356
+
357
+ $earliest_event_saved = get_post_meta( $post->ID, '_feed_earliest_event_date', true );
358
+ $earliest_event = false == $earliest_event_saved ? 'months_before' : esc_attr( $earliest_event_saved );
359
+
360
+ simcal_print_field( array(
361
+ 'type' => 'standard',
362
+ 'subtype' => 'number',
363
+ 'name' => '_feed_earliest_event_date_range',
364
+ 'id' => '_feed_earliest_event_date_range',
365
+ 'value' => strval( max( absint( get_post_meta( $post->ID, '_feed_earliest_event_date_range', true ) ), 1 ) ),
366
+ 'attributes' => array(
367
+ 'min' => '1',
368
+ ),
369
+ 'class' => array(
370
+ 'simcal-field-inline',
371
+ 'simcal-field-tiny',
372
+ ),
373
+ 'style' => ( 'now' != $earliest_event ) && ( 'today' != $earliest_event ) ? array( 'display' => 'none' ) : '',
374
+ ) );
375
+
376
+ ?>
377
+ <select name="_feed_earliest_event_date"
378
+ id="_feed_earliest_event_date"
379
+ class="simcal-field simcal-field-select simcal-field-inline simcal-field-switch-other">
380
+ <option value="calendar_start" data-hide-field="_feed_earliest_event_date_range" <?php selected( 'calendar_start', $earliest_event, true ); ?>><?php _e( 'Same as start date', 'google-calendar-events' ); ?></option>
381
+ <option value="days_before" data-show-field="_feed_earliest_event_date_range" <?php selected( 'days_before', $earliest_event, true ); ?>><?php _e( 'Day(s) before start date', 'google-calendar-events' ); ?></option>
382
+ <option value="weeks_before" data-show-field="_feed_earliest_event_date_range" <?php selected( 'weeks_before', $earliest_event, true ); ?>><?php _e( 'Week(s) before start date', 'google-calendar-events' ); ?></option>
383
+ <option value="months_before" data-show-field="_feed_earliest_event_date_range" <?php selected( 'months_before', $earliest_event, true ); ?>><?php _e( 'Month(s) before start date', 'google-calendar-events' ); ?></option>
384
+ <option value="years_before" data-show-field="_feed_earliest_event_date_range" <?php selected( 'years_before', $earliest_event, true ); ?>><?php _e( 'Year(s) before start date', 'google-calendar-events' ); ?></option>
385
+ </select>
386
+ <i class="simcal-icon-help simcal-help-tip"
387
+ data-tip="<?php _e( 'Set the date for the earliest possible event to show in calendar. There will not be events before this date.', 'google-calendar-events' ); ?>"></i>
388
+ </td>
389
+ </tr>
390
+ <tr class="simcal-panel-field">
391
+ <th><label for="_feed_latest_event_date"><?php _e( 'Latest event', 'google-calendar-events' ); ?></label></th>
392
+ <td>
393
+ <?php
394
+
395
+ $latest_event_saved = get_post_meta( $post->ID, '_feed_latest_event_date', true );
396
+ $latest_event = false == $latest_event_saved ? 'years_after' : esc_attr( $latest_event_saved );
397
+
398
+ simcal_print_field( array(
399
+ 'type' => 'standard',
400
+ 'subtype' => 'number',
401
+ 'name' => '_feed_latest_event_date_range',
402
+ 'id' => '_feed_latest_event_date_range',
403
+ 'value' => strval( max( absint( get_post_meta( $post->ID, '_feed_latest_event_date_range', true ) ), 1 ) ),
404
+ 'attributes' => array(
405
+ 'min' => '1',
406
+ ),
407
+ 'class' => array(
408
+ 'simcal-field-inline',
409
+ 'simcal-field-tiny',
410
+ ),
411
+ 'style' => 'indefinite' != $latest_event ? array( 'display' => 'none' ) : '',
412
+ ) );
413
+
414
+ ?>
415
+ <select name="_feed_latest_event_date"
416
+ id="_feed_latest_event_date"
417
+ class="simcal-field simcal-field-select simcal-field-inline simcal-field-switch-other">
418
+ <option value="calendar_start" data-hide-field="_feed_latest_event_date_range" <?php selected( 'calendar_start', $earliest_event, true ); ?>><?php _e( 'Day end of start date', 'google-calendar-events' ); ?></option>
419
+ <option value="days_after" data-show-field="_feed_latest_event_date_range" <?php selected( 'days_after', $latest_event, true ); ?>><?php _e( 'Day(s) after start date', 'google-calendar-events' ); ?></option>
420
+ <option value="weeks_after" data-show-field="_feed_latest_event_date_range" <?php selected( 'weeks_after', $latest_event, true ); ?>><?php _e( 'Weeks(s) after start date', 'google-calendar-events' ); ?></option>
421
+ <option value="months_after" data-show-field="_feed_latest_event_date_range" <?php selected( 'months_after', $latest_event, true ); ?>><?php _e( 'Month(s) after start date', 'google-calendar-events' ); ?></option>
422
+ <option value="years_after" data-show-field="_feed_latest_event_date_range" <?php selected( 'years_after', $latest_event, true ); ?>><?php _e( 'Year(s) after start date', 'google-calendar-events' ); ?></option>
423
+ </select>
424
+ <i class="simcal-icon-help simcal-help-tip"
425
+ data-tip="<?php _e( 'Set the date for the latest possible event to show on calendar. There will not be events after this date.', 'google-calendar-events' ); ?>"></i>
426
+ </td>
427
+ </tr>
428
+ </tbody>
429
+ </table>
430
+ <?php
431
+
432
+ }
433
+
434
+ /**
435
+ * Print the calendar settings panel.
436
+ *
437
+ * @since 3.0.0
438
+ * @access private
439
+ *
440
+ * @param \WP_Post $post
441
+ */
442
+ private static function calendar_settings_panel( $post ) {
443
+
444
+ ?>
445
+ <table>
446
+ <thead>
447
+ <tr><th colspan="2"><?php _e( 'Miscellaneous', 'google-calendar-events' ); ?></th></tr>
448
+ </thead>
449
+ <tbody class="simcal-panel-section">
450
+ <tr class="simcal-panel-field">
451
+ <th><label for="_calendar_is_static"><?php _e( 'Static calendar', 'google-calendar-events' ); ?></label></th>
452
+ <td>
453
+ <?php
454
+
455
+ $fixed = get_post_meta( $post->ID, '_calendar_is_static', true );
456
+
457
+ simcal_print_field( array(
458
+ 'type' => 'checkbox',
459
+ 'name' => '_calendar_is_static',
460
+ 'id' => '_calendar_is_static',
461
+ 'tooltip' => __( "Remove the navigation arrows and fix the calendar view to it's initial state.", 'google-calendar-events' ),
462
+ 'value' => 'yes' == $fixed ? 'yes' : 'no',
463
+ ) );
464
+
465
+ ?>
466
+ </td>
467
+ </tr>
468
+ <tr class="simcal-panel-field">
469
+ <th><label for="_no_events_message"><?php _e( 'No events message', 'google-calendar-events' ); ?></label></th>
470
+ <td>
471
+ <?php
472
+
473
+ simcal_print_field( array(
474
+ 'type' => 'textarea',
475
+ 'name' => '_no_events_message',
476
+ 'id' => '_no_events_message',
477
+ 'tooltip' => __( 'Some calendars may display a message when no events are found. You can change the default message here.', 'google-calendar-events' ),
478
+ 'value' => get_post_meta( $post->ID, '_no_events_message', true ),
479
+ ) );
480
+
481
+ ?>
482
+ </td>
483
+ </tr>
484
+ </tbody>
485
+ </table>
486
+ <?php
487
+
488
+ }
489
+
490
+ /**
491
+ * Print the advanced settings panel.
492
+ *
493
+ * @since 3.0.0
494
+ * @access private
495
+ *
496
+ * @param \WP_Post $post
497
+ */
498
+ private static function advanced_settings_panel( $post ) {
499
+
500
+ ?>
501
+ <table>
502
+ <thead>
503
+ <tr><th colspan="2"><?php _e( 'Date and Time', 'google-calendar-events' ); ?></th></tr>
504
+ </thead>
505
+ <tbody class="simcal-panel-section simcal-panel-datetime-formatting">
506
+ <tr class="simcal-panel-field">
507
+ <th><label for="_calendar_timezone_setting"><?php _e( 'Timezone', 'google-calendar-events' ); ?></label></th>
508
+ <td>
509
+ <?php
510
+
511
+ $timezone_wordpress = simcal_get_wp_timezone();
512
+ $timezone_default = $timezone_wordpress ? $timezone_wordpress : 'UTC';
513
+ $timezone_setting = esc_attr( get_post_meta( $post->ID, '_feed_timezone_setting', true ) );
514
+ $timezone = esc_attr( get_post_meta( $post->ID, '_feed_timezone', true ) );
515
+ $timezone = $timezone ? $timezone : $timezone_default;
516
+
517
+ ?>
518
+ <select name="_feed_timezone_setting"
519
+ id="_feed_timezone_setting"
520
+ class="simcal-field simcal-field-select simcal-field-inline simcal-field-show-next"
521
+ data-show-next-if-value="use_custom">
522
+ <option value="use_calendar" <?php selected( 'use_calendar', $timezone_setting, true ); ?>><?php _ex( 'Events source default', 'Use the calendar default setting', 'google-calendar-events' ); ?></option>
523
+ <option value="use_site" <?php selected( 'use_site', $timezone_setting, true ); ?>><?php printf( _x( 'Site default', 'Use this site default setting', 'google-calendar-events' ) . ' (%s)', $timezone_default ); ?></option>
524
+ <option value="use_custom" <?php selected( 'use_custom', $timezone_setting, true ); ?>><?php _ex( 'Custom', 'Use a custom setting', 'google-calendar-events' ); ?></option>
525
+ </select>
526
+ <select name="_feed_timezone"
527
+ id="_feed_timezone"
528
+ class="simcal-field simcal-field-select simcal-field-inline"
529
+ <?php echo 'use_custom' != $timezone_setting ? 'style="display: none;"' : ''; ?>>
530
+ <?php echo wp_timezone_choice( $timezone ); ?>
531
+ </select>
532
+ <i class="simcal-icon-help simcal-help-tip" data-tip="<?php _e( 'Using a different timezone may alter the date and time display of your calendar events. It is recommended to keep the calendar default timezone.', 'google-calendar-events' ); ?>"></i>
533
+ </td>
534
+ </tr>
535
+ <tr class="simcal-panel-field">
536
+ <th><label for="_calendar_date_format_setting"><?php _e( 'Date format', 'google-calendar-events' ); ?></label></th>
537
+ <td>
538
+ <?php
539
+
540
+ $date_format_setting = esc_attr( get_post_meta( $post->ID, '_calendar_date_format_setting', true ) );
541
+ $date_format_default = esc_attr( get_option( 'date_format' ) );
542
+ $date_format = esc_attr( get_post_meta( $post->ID, '_calendar_date_format', true ) );
543
+ $date_format_php = esc_attr( get_post_meta( $post->ID, '_calendar_date_format_php', true ) );
544
+ $date_format_php = $date_format_php ? $date_format_php : $date_format_default;
545
+
546
+ ?>
547
+ <select name="_calendar_date_format_setting"
548
+ id="_calendar_date_format_setting"
549
+ class="simcal-field simcal-field-select simcal-field-show-other">
550
+ <option value="use_site" data-show-field="_calendar_date_format_default" <?php selected( 'use_site', $date_format_setting, true ); ?>><?php _ex( 'Site default', 'Use this site default setting', 'google-calendar-events' ); ?></option>
551
+ <option value="use_custom" data-show-field="_calendar_date_format" <?php selected( 'use_custom', $date_format_setting, true ); ?>><?php _ex( 'Custom', 'Use a custom setting', 'google-calendar-events' ); ?></option>
552
+ <option value="use_custom_php" data-show-field="_calendar_date_format_php_field" <?php selected( 'use_custom_php', $date_format_setting, true ); ?>><?php _e( 'Custom (PHP format)', 'google-calendar-events' ); ?></option>
553
+ </select>
554
+ <i class="simcal-icon-help simcal-help-tip" data-tip="<?php _e( 'This option sets how calendars display event dates. It is recommended to keep your site default setting.', 'google-calendar-events' ); ?>"></i>
555
+ <p id="_calendar_date_format_default" style="<?php echo $date_format_setting != 'use_site' ? 'display: none;' : ''; ?>">
556
+ <em><?php _e( 'Preview', 'google-calendar-events' ) ?>:</em>&nbsp;&nbsp;
557
+ <code><?php echo date_i18n( $date_format_default, time() ); ?></code>
558
+ </p>
559
+ <?php simcal_print_field( array(
560
+ 'type' => 'datetime-format',
561
+ 'subtype' => 'date',
562
+ 'name' => '_calendar_date_format',
563
+ 'id' => '_calendar_date_format',
564
+ 'value' => $date_format,
565
+ 'style' => $date_format_setting != 'use_custom' ? array( 'display' => 'none' ) : '',
566
+ ) ); ?>
567
+ <div class="simcal-field-datetime-format-php" id="_calendar_date_format_php_field" style="<?php echo $date_format_setting != 'use_custom_php' ? 'display: none;' : ''; ?>">
568
+ <br>
569
+ <label for="_calendar_date_format_php">
570
+ <input type="text"
571
+ name="_calendar_date_format_php"
572
+ id="_calendar_date_format_php"
573
+ class="simcal-field simcal-field-text simcal-field-small"
574
+ value="<?php echo $date_format_php; ?>" />
575
+ <?php printf( __( 'Enter a date format using %s values.', 'google-calendar-events' ), '<a href="//php.net/manual/en/function.date.php" target="_blank">PHP</a>' ); ?>
576
+ </label>
577
+ <p>
578
+ <em><?php _e( 'Preview', 'google-calendar-events' ) ?>:</em>&nbsp;&nbsp;
579
+ <code><?php echo date_i18n( $date_format_php, time() ); ?></code>
580
+ </p>
581
+ </div>
582
+ </td>
583
+ </tr>
584
+ <tr class="simcal-panel-field">
585
+ <th><label for="_calendar_datetime_separator"><?php _e( 'Separator', 'google-calendar-events' ); ?></label></th>
586
+ <td>
587
+ <?php
588
+
589
+ $separator = get_post_meta( $post->ID, '_calendar_datetime_separator', true );
590
+ $separator = false == $separator ? '@' : $separator;
591
+
592
+ simcal_print_field( array(
593
+ 'type' => 'standard',
594
+ 'subtype' => 'text',
595
+ 'name' => '_calendar_datetime_separator',
596
+ 'id' => '_calendar_datetime_separator',
597
+ 'value' => $separator,
598
+ 'tooltip' => __( 'Used to divide date and time when both are shown.', 'google-calendar-events' ),
599
+ 'class' => array(
600
+ 'simcal-field-tiny',
601
+ ),
602
+ ) );
603
+
604
+ ?>
605
+ </td>
606
+ </tr>
607
+ <tr class="simcal-panel-field">
608
+ <th><label for="_calendar_time_format_setting"><?php _e( 'Time format', 'google-calendar-events' ); ?></label></th>
609
+ <td>
610
+ <?php
611
+
612
+ $time_format_setting = esc_attr( get_post_meta( $post->ID, '_calendar_time_format_setting', true ) );
613
+ $time_format_default = esc_attr( get_option( 'time_format' ) );
614
+ $time_format = esc_attr( get_post_meta( $post->ID, '_calendar_time_format', true ) );
615
+ $time_format_php = esc_attr( get_post_meta( $post->ID, '_calendar_time_format_php', true ) );
616
+ $time_format_php = $time_format_php ? $time_format_php : $time_format_default;
617
+
618
+ ?>
619
+ <select name="_calendar_time_format_setting"
620
+ id="_calendar_time_format_setting"
621
+ class="simcal-field simcal-field-select simcal-field-show-other">
622
+ <option value="use_site" data-show-field="_calendar_time_format_default" <?php selected( 'use_site', $time_format_setting, true ); ?>><?php _ex( 'Site default', 'Use this site default setting', 'google-calendar-events' ); ?></option>
623
+ <option value="use_custom" data-show-field="_calendar_time_format" <?php selected( 'use_custom', $time_format_setting, true ); ?>><?php _ex( 'Custom', 'Use a custom setting', 'google-calendar-events' ); ?></option>
624
+ <option value="use_custom_php" data-show-field="_calendar_time_format_php_field" <?php selected( 'use_custom_php', $time_format_setting, true ); ?>><?php _e( 'Custom (PHP format)', 'google-calendar-events' ); ?></option>
625
+ </select>
626
+ <i class="simcal-icon-help simcal-help-tip" data-tip="<?php _e( 'This option sets how calendars display event times. It is recommended to keep your site default setting.', 'google-calendar-events' ); ?>"></i>
627
+ <p id="_calendar_time_format_default" style="<?php echo $time_format_setting != 'use_site' ? 'display: none;' : ''; ?>">
628
+ <em><?php _e( 'Preview', 'google-calendar-events' ) ?>:</em>&nbsp;&nbsp;
629
+ <code><?php echo date_i18n( $time_format_default, time() ); ?></code>
630
+ </p>
631
+ <?php simcal_print_field( array(
632
+ 'type' => 'datetime-format',
633
+ 'subtype' => 'time',
634
+ 'name' => '_calendar_time_format',
635
+ 'id' => '_calendar_time_format',
636
+ 'value' => $time_format,
637
+ 'style' => $time_format_setting != 'use_custom' ? array( 'display' => 'none' ) : '',
638
+ ) ); ?>
639
+ <div class="simcal-field-datetime-format-php" id="_calendar_time_format_php_field" style="<?php echo $time_format_setting != 'use_custom_php' ? 'display: none;' : ''; ?>">
640
+ <br>
641
+ <label for="_calendar_date_format_php">
642
+ <input type="text"
643
+ name="_calendar_time_format_php"
644
+ id="_calendar_time_format_php"
645
+ class="simcal-field simcal-field-text simcal-field-small"
646
+ value="<?php echo $time_format_php; ?>"/>
647
+ <?php printf( __( 'Enter a time format using %s values.', 'google-calendar-events' ), '<a href="//php.net/manual/en/function.date.php" target="_blank">PHP</a>' ); ?>
648
+ </label>
649
+ <p>
650
+ <em><?php _e( 'Preview', 'google-calendar-events' ) ?>:</em>&nbsp;&nbsp;
651
+ <code><?php echo date_i18n( $time_format_php, time() ); ?></code>
652
+ </p>
653
+ </div>
654
+ </td>
655
+ </tr>
656
+ <tr class="simcal-panel-field">
657
+ <th><label for="_calendar_week_starts_on_setting"><?php _e( 'Week starts on', 'google-calendar-events' ); ?></label></th>
658
+ <td>
659
+ <?php
660
+
661
+ $week_starts_setting = esc_attr( get_post_meta( $post->ID, '_calendar_week_starts_on_setting', true ) );
662
+ $week_starts_default = esc_attr( get_option( 'start_of_week' ) );
663
+ $week_starts = intval( get_post_meta( $post->ID, '_calendar_week_starts_on', true ) );
664
+ $week_starts = is_numeric( $week_starts ) ? strval( $week_starts ) : $week_starts_default;
665
+
666
+ ?>
667
+ <select
668
+ name="_calendar_week_starts_on_setting"
669
+ id="_calendar_week_starts_on_setting"
670
+ class="simcal-field simcal-field-select simcal-field-inline simcal-field-show-next"
671
+ data-show-next-if-value="use_custom">
672
+ <option value="use_site" <?php selected( 'use_site', $week_starts_setting, true ); ?>><?php printf( _x( 'Site default', 'Use this site default setting', 'google-calendar-events' ) . ' (%s)', date_i18n( 'l', strtotime( "Sunday + $week_starts_default Days" ) ) ); ?></option>
673
+ <option value="use_custom" <?php selected( 'use_custom', $week_starts_setting, true ); ?>><?php _ex( 'Custom', 'Use a custom setting', 'google-calendar-events' ); ?></option>
674
+ </select>
675
+ <select
676
+ name="_calendar_week_starts_on"
677
+ id="_calendar_week_starts_on"
678
+ class="simcal-field simcal-field-select simcal-field-inline"
679
+ <?php echo 'use_custom' != $week_starts_setting ? 'style="display: none;"' : ''; ?>>
680
+ <?php $day_names = simcal_get_calendar_names_i18n( 'day', 'full' ); ?>
681
+ <?php for ( $i = 0; $i <= 6; $i++ ) : ?>
682
+ <option value="<?php echo $i; ?>" <?php selected( $i, $week_starts, true ); ?>><?php echo $day_names[ $i ]; ?></option>
683
+ <?php endfor; ?>
684
+ </select>
685
+ <i class="simcal-icon-help simcal-help-tip" data-tip="<?php _e( 'Some calendars may use this setting to display the start of the week. It is recommended to keep the site default setting.', 'google-calendar-events' ); ?>"></i>
686
+ </td>
687
+ </tr>
688
+ </tbody>
689
+ </table>
690
+ <table>
691
+ <thead>
692
+ <tr><th colspan="2"><?php _e( 'Cache', 'google-calendar-events' ); ?></th></tr>
693
+ </thead>
694
+ <tbody class="simcal-panel-section simcal-panel-section-cache">
695
+ <?php
696
+
697
+ $cache_freq = esc_attr( get_post_meta( $post->ID, '_feed_cache_user_amount', true ) );
698
+ $cache_unit = esc_attr( get_post_meta( $post->ID, '_feed_cache_user_unit', true ) );
699
+ $cache_freq = $cache_freq ? $cache_freq : '2';
700
+ $cache_unit = $cache_unit ? $cache_unit : '3600';
701
+
702
+ ?>
703
+ <tr class="simcal-panel-field">
704
+ <th><label for="_feed_cache_user_amount"><?php _ex( 'Refresh interval', 'Cache maximum interval', 'google-calendar-events' ); ?></label></th>
705
+ <td>
706
+ <input type="number"
707
+ name="_feed_cache_user_amount"
708
+ id="_feed_cache_user_amount"
709
+ class="simcal-field simcal-field-number simcal-field-tiny simcal-field-inline"
710
+ value="<?php echo $cache_freq; ?>"
711
+ min="1" />
712
+ <select name="_feed_cache_user_unit"
713
+ id="_feed_cache_user_unit"
714
+ class="simcal-field simcalfield-select simcal-field-inline">
715
+ <option value="60" <?php selected( '60', $cache_unit, true ); ?>><?php _e( 'Minute(s)', 'google-calendar-events' ); ?></option>
716
+ <option value="3600" <?php selected( '3600', $cache_unit, true ); ?>><?php _e( 'Hour(s)', 'google-calendar-events' ); ?></option>
717
+ <option value="86400" <?php selected( '86400', $cache_unit, true ); ?>><?php _e( 'Day(s)', 'google-calendar-events' ); ?></option>
718
+ <option value="604800" <?php selected( '604800', $cache_unit, true ); ?>><?php _e( 'Week(s)', 'google-calendar-events' ); ?></option>
719
+ </select>
720
+ <i class="simcal-icon-help simcal-help-tip" data-tip="<?php _e( 'If you add, edit or remove events in your calendar very often, you can set a lower interval to refresh the events displayed. Set a higher interval for best performance.', 'google-calendar-events' ); ?>"></i>
721
+ </td>
722
+ </tr>
723
+ </tbody>
724
+ </table>
725
+ <?php
726
+
727
+ }
728
+
729
+ /**
730
+ * Print fields in a panel.
731
+ *
732
+ * @since 3.0.0
733
+ *
734
+ * @param array $array
735
+ * @param int $post_id
736
+ *
737
+ * @return void
738
+ */
739
+ public static function print_panel_fields( $array, $post_id ) {
740
+
741
+ foreach ( $array as $section => $fields ) :
742
+
743
+ if ( $fields && is_array( $fields ) ) :
744
+
745
+ ?>
746
+ <tbody class="simcal-panel-section simcal-panel-section-<?php echo esc_attr( $section ); ?>">
747
+ <?php foreach ( $fields as $key => $field ) :
748
+
749
+ $value = get_post_meta( $post_id, $key, true );
750
+ $field['value'] = $value ? $value : ( isset( $field['default'] ) ? $field['default'] : '' );
751
+ $the_field = simcal_get_field( $field ); ?>
752
+
753
+ <?php if ( $the_field instanceof Field ) : ?>
754
+ <tr class="simcal-panel-field">
755
+ <th><label for="<?php echo $the_field->id ?>"><?php echo $the_field->title; ?></label></th>
756
+ <td><?php $the_field->html(); ?></td>
757
+ </tr>
758
+ <?php endif; ?>
759
+
760
+ <?php endforeach; ?>
761
+ </tbody>
762
+ <?php
763
+
764
+ endif;
765
+
766
+ endforeach;
767
+
768
+ }
769
+
770
+ /**
771
+ * Validate and save the meta box fields.
772
+ *
773
+ * @since 3.0.0
774
+ *
775
+ * @param int $post_id
776
+ * @param \WP_Post $post
777
+ *
778
+ * @return void
779
+ */
780
+ public static function save( $post_id, $post ) {
781
+
782
+ /* ====================== *
783
+ * Calendar type and view *
784
+ * ====================== */
785
+
786
+ // Unlink existing terms for feed type and calendar type.
787
+ wp_delete_object_term_relationships( $post_id, array(
788
+ 'calendar_feed',
789
+ 'calendar_type',
790
+ ) );
791
+
792
+ // Set the feed type as term.
793
+ $feed_type = isset( $_POST['_feed_type'] ) ? sanitize_title( stripslashes( $_POST['_feed_type'] ) ) : apply_filters( 'simcal_default_feed_type', 'google' );
794
+ wp_set_object_terms( $post_id, $feed_type, 'calendar_feed' );
795
+
796
+ // Set the calendar type as a term.
797
+ $calendar_type = isset( $_POST['_calendar_type'] ) ? sanitize_title( stripslashes( $_POST['_calendar_type'] ) ) : apply_filters( 'simcal_default_calendar_type', 'default-calendar' );
798
+ wp_set_object_terms( $post_id, $calendar_type, 'calendar_type' );
799
+ // Set the calendar type view as post meta.
800
+ $calendar_view = isset( $_POST['_calendar_view'] ) ? $_POST['_calendar_view'] : '';
801
+ if ( $calendar_view && is_array( $calendar_view ) ) {
802
+ $views = array_map( 'sanitize_title', $calendar_view );
803
+ update_post_meta( $post_id, '_calendar_view', $views );
804
+ }
805
+
806
+ /* ===================== *
807
+ * Events settings panel *
808
+ * ===================== */
809
+
810
+ // Calendar opening.
811
+ $calendar_begins = isset( $_POST['_calendar_begins'] ) ? sanitize_key( $_POST['_calendar_begins'] ) : 'this_month';
812
+ update_post_meta( $post_id, '_calendar_begins', $calendar_begins );
813
+ $calendar_begins_nth = isset( $_POST['_calendar_begins_nth'] ) ? absint( $_POST['_calendar_begins_nth'] ) : 2;
814
+ update_post_meta( $post_id, '_calendar_begins_nth', $calendar_begins_nth );
815
+ $calendar_begins_custom_date = isset( $_POST['_calendar_begins_custom_date'] ) ? sanitize_title( $_POST['_calendar_begins_custom_date'] ) : '';
816
+ update_post_meta( $post_id, '_calendar_begins_custom_date', $calendar_begins_custom_date );
817
+
818
+ // Feed earliest events date.
819
+ $earliest_events = isset( $_POST['_feed_earliest_event_date'] ) ? sanitize_key( $_POST['_feed_earliest_event_date'] ) : '';
820
+ update_post_meta( $post_id, '_feed_earliest_event_date', $earliest_events );
821
+ $earliest_events_range = isset( $_POST['_feed_earliest_event_date_range'] ) ? max( absint( $_POST['_feed_earliest_event_date_range'] ), 1 ) : 1;
822
+ update_post_meta( $post_id, '_feed_earliest_event_date_range', $earliest_events_range );
823
+
824
+ // Feed latest events date.
825
+ $latest_events = isset( $_POST['_feed_latest_event_date'] ) ? sanitize_key( $_POST['_feed_latest_event_date'] ) : '';
826
+ update_post_meta( $post_id, '_feed_latest_event_date', $latest_events );
827
+ $latest_events_range = isset( $_POST['_feed_latest_event_date_range'] ) ? max( absint( $_POST['_feed_latest_event_date_range'] ), 1 ) : 1;
828
+ update_post_meta( $post_id, '_feed_latest_event_date_range', $latest_events_range );
829
+
830
+ /* ======================= *
831
+ * Calendar settings panel *
832
+ * ======================= */
833
+
834
+ // Static calendar.
835
+ $static = isset( $_POST['_calendar_is_static'] ) ? 'yes' : 'no';
836
+ update_post_meta( $post_id, '_calendar_is_static', $static );
837
+
838
+ // No events message.
839
+ $message = isset( $_POST['_no_events_message'] ) ? wp_kses_post( $_POST['_no_events_message'] ) : '';
840
+ update_post_meta( $post_id, '_no_events_message', $message );
841
+
842
+ /* ======================= *
843
+ * Advanced settings panel *
844
+ * ======================= */
845
+
846
+ // Timezone.
847
+ $feed_timezone_setting = isset( $_POST['_feed_timezone_setting'] ) ? sanitize_key( $_POST['_feed_timezone_setting'] ) : 'use_calendar';
848
+ update_post_meta( $post_id, '_feed_timezone_setting', $feed_timezone_setting );
849
+ $default_timezone = simcal_get_wp_timezone();
850
+ $feed_timezone = $default_timezone ? $default_timezone : 'UTC';
851
+ $feed_timezone = isset( $_POST['_feed_timezone'] ) ? sanitize_text_field( $_POST['_feed_timezone'] ) : $feed_timezone;
852
+ update_post_meta( $post_id, '_feed_timezone', $feed_timezone );
853
+
854
+ // Date format.
855
+ $date_format_setting = isset( $_POST['_calendar_date_format_setting'] ) ? sanitize_key( $_POST['_calendar_date_format_setting'] ) : 'use_site';
856
+ update_post_meta( $post_id, '_calendar_date_format_setting', $date_format_setting );
857
+ $date_format = isset( $_POST['_calendar_date_format'] ) ? sanitize_text_field( trim( $_POST['_calendar_date_format'] ) ) : get_option( 'date_format' );
858
+ update_post_meta( $post_id, '_calendar_date_format', $date_format );
859
+ $date_format_php = isset( $_POST['_calendar_date_format_php'] ) ? sanitize_text_field( trim( $_POST['_calendar_date_format_php'] ) ) : get_option( 'date_format' );
860
+ update_post_meta( $post_id, '_calendar_date_format_php', $date_format_php );
861
+
862
+ // Time format.
863
+ $time_format_setting = isset( $_POST['_calendar_time_format_setting'] ) ? sanitize_key( $_POST['_calendar_time_format_setting'] ) : 'use_site';
864
+ update_post_meta( $post_id, '_calendar_time_format_setting', $time_format_setting );
865
+ $time_format = isset( $_POST['_calendar_time_format'] ) ? sanitize_text_field( trim( $_POST['_calendar_time_format'] ) ) : get_option( 'time_format' );
866
+ update_post_meta( $post_id, '_calendar_time_format', $time_format );
867
+ $time_format_php = isset( $_POST['_calendar_time_format_php'] ) ? sanitize_text_field( trim( $_POST['_calendar_time_format_php'] ) ) : get_option( 'time_format' );
868
+ update_post_meta( $post_id, '_calendar_time_format_php', $time_format_php );
869
+
870
+ // Date-time separator.
871
+ $datetime_separator = isset( $_POST['_calendar_datetime_separator'] ) ? sanitize_text_field( $_POST['_calendar_datetime_separator'] ) : ' ';
872
+ update_post_meta( $post_id, '_calendar_datetime_separator', $datetime_separator );
873
+
874
+ // Week start.
875
+ $week_start_setting = isset( $_POST['_calendar_week_starts_on_setting'] ) ? sanitize_key( $_POST['_calendar_week_starts_on_setting'] ) : 'use_site';
876
+ update_post_meta( $post_id, '_calendar_week_starts_on_setting', $week_start_setting );
877
+ $week_start = isset( $_POST['_calendar_week_starts_on'] ) ? intval( $_POST['_calendar_week_starts_on'] ) : get_option( 'start_of_week' );
878
+ update_post_meta( $post_id, '_calendar_week_starts_on', $week_start );
879
+
880
+ // Cache interval.
881
+ $cache = 7200;
882
+ if ( isset( $_POST['_feed_cache_user_amount'] ) && isset( $_POST['_feed_cache_user_unit'] ) ) {
883
+ $amount = is_numeric( $_POST['_feed_cache_user_amount'] ) ? absint( $_POST['_feed_cache_user_amount'] ) : 1;
884
+ $unit = is_numeric( $_POST['_feed_cache_user_unit'] ) ? absint( $_POST['_feed_cache_user_unit'] ) : 3600;
885
+ update_post_meta( $post_id, '_feed_cache_user_amount', $amount );
886
+ update_post_meta( $post_id, '_feed_cache_user_unit', $unit );
887
+ $cache = $amount * $unit;
888
+ }
889
+ update_post_meta( $post_id, '_feed_cache', $cache );
890
+
891
+ /* ============= *
892
+ * Miscellaneous *
893
+ * ============= */
894
+
895
+ // Update version.
896
+ update_post_meta( $post_id, '_calendar_version', SIMPLE_CALENDAR_VERSION );
897
+
898
+ // Action hook.
899
+ do_action( 'simcal_process_settings_meta', $post_id );
900
+
901
+ // Clear cache.
902
+ simcal_delete_feed_transients( $post_id );
903
+ }
904
+
905
+ }
includes/admin/notice.php ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Notice
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Admin notice.
15
+ *
16
+ * An admin notice as an object.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Notice {
21
+
22
+ /**
23
+ * Notice id.
24
+ * Will be the notice key in saved notices option.
25
+ *
26
+ * @access public
27
+ * @var string|array
28
+ */
29
+ public $id = '';
30
+
31
+ /**
32
+ * Notice type
33
+ * Gives the notice a CSS class.
34
+ *
35
+ * @access public
36
+ * @var string notice|error|updated|update-nag
37
+ */
38
+ public $type = '';
39
+
40
+ /**
41
+ * Additional classes.
42
+ *
43
+ * @access public
44
+ * @var string
45
+ */
46
+ public $class = '';
47
+
48
+ /**
49
+ * To which users the notice should be shown.
50
+ * If not set, will be visible to all users.
51
+ *
52
+ * @access public
53
+ * @var string
54
+ */
55
+ public $capability = '';
56
+
57
+ /**
58
+ * In which screen the notice should appear.
59
+ * If not set, will appear in every dashboard page/screen.
60
+ *
61
+ * @access public
62
+ * @var array
63
+ */
64
+ public $screen = array();
65
+
66
+ /**
67
+ * For which posts edit screens the notice should appear.
68
+ * If not set, will fallback on $screen rule only.
69
+ *
70
+ * @access public
71
+ * @var array
72
+ */
73
+ public $post = array();
74
+
75
+ /**
76
+ * Can the notice be dismissed by the user?
77
+ * If false, you need to set up a dismissal event.
78
+ *
79
+ * @access public
80
+ * @var bool
81
+ */
82
+ public $dismissible = true;
83
+
84
+ /**
85
+ * Whether to hide notice while keeping it stored.
86
+ * If false, will keep the notice in option without showing it.
87
+ *
88
+ * @access public
89
+ * @var bool
90
+ */
91
+ public $visible = true;
92
+
93
+ /**
94
+ * The notice content.
95
+ * Supports html. You would normally wrap this in paragraph tags.
96
+ *
97
+ * @access public
98
+ * @var string
99
+ */
100
+ public $content = '';
101
+
102
+ /**
103
+ * Make a notice.
104
+ *
105
+ * @since 3.0.0
106
+ *
107
+ * @param array $notice
108
+ */
109
+ public function __construct( $notice ) {
110
+
111
+ if ( ! empty( $notice['id'] ) && ! empty( $notice['content'] ) ) {
112
+
113
+ // Content.
114
+ $this->id = is_array( $notice['id'] ) ? array_map( 'sanitize_key', $notice['id'] ) : sanitize_key( $notice['id'] );
115
+ $this->content = wp_kses_post( $notice['content'] );
116
+ if ( ! empty( $notice['class'] ) ) {
117
+ $this->class = is_array( $notice['class'] ) ? join( ' ', array_map( 'esc_attr', $notice['class'] ) ) : esc_attr( $notice['class'] );
118
+ }
119
+
120
+ // Type.
121
+ $default = 'notice';
122
+ $type = isset( $notice['type'] ) ? esc_attr( $notice['type'] ) : $default;
123
+ $types = array(
124
+ 'error',
125
+ 'notice',
126
+ 'updated',
127
+ 'update-nag',
128
+ );
129
+ $this->type = in_array( $type, $types ) ? $type : $default;
130
+
131
+ // Visibility.
132
+ if ( ! empty( $notice['capability'] ) ) {
133
+ $this->capability = esc_attr( $notice['capability'] );
134
+ }
135
+ if ( ! empty( $notice['screen'] ) ) {
136
+ $this->screen = is_array( $notice['screen'] ) ? array_map( 'esc_attr', $notice['screens'] ) : array( esc_attr( $notice['screen'] ) );
137
+ }
138
+ if ( ! empty( $notice['post'] ) ) {
139
+ $this->post = is_array( $notice['post'] ) ? array_map( 'intval', $notice['post'] ) : array( intval( $notice['post'] ) );
140
+ }
141
+ if ( ! empty( $notice['dismissible'] ) ) {
142
+ $this->dismissible = $notice['dismissible'] === false ? false: true;
143
+ }
144
+ if ( ! empty( $notice['visible'] ) ) {
145
+ $this->visible = $notice['visible'] === false ? false: true;
146
+ }
147
+ }
148
+
149
+ }
150
+
151
+ /**
152
+ * Add the notice.
153
+ *
154
+ * @since 3.0.0
155
+ */
156
+ public function add() {
157
+ if ( ! empty( $this->id ) && ! empty( $this->content ) ) {
158
+ $notices = get_option( 'simple-calendar_admin_notices', array() );
159
+ if ( is_array( $this->id ) ) {
160
+ foreach ( $this->id as $k => $v ) {
161
+ $notices[ $k ][ $v ] = $this;
162
+ }
163
+ } else {
164
+ $notices[ $this->id ][] = $this;
165
+ }
166
+ update_option( 'simple-calendar_admin_notices', $notices );
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Remove the notice.
172
+ *
173
+ * @since 3.0.0
174
+ */
175
+ public function remove() {
176
+ if ( ! empty( $this->id ) && ! empty( $this->content ) ) {
177
+ $notices = get_option( 'simple-calendar_admin_notices', array() );
178
+ if ( is_array( $this->id ) ) {
179
+ foreach ( $this->id as $k => $v ) {
180
+ unset( $notices[ $k ] );
181
+ }
182
+ } else {
183
+ unset( $notices[ $this->id ] );
184
+ }
185
+ update_option( 'simple-calendar_admin_notices', $notices );
186
+ }
187
+ }
188
+
189
+ }
includes/admin/notices.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin notices
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Admin notices class.
15
+ *
16
+ * Handles and displays notices in admin dashboard pages.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Notices {
21
+
22
+ /**
23
+ * Get notices.
24
+ *
25
+ * @since 3.0.0
26
+ */
27
+ public function __construct() {
28
+ add_action( 'admin_init', array( $this, 'remove_notice' ), 10 );
29
+ add_action( 'admin_init', array( $this, 'process_notices' ), 40 );
30
+ }
31
+
32
+ /**
33
+ * Process notices.
34
+ *
35
+ * @since 3.0.0
36
+ */
37
+ public function process_notices() {
38
+
39
+ $notices = $this->get_notices();
40
+
41
+ if ( ! empty( $notices ) && is_array( $notices ) ) {
42
+
43
+ foreach ( $notices as $group ) {
44
+ foreach ( $group as $notice ) {
45
+
46
+ if ( $notice instanceof Notice ) {
47
+
48
+ if ( $notice->visible === false ) {
49
+ continue;
50
+ }
51
+
52
+ if ( ! empty( $notice->capability ) ) {
53
+ if ( ! current_user_can( $notice->capability ) ) {
54
+ continue;
55
+ }
56
+ }
57
+
58
+ if ( ! empty( $notice->screen ) && is_array( $notice->screen ) && function_exists( 'get_current_screen' ) ) {
59
+ $screen = get_current_screen();
60
+ if ( isset( $screen->id ) ) {
61
+ if ( ! in_array( $screen->id, $notice->screen ) ) {
62
+ continue;
63
+ }
64
+ }
65
+ }
66
+
67
+ if ( ! empty( $notice->post ) && is_array( $notice->post ) ) {
68
+ if ( isset( $_GET['post'] ) ) {
69
+ if ( ! in_array( intval( $_GET['post'] ), $notice->post ) ) {
70
+ continue;
71
+ }
72
+ } else {
73
+ continue;
74
+ }
75
+ }
76
+
77
+ $this->add_notice( $notice );
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Add notice.
86
+ *
87
+ * @TODO Improve notice dismissal with ajax.
88
+ *
89
+ * @since 3.0.0
90
+ *
91
+ * @param Notice $notice
92
+ *
93
+ * @return void
94
+ */
95
+ public function add_notice( $notice ) {
96
+
97
+ if ( $notice instanceof Notice ) {
98
+
99
+ add_action( 'admin_notices', $print_notice = function() use ( $notice ) {
100
+
101
+ $name = is_array( $notice->id ) ? key( $notice->id ) : $notice->id;
102
+ $url = add_query_arg( array( 'dismiss_simcal_notice' => $name ) );
103
+ $dismiss_link = $notice->dismissible === true
104
+ ? sprintf( '<a class="dashicons-before dashicons-dismiss simcal-dismiss-notice" href="%1$s"></a>', $url )
105
+ : '';
106
+
107
+ echo '<div class="' . $notice->type . ' ' . $notice->class . ' simcal-admin-notice" data-notice-id="' . $name . '">' . $dismiss_link . $notice->content . '</div>';
108
+ } );
109
+
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Dismiss a notice.
115
+ *
116
+ * @TODO Improve notice dismissal with ajax.
117
+ *
118
+ * @since 3.0.0
119
+ *
120
+ * @param string $notice (optional) The notice id.
121
+ *
122
+ * @return void
123
+ */
124
+ public function remove_notice( $notice = '' ) {
125
+
126
+ $notices = $this->get_notices();
127
+ $update = false;
128
+
129
+ if ( ! empty( $notice ) ) {
130
+ if ( isset( $notices[ $notice ] ) ) {
131
+ unset( $notices[ $notice ] );
132
+ $update = true;
133
+ }
134
+ }
135
+
136
+ if ( isset( $_GET['dismiss_simcal_notice'] ) ) {
137
+ if ( isset( $notices[ $_GET['dismiss_simcal_notice'] ] ) ) {
138
+ unset( $notices[ esc_attr( $_GET['dismiss_simcal_notice'] ) ] );
139
+ $update = true;
140
+ }
141
+ }
142
+
143
+ if ( $update === true ) {
144
+ update_option( 'simple-calendar_admin_notices', $notices );
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Show a notice.
150
+ *
151
+ * @since 3.0.0
152
+ *
153
+ * @param string $notice
154
+ *
155
+ * @return void
156
+ */
157
+ public function show_notice( $notice ) {
158
+
159
+ $notices = $this->get_notices();
160
+
161
+ if ( isset( $notices[ $notice ]->visible ) ) {
162
+ $notices[ $notice ]->visible = true;
163
+ update_option( 'simple-calendar_admin_notices', $notices );
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Hide a notice.
169
+ *
170
+ * @since 3.0.0
171
+ *
172
+ * @param string $notice
173
+ *
174
+ * @return void
175
+ */
176
+ public function hide_notice( $notice ) {
177
+
178
+ $notices = $this->get_notices();
179
+
180
+ if ( isset( $notices[ $notice ]->visible ) ) {
181
+ $notices[ $notice ]->visible = false;
182
+ update_option( 'simple-calendar_admin_notices', $notices );
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Get current notices.
188
+ *
189
+ * @since 3.0.0
190
+ *
191
+ * @return array
192
+ */
193
+ public function get_notices() {
194
+ return apply_filters(
195
+ 'simcal_admin_notices',
196
+ get_option( 'simple-calendar_admin_notices', array() )
197
+ );
198
+ }
199
+
200
+ }
includes/admin/pages.php ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Settings Pages
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin;
8
+
9
+ use SimpleCalendar\Abstracts\Field;
10
+ use SimpleCalendar\Abstracts\Admin_Page;
11
+
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * Admin pages class.
18
+ *
19
+ * Handles settings pages and settings UI in admin dashboard.
20
+ *
21
+ * @since 3.0.0
22
+ */
23
+ class Pages {
24
+
25
+ /**
26
+ * Current settings page.
27
+ *
28
+ * @access private
29
+ * @var string
30
+ */
31
+ private $page = '';
32
+
33
+ /**
34
+ * Default tab.
35
+ *
36
+ * @access private
37
+ * @var string
38
+ */
39
+ private $tab = '';
40
+
41
+ /**
42
+ * Settings pages.
43
+ *
44
+ * @access private
45
+ * @var array
46
+ */
47
+ private $settings = array();
48
+
49
+ /**
50
+ * Constructor.
51
+ *
52
+ * @since 3.0.0
53
+ *
54
+ * @param string $page
55
+ */
56
+ public function __construct( $page = 'settings' ) {
57
+
58
+ $this->page = $page;
59
+ $settings_pages = ! is_null( \SimpleCalendar\plugin()->objects ) ? simcal_get_admin_pages() : '';
60
+ $settings_page_tabs = array();
61
+ $tabs = isset( $settings_pages[ $page ] ) ? $settings_pages[ $page ] : false;
62
+
63
+ if ( $tabs && is_array( $tabs ) ) {
64
+ foreach ( $tabs as $tab ) {
65
+
66
+ $settings_page = simcal_get_admin_page( $tab );
67
+
68
+ if ( $settings_page instanceof Admin_Page ) {
69
+ $settings_page_tabs[ $settings_page->id ] = $settings_page;
70
+ }
71
+ }
72
+
73
+ $this->settings = $settings_page_tabs;
74
+ }
75
+
76
+ // The first tab is the default tab when opening a page.
77
+ $this->tab = isset( $tabs[0] ) ? $tabs[0] : '';
78
+
79
+ do_action( 'simcal_admin_pages', $page );
80
+ }
81
+
82
+ /**
83
+ * Get settings pages.
84
+ *
85
+ * @since 3.0.0
86
+ * @access private
87
+ *
88
+ * @return array
89
+ */
90
+ public function get_settings() {
91
+
92
+ $settings = array();
93
+
94
+ if ( ! empty( $this->settings ) && is_array( $this->settings ) ) {
95
+ foreach ( $this->settings as $id => $object ) {
96
+
97
+ if ( $object instanceof Admin_Page ) {
98
+
99
+ $settings_page = $object->get_settings();
100
+
101
+ if ( isset( $settings_page[ $id ] ) ) {
102
+ $settings[ $id ] = $settings_page[ $id ];
103
+ }
104
+ }
105
+
106
+ }
107
+ }
108
+
109
+ return $settings;
110
+ }
111
+
112
+ /**
113
+ * Register settings.
114
+ *
115
+ * Adds settings sections and fields to settings pages.
116
+ *
117
+ * @since 3.0.0
118
+ *
119
+ * @param array $settings
120
+ */
121
+ public function register_settings( $settings = array() ) {
122
+
123
+ $settings = $settings ? $settings : $this->get_settings();
124
+
125
+ if ( ! empty( $settings ) && is_array( $settings ) ) {
126
+
127
+ foreach ( $settings as $tab_id => $settings_page ) {
128
+
129
+ if ( isset( $settings_page['sections'] ) ) {
130
+
131
+ $sections = $settings_page['sections'];
132
+
133
+ if ( ! empty( $sections ) && is_array( $sections ) ) {
134
+
135
+ foreach ( $sections as $section_id => $section ) {
136
+
137
+ add_settings_section(
138
+ $section_id,
139
+ isset( $section['title'] ) ? $section['title'] : '',
140
+ isset( $section['callback'] ) ? $section['callback'] : '',
141
+ 'simple-calendar_' . $this->page . '_' . $tab_id
142
+ );
143
+
144
+ if ( isset( $section['fields'] ) ) {
145
+
146
+ $fields = $section['fields'];
147
+
148
+ if ( ! empty( $fields ) && is_array( $fields ) ) {
149
+
150
+ foreach ( $fields as $field ) {
151
+
152
+ if ( isset( $field['id'] ) && isset( $field['type'] ) ) {
153
+
154
+ $field_object = simcal_get_field( $field, $field['type'] );
155
+
156
+ if ( $field_object instanceof Field ) {
157
+
158
+ add_settings_field(
159
+ $field['id'],
160
+ isset( $field['title'] ) ? $field['title'] : '',
161
+ array( $field_object, 'html' ),
162
+ 'simple-calendar_' . $this->page . '_' . $tab_id,
163
+ $section_id
164
+ );
165
+
166
+ } // add field
167
+
168
+ } // is field valid?
169
+
170
+ } // loop fields
171
+
172
+ } // are fields non empty?
173
+
174
+ } // are there fields?
175
+
176
+ $page = simcal_get_admin_page( $tab_id );
177
+
178
+ register_setting(
179
+ 'simple-calendar_' . $this->page . '_' . $tab_id,
180
+ 'simple-calendar_' . $this->page . '_' . $tab_id,
181
+ $page instanceof Admin_Page ? array( $page, 'validate' ) : ''
182
+ );
183
+
184
+ } // loop sections
185
+
186
+ } // are sections non empty?
187
+
188
+ } // are there sections?
189
+
190
+ } // loop settings
191
+
192
+ } // are there settings?
193
+
194
+ }
195
+
196
+ /**
197
+ * Print Settings Pages.
198
+ *
199
+ * @since 3.0.0
200
+ */
201
+ public function html() {
202
+
203
+ global $current_tab;
204
+
205
+ // Get current tab/section
206
+ $current_tab = empty( $_GET['tab'] ) ? $this->tab : sanitize_title( $_GET['tab'] );
207
+ $this->tab = $current_tab;
208
+
209
+ ?>
210
+ <div class="wrap" id="simcal-settings-page">
211
+ <form id="simcal-settings-page-form"
212
+ method="post"
213
+ action="options.php">
214
+ <?php
215
+
216
+ // Include settings pages
217
+ $settings_pages = self::get_settings();
218
+ if ( ! empty( $settings_pages ) && is_array( $settings_pages ) ) {
219
+
220
+ echo '<h2 class="nav-tab-wrapper simcal-nav-tab-wrapper">';
221
+
222
+ // Get tabs for the settings page
223
+ if ( ! empty( $settings_pages ) && is_array( $settings_pages ) ) {
224
+
225
+ foreach ( $settings_pages as $id => $settings ) {
226
+
227
+ $tab_id = isset( $id ) ? $id : '';
228
+ $tab_label = isset( $settings['label'] ) ? $settings['label'] : '';
229
+ $tab_link = admin_url( 'edit.php?post_type=calendar&page=simple-calendar_' . $this->page . '&tab=' . $tab_id );
230
+
231
+ echo '<a href="' . $tab_link . '" class="nav-tab ' . ( $current_tab == $tab_id ? 'nav-tab-active' : '' ) . '">' . $tab_label . '</a>';
232
+ }
233
+
234
+ }
235
+
236
+ do_action( 'simcal_admin_page_' . $this->page . '_tabs' );
237
+
238
+ echo '</h2>';
239
+
240
+ settings_errors();
241
+
242
+ foreach ( $settings_pages as $tab_id => $contents ) {
243
+
244
+ if ( $tab_id === $current_tab ) {
245
+
246
+ echo isset( $contents['description'] ) ? '<p>' . $contents['description'] . '</p>' : '';
247
+
248
+ do_action( 'simcal_admin_page_' . $this->page . '_' . $current_tab . '_start' );
249
+
250
+ settings_fields( 'simple-calendar_' . $this->page . '_' . $tab_id );
251
+ do_settings_sections( 'simple-calendar_' . $this->page . '_' . $tab_id );
252
+
253
+ do_action( 'simcal_admin_page_' . $this->page . '_' . $current_tab . '_end' );
254
+
255
+ $submit = apply_filters( 'simcal_admin_page_' . $this->page . '_' . $current_tab . '_submit', true );
256
+ if ( true === $submit ) {
257
+ submit_button();
258
+ }
259
+ }
260
+ }
261
+ }
262
+
263
+ ?>
264
+ </form>
265
+ </div>
266
+ <?php
267
+
268
+ }
269
+
270
+ }
includes/admin/pages/add-ons.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Add-ons Page
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Pages;
8
+
9
+ use SimpleCalendar\Abstracts\Admin_Page;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Add-ons page.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Add_Ons extends Admin_Page {
21
+
22
+ /**
23
+ * Constructor.
24
+ *
25
+ * @since 3.0.0
26
+ */
27
+ public function __construct() {
28
+
29
+ $this->id = $tab = 'add-ons';
30
+ $this->option_group = $page = 'add-ons';
31
+ $this->label = __( 'Add-ons', 'google-calendar-events' );
32
+ $this->description = '';
33
+ $this->sections = $this->add_sections();
34
+ $this->fields = $this->add_fields();
35
+
36
+ // Disable the submit button for this page.
37
+ add_filter( 'simcal_admin_page_' . $page . '_' . $tab . '_submit', function() { return false; } );
38
+
39
+ // Add html.
40
+ add_action( 'simcal_admin_page_' . $page . '_' . $tab . '_end', array( $this, 'html' ) );
41
+
42
+ }
43
+
44
+ /**
45
+ * Output page markup.
46
+ *
47
+ * @since 3.0.0
48
+ */
49
+ public function html() {
50
+
51
+ // @todo pull data from simplecalendar.io to showcase add-ons
52
+ $js_redirect = '<script type="text/javascript">';
53
+ $js_redirect .= 'window.location = "' . simcal_ga_campaign_url( simcal_get_url( 'add-ons' ), 'core-plugin', 'plugin-submenu-link', true ) . '"';
54
+ $js_redirect .= '</script>';
55
+
56
+ echo $js_redirect;
57
+ }
58
+
59
+ /**
60
+ * Add sections.
61
+ *
62
+ * @since 3.0.0
63
+ *
64
+ * @return array
65
+ */
66
+ public function add_sections() {
67
+ return array();
68
+ }
69
+
70
+ /**
71
+ * Add fields.
72
+ *
73
+ * @since 3.0.0
74
+ *
75
+ * @return array
76
+ */
77
+ public function add_fields() {
78
+ return array();
79
+ }
80
+
81
+ }
includes/admin/pages/advanced.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * General Settings Page
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Pages;
8
+
9
+ use SimpleCalendar\Abstracts\Admin_Page;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * General settings.
17
+ *
18
+ * Handles the plugin general settings and outputs the markup the settings page.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Advanced extends Admin_Page {
23
+
24
+ /**
25
+ * Constructor.
26
+ *
27
+ * @since 3.0.0
28
+ */
29
+ public function __construct() {
30
+ $this->id = 'advanced';
31
+ $this->option_group = 'settings';
32
+ $this->label = __( 'Advanced', 'google-calendar-events' );
33
+ //$this->description = __( 'Advanced settings.', 'google-calendar-events' );
34
+ $this->sections = $this->add_sections();
35
+ $this->fields = $this->add_fields();
36
+ }
37
+
38
+ /**
39
+ * Add sections.
40
+ *
41
+ * @since 3.0.0
42
+ *
43
+ * @return array
44
+ */
45
+ public function add_sections() {
46
+ return apply_filters( 'simcal_add_' . $this->option_group . '_' . $this->id .'_sections', array(
47
+ 'assets' => array(
48
+ 'title' => __( 'Scripts and Styles', 'google-calendar-events' ),
49
+ 'description' => __( 'Manage front end assets that handle the calendars appearance and user interface.', 'google-calendar-events' )
50
+ ),
51
+ 'installation' => array(
52
+ 'title' => __( 'Installation', 'google-calendar-events' ),
53
+ 'description' => __( 'Manage your data (plugin settings and saved calendars).', 'google-calendar-events' )
54
+ )
55
+ ) );
56
+ }
57
+
58
+ /**
59
+ * Add fields.
60
+ *
61
+ * @since 3.0.0
62
+ *
63
+ * @return array
64
+ */
65
+ public function add_fields() {
66
+
67
+ $fields = array();
68
+ $this->values = get_option( 'simple-calendar_' . $this->option_group . '_' . $this->id );
69
+
70
+ foreach ( $this->sections as $section => $a ) :
71
+
72
+ if ( 'assets' == $section ) {
73
+
74
+ $fields[ $section ] = array(
75
+ 'always_enqueue' => array(
76
+ 'title' => __( 'Always Enqueue', 'google-calendar-events' ),
77
+ 'tooltip' => __( 'If ticked, this option will load all scripts on every page load.', 'google-calendar-events' ),
78
+ 'type' => 'checkbox',
79
+ 'name' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '[' . $section . '][always_enqueue]',
80
+ 'id' => 'simple-calendar-' . $this->option_group . '-' . $this->id . '-' . $section . '-always-enqueue',
81
+ 'value' => $this->get_option_value( $section, 'always_enqueue' )
82
+ ),
83
+ 'disable_css' => array(
84
+ 'title' => __( 'Disable Styles', 'google-calendar-events' ),
85
+ 'tooltip' => __( 'If ticked, this option will prevent front end stylesheet to load.', 'google-calendar-events' ),
86
+ 'type' => 'checkbox',
87
+ 'name' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '[' . $section . '][disable_css]',
88
+ 'id' => 'simple-calendar-' . $this->option_group . '-' . $this->id . '-' . $section . '-disable-css',
89
+ 'value' => $this->get_option_value( $section, 'disable_css' )
90
+ ),
91
+ 'disable_js' => array(
92
+ 'title' => __( 'Disable Scripts', 'google-calendar-events' ),
93
+ 'tooltip' => __( 'If ticked, this option will prevent front end JavaScript to load.', 'google-calendar-events' ),
94
+ 'type' => 'checkbox',
95
+ 'name' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '[' . $section . '][disable_js]',
96
+ 'id' => 'simple-calendar-' . $this->option_group . '-' . $this->id . '-' . $section . '-disable-js',
97
+ 'value' => $this->get_option_value( $section, 'disable_js' )
98
+ )
99
+ );
100
+
101
+ } elseif ( 'installation' == $section ) {
102
+
103
+ $fields[ $section ] = array(
104
+ 'delete_settings' => array(
105
+ 'title' => __( 'Delete settings', 'google-calendar-events' ),
106
+ 'tooltip' => __( 'Tick this option if you want to wipe this plugin settings from database when uninstalling.', 'google-calendar-events' ),
107
+ 'type' => 'checkbox',
108
+ 'name' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '[' . $section . '][delete_settings]',
109
+ 'id' => 'simple-calendar-' . $this->option_group . '-' . $this->id . '-' . $section . '-delete-settings',
110
+ 'value' => $this->get_option_value( $section, 'delete_settings' ),
111
+ ),
112
+ 'erase_data' => array(
113
+ 'title' => __( 'Erase calendar data', 'google-calendar-events' ),
114
+ 'tooltip' => __( 'By default your data will be retained in database even after uninstall. Tick this option if you want to delete all your calendar data when uninstalling.', 'google-calendar-events' ),
115
+ 'type' => 'checkbox',
116
+ 'name' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '[' . $section . '][erase_data]',
117
+ 'id' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '-delete-data',
118
+ 'value' => $this->get_option_value( $section, 'erase_data' ),
119
+ )
120
+ );
121
+
122
+ }
123
+
124
+ endforeach;
125
+
126
+ return apply_filters( 'simcal_add_' . $this->option_group . '_' . $this->id . '_fields', $fields );
127
+ }
128
+
129
+ }
includes/admin/pages/calendars.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Calendar Settings Page
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Pages;
8
+
9
+ use SimpleCalendar\Abstracts\Calendar;
10
+ use SimpleCalendar\Abstracts\Admin_Page;
11
+
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * Calendar settings.
18
+ *
19
+ * Handles settings for specific calendar types and outputs the markup the settings page. a settings page.
20
+ *
21
+ * @since 3.0.0
22
+ */
23
+ class Calendars extends Admin_Page {
24
+
25
+ /**
26
+ * Calendar Types.
27
+ *
28
+ * @access private
29
+ * @var array
30
+ */
31
+ private $calendar_types = array();
32
+
33
+ /**
34
+ * Constructor.
35
+ *
36
+ * @since 3.0.0
37
+ */
38
+ public function __construct() {
39
+
40
+ $this->id = 'calendars';
41
+ $this->option_group = 'settings';
42
+ $this->label = __( 'Calendars', 'google-calendar-events' );
43
+ //$this->description = __( 'Manage calendar preferences and calendar types settings and options.', 'google-calendar-events' );
44
+
45
+ $calendars = simcal_get_calendar_types();
46
+ $calendar_settings = array();
47
+ if ( ! empty( $calendars ) && is_array( $calendars ) ) {
48
+ foreach ( $calendars as $calendar => $views ) {
49
+
50
+ $calendar_type = simcal_get_calendar( $calendar );
51
+
52
+ if ( $calendar_type instanceof Calendar ) {
53
+ $settings = $calendar_type->settings_fields();
54
+ if ( ! empty( $settings ) ) {
55
+ $calendar_settings[ $calendar ] = $settings;
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ $this->calendar_types = $calendar_settings;
62
+ $this->sections = $this->add_sections();
63
+ $this->fields = $this->add_fields();
64
+ }
65
+
66
+ /**
67
+ * Add sections.
68
+ *
69
+ * @since 3.0.0
70
+ *
71
+ * @return array
72
+ */
73
+ public function add_sections() {
74
+
75
+ $sections = array(
76
+ 'general' => array(
77
+ 'title' => __( 'General', 'google-calendar-events' ),
78
+ 'description' => '',
79
+ ),
80
+ );
81
+
82
+ $calendar_types = $this->calendar_types;
83
+
84
+ if ( ! empty( $calendar_types ) && is_array( $calendar_types ) ) {
85
+ foreach ( $calendar_types as $calendar_type => $type ) {
86
+
87
+ $sections[ $calendar_type ] = array(
88
+ 'title' => $type['name'],
89
+ 'description' => $type['description'],
90
+ );
91
+
92
+ }
93
+ }
94
+
95
+ arsort( $calendar_types );
96
+
97
+ $sections['poweredby'] = array(
98
+ 'title' => __( 'Show Some Love', 'google-calendar-events' ),
99
+ 'description' => '',
100
+ );
101
+
102
+ return apply_filters( 'simcal_add_' . $this->option_group . '_' . $this->id .'_sections', $sections );
103
+ }
104
+
105
+ /**
106
+ * Add fields.
107
+ *
108
+ * @since 3.0.0
109
+ *
110
+ * @return array
111
+ */
112
+ public function add_fields() {
113
+
114
+ $fields = array();
115
+ $feed_types = $this->calendar_types;
116
+ $this->values = get_option( 'simple-calendar_' . $this->option_group . '_' . $this->id );
117
+
118
+ foreach ( $this->sections as $section => $contents ) :
119
+
120
+ if ( 'general' == $section ) {
121
+
122
+ $options = array();
123
+ $post_types = get_post_types(
124
+ array(
125
+ 'public' => false,
126
+ 'publicly_queriable' => false,
127
+ 'show_ui' => false,
128
+ ),
129
+ 'objects',
130
+ 'not'
131
+ );
132
+ unset( $post_types['attachment'] );
133
+ unset( $post_types['calendar'] );
134
+ unset( $post_types['gce_feed'] );
135
+ foreach ( $post_types as $slug => $post_type ) {
136
+ $options[ $slug ] = $post_type->label;
137
+ }
138
+ asort( $options );
139
+
140
+ $fields[ $section ][] = array(
141
+ 'type' => 'select',
142
+ 'multiselect' => 'multiselect',
143
+ 'enhanced' => 'enhanced',
144
+ 'title' => __( 'Attach Calendars', 'google-calendar-events' ),
145
+ 'tooltip' => __( 'You can choose on which content types to add the ability to attach calendars.', 'google-calendar-events' ),
146
+ 'name' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '[' . $section . '][attach_calendars_posts]',
147
+ 'id' => 'simple-calendar-' . $this->option_group . '-' . $this->id . '-attach-calendars-posts',
148
+ 'value' => $this->get_option_value( $section, 'attach_calendars_posts' ),
149
+ 'default' => 'post,page',
150
+ 'options' => $options,
151
+ );
152
+
153
+ } elseif ( isset( $feed_types[ $section ]['fields'] ) ) {
154
+
155
+ foreach ( $feed_types[ $section ]['fields'] as $key => $args ) {
156
+
157
+ $fields[ $section ][] = array_merge( $args, array(
158
+ 'name' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '[' . $section . '][' . $key . ']',
159
+ 'id' => 'simple-calendar-' . $this->option_group . '-' . $this->id . '-' . $key,
160
+ 'value' => $this->get_option_value( $section, $key )
161
+ ) );
162
+
163
+ }
164
+
165
+ } elseif ( 'poweredby' == $section ) {
166
+
167
+ $fields[ $section ][] = array(
168
+ 'type' => 'checkbox',
169
+ 'title' => __( 'Powered by Simple Calendar', 'google-calendar-events' ),
170
+ 'tooltip' => __( 'Help our plugin get noticed and display a small link to our plugin below your calendars. Thanks!', 'google-calendar-events' ),
171
+ 'name' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '[' . $section . '][opt_in]',
172
+ 'id' => 'simple-calendar-' . $this->option_group . '-' . $this->id . '-poweredby-optin',
173
+ 'value' => $this->get_option_value( $section, 'opt_in' ),
174
+ );
175
+ }
176
+
177
+ endforeach;
178
+
179
+ return apply_filters( 'simcal_add_' . $this->option_group . '_' . $this->id . '_fields', $fields );
180
+ }
181
+
182
+ }
includes/admin/pages/feeds.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Feeds Settings Page
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Pages;
8
+
9
+ use SimpleCalendar\Abstracts\Feed;
10
+ use SimpleCalendar\Abstracts\Admin_Page;
11
+
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * Feeds settings.
18
+ *
19
+ * Handles calendar feeds settings and outputs the settings page markup.
20
+ *
21
+ * @since 3.0.0
22
+ */
23
+ class Feeds extends Admin_Page {
24
+
25
+ /**
26
+ * Feed types.
27
+ *
28
+ * @access private
29
+ * @var array
30
+ */
31
+ private $feed_types = array();
32
+
33
+ /**
34
+ * Constructor.
35
+ *
36
+ * @since 3.0.0
37
+ */
38
+ public function __construct() {
39
+
40
+ $this->id = 'feeds';
41
+ $this->option_group = 'settings';
42
+ $this->label = __( 'Event Sources', 'google-calendar-events' );
43
+ //$this->description = __( 'Manage calendar event sources settings.', 'google-calendar-events' );
44
+
45
+ $feeds_settings = array();
46
+ $feeds = simcal_get_feed_types();
47
+ if ( ! empty( $feeds ) && is_array( $feeds ) ) {
48
+ foreach ( $feeds as $feed ) {
49
+
50
+ $feed_type = simcal_get_feed( $feed );
51
+
52
+ if ( $feed_type instanceof Feed ) {
53
+ $settings = $feed_type->settings_fields();
54
+ if ( ! empty( $settings ) ) {
55
+ $feeds_settings[ $feed ] = $settings;
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ $this->feed_types = $feeds_settings;
62
+ $this->sections = $this->add_sections();
63
+ $this->fields = $this->add_fields();
64
+ }
65
+
66
+ /**
67
+ * Add sections.
68
+ *
69
+ * @since 3.0.0
70
+ *
71
+ * @return array
72
+ */
73
+ public function add_sections() {
74
+
75
+ $sections = array();
76
+
77
+ foreach ( $this->feed_types as $feed_type => $type ) {
78
+
79
+ $sections[ $feed_type ] = array(
80
+ 'title' => $type['name'],
81
+ 'description' => $type['description'],
82
+ );
83
+
84
+ }
85
+
86
+ arsort( $sections );
87
+
88
+ return apply_filters( 'simcal_add_' . $this->option_group . '_' . $this->id .'_sections', $sections );
89
+ }
90
+
91
+ /**
92
+ * Add fields.
93
+ *
94
+ * @since 3.0.0
95
+ *
96
+ * @return array
97
+ */
98
+ public function add_fields() {
99
+
100
+ $fields = array();
101
+ $feed_types = $this->feed_types;
102
+ $this->values = get_option( 'simple-calendar_' . $this->option_group . '_' . $this->id );
103
+
104
+ foreach ( $this->sections as $type => $contents ) :
105
+
106
+ if ( isset( $feed_types[ $type ]['fields'] ) ) {
107
+ foreach ( $feed_types[ $type ]['fields'] as $key => $args ) {
108
+
109
+ $fields[ $type ][] = array_merge( $args, array(
110
+ 'name' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '[' . $type . '][' . $key . ']',
111
+ 'id' => 'simple-calendar-' . $this->option_group . '-' . $this->id . '-' . $type . '-' . $key,
112
+ 'value' => $this->get_option_value( $type, $key )
113
+ ) );
114
+
115
+ }
116
+ }
117
+
118
+ endforeach;
119
+
120
+ return apply_filters( 'simcal_add_' . $this->option_group . '_' . $this->id . '_fields', $fields );
121
+ }
122
+
123
+ }
includes/admin/pages/licenses.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Licenses management page
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Pages;
8
+
9
+ use SimpleCalendar\Abstracts\Admin_Page;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Licenses.
17
+ *
18
+ * Handles the plugin add-ons licenses if at least one is installed and active.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Licenses extends Admin_Page {
23
+
24
+ /**
25
+ * Constructor.
26
+ *
27
+ * @since 3.0.0
28
+ */
29
+ public function __construct() {
30
+
31
+ $this->id = $tab = 'licenses';
32
+ $this->option_group = $page = 'settings';
33
+ $this->label = __( 'Add-on Licenses', 'google-calendar-events' );
34
+ //$this->description = __( 'Manage your premium add-on license keys.', 'google-calendar-events' );
35
+ $this->sections = $this->add_sections();
36
+ $this->fields = $this->add_fields();
37
+
38
+ // Disabled the 'save changes' button for this page.
39
+ add_filter( 'simcal_admin_page_' . $page . '_' . $tab . '_submit', function() { return false; } );
40
+
41
+ // Add html to page.
42
+ add_action( 'simcal_admin_page_' . $page . '_' . $tab . '_end', array( __CLASS__, 'html' ) );
43
+ }
44
+
45
+ /**
46
+ * Add additional html.
47
+ *
48
+ * @since 3.0.0
49
+ *
50
+ * @return void
51
+ */
52
+ public static function html() {
53
+ // Add a nonce field used in ajax.
54
+ wp_nonce_field( 'simcal_license_manager', 'simcal_license_manager' );
55
+ // Add a license 'reset' button.
56
+ ?>
57
+ <br><br>
58
+ <a href="#" id="simcal-reset-licenses" data-dialog="<?php _e( 'WARNING: Are you sure you want to start over and delete all license keys from the settings?', 'google-calendar-events' ) ?>">
59
+ <?php _e( 'Delete your license keys', 'google-calendar-events' ) ?>
60
+ <i class="simcal-icon-spinner simcal-icon-spin" style="display: none;"></i>
61
+ </a>
62
+ <?php
63
+
64
+ }
65
+
66
+ /**
67
+ * Add sections.
68
+ *
69
+ * @since 3.0.0
70
+ *
71
+ * @return array
72
+ */
73
+ public function add_sections() {
74
+ $sections = array(
75
+ 'keys' => array(
76
+ 'title' => __( 'Premium Add-on License Keys', 'google-calendar-events' ),
77
+ 'description' => __( 'Enter your add-on license keys below, making sure to activate each one to ensure they are valid.', 'google-calendar-events' ) .
78
+ '<br/><br/>' .
79
+ '<em>' . __( 'Your license keys are used for access to automatic upgrades and premium support.', 'google-calendar-events' ) . '</em>',
80
+ ),
81
+ );
82
+ return apply_filters( 'simcal_add_' . $this->option_group . '_' . $this->id .'_sections', $sections );
83
+ }
84
+
85
+ /**
86
+ * Add fields.
87
+ *
88
+ * @since 3.0.0
89
+ *
90
+ * @return array
91
+ */
92
+ public function add_fields() {
93
+
94
+ $fields = array();
95
+ $this->values = get_option( 'simple-calendar_' . $this->option_group . '_' . $this->id );
96
+
97
+ foreach ( $this->sections as $section => $contents ) {
98
+
99
+ if ( 'keys' == $section ) {
100
+
101
+ $addons = apply_filters( 'simcal_installed_addons', array() );
102
+
103
+ if ( ! empty( $addons ) && is_array( $addons ) ) {
104
+
105
+ foreach ( $addons as $addon_id => $addon_name ) {
106
+
107
+ $fields[ $section ][ $addon_id ] = array(
108
+ 'type' => 'license',
109
+ 'addon' => $addon_id,
110
+ 'title' => esc_attr( $addon_name ),
111
+ 'name' => 'simple-calendar_' . $this->option_group . '_' . $this->id . '[' . $section . '][' . $addon_id . ']',
112
+ 'id' => 'simple-calendar-' . $this->option_group . '-' . $this->id . '-' . $section . '-' . sanitize_key( $addon_id ),
113
+ 'value' => $this->get_option_value( $section, $addon_id ),
114
+ 'class' => array(
115
+ 'regular-text',
116
+ 'ltr',
117
+ )
118
+ );
119
+
120
+ }
121
+
122
+ }
123
+
124
+ }
125
+
126
+ }
127
+
128
+ return apply_filters( 'simcal_add_' . $this->option_group . '_' . $this->id . '_fields', $fields );
129
+ }
130
+
131
+ }
includes/admin/pages/system-status.php ADDED
@@ -0,0 +1,709 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * System Status Page
4
+ *
5
+ * @package SimpleCalendar/Admin
6
+ */
7
+ namespace SimpleCalendar\Admin\Pages;
8
+
9
+ use SimpleCalendar\Abstracts\Admin_Page;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * System status.
17
+ *
18
+ * Generates reports on current installation, with information for debugging or support purposes.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class System_Status extends Admin_Page {
23
+
24
+ /**
25
+ * Constructor.
26
+ *
27
+ * @since 3.0.0
28
+ */
29
+ public function __construct() {
30
+
31
+ $this->id = $tab = 'system-status';
32
+ $this->option_group = $page = 'tools';
33
+ $this->label = __( 'System Report', 'google-calendar-events' );
34
+ $this->description = '';
35
+ $this->sections = $this->add_sections();
36
+ $this->fields = $this->add_fields();
37
+
38
+ // Disable the submit button for this page.
39
+ add_filter( 'simcal_admin_page_' . $page . '_' . $tab . '_submit', function() { return false; } );
40
+
41
+ // Add html.
42
+ add_action( 'simcal_admin_page_' . $page . '_' . $tab . '_end', array( $this, 'html' ) );
43
+ }
44
+
45
+ /**
46
+ * Output page markup.
47
+ *
48
+ * @since 3.0.0
49
+ */
50
+ public function html() {
51
+
52
+ ?>
53
+ <div id="simcal-system-status-report">
54
+ <p><?php _e( 'Please copy and paste this information when contacting support:', 'google-calendar-events' ); ?> </p>
55
+ <textarea readonly="readonly" onclick="this.select();"></textarea>
56
+ <p><?php _e( 'You can also download your information as a text file to attach, or simply view it below.', 'google-calendar-events' ); ?></p>
57
+ <p><a href="#" id="simcal-system-status-report-download" class="button button-primary"><?php _e( 'Download System Report', 'google-calendar-events' ); ?></a></p>
58
+ </div>
59
+ <hr>
60
+ <?php
61
+
62
+ global $wpdb;
63
+ $wp_version = get_bloginfo( 'version' );
64
+
65
+ $sections = array();
66
+ $panels = array(
67
+ 'wordpress' => array(
68
+ 'label' => __( 'WordPress Installation', 'google-calendar-events' ),
69
+ 'export' => 'WordPress Installation',
70
+ ),
71
+ 'theme' => array(
72
+ 'label' => __( 'Active Theme', 'google-calendar-events' ),
73
+ 'export' => 'Active Theme',
74
+ ),
75
+ 'plugins' => array(
76
+ 'label' => __( 'Active Plugins', 'google-calendar-events' ),
77
+ 'export' => 'Active Plugins',
78
+ ),
79
+ 'server' => array(
80
+ 'label' => __( 'Server Environment', 'google-calendar-events' ),
81
+ 'export' => 'Server Environment',
82
+ ),
83
+ 'client' => array(
84
+ 'label' => __( 'Client Information', 'google-calendar-events' ),
85
+ 'export' => 'Client Information',
86
+ )
87
+ );
88
+
89
+ /**
90
+ * Plugin Information
91
+ * ==================
92
+ */
93
+
94
+ // @todo add report information section for current plugin
95
+
96
+ /**
97
+ * WordPress Installation
98
+ * ======================
99
+ */
100
+
101
+ $debug_mode = $script_debug = __( 'No', 'google-calendar-events' );
102
+ if ( defined( 'WP_DEBUG' ) ) {
103
+ $debug_mode = true === WP_DEBUG ? __( 'Yes', 'google-calendar-events' ) : $debug_mode;
104
+ }
105
+ if ( defined( 'SCRIPT_DEBUG' ) ) {
106
+ $script_debug = true === SCRIPT_DEBUG ? __( 'Yes', 'google-calendar-events' ) : $script_debug;
107
+ }
108
+
109
+ $memory = $this->let_to_num( WP_MEMORY_LIMIT );
110
+ $memory_export = size_format( $memory );
111
+ if ( $memory < 67108864 ) {
112
+ $memory = '<mark class="error">' . sprintf( __( '%1$s - It is recomendend to set memory to at least 64MB. See: <a href="%2$s" target="_blank">Increasing memory allocated to PHP</a>', 'google-calendar-events' ), $memory_export, 'http://codex.wordpress.org/Editing_wp-config.php#Increasing_memory_allocated_to_PHP' ) . '</mark>';
113
+ } else {
114
+ $memory = '<mark class="ok">' . $memory_export . '</mark>';
115
+ }
116
+
117
+ $permalinks = get_option( 'permalink_structure' );
118
+ $permalinks = empty( $permalinks ) ? '/?' : $permalinks;
119
+
120
+ $is_multisite = is_multisite();
121
+
122
+ $sections['wordpress'] = array(
123
+ 'name' => array(
124
+ 'label' => __( 'Site Name', 'google-calendar-events' ),
125
+ 'label_export' => 'Site Name',
126
+ 'result' => get_bloginfo( 'name' ),
127
+ ),
128
+ 'home_url' => array(
129
+ 'label' => __( 'Home URL', 'google-calendar-events' ),
130
+ 'label_export' => 'Home URL',
131
+ 'result' => home_url(),
132
+ ),
133
+ 'site_url' => array(
134
+ 'label' => __( 'Site URL', 'google-calendar-events' ),
135
+ 'label_export' => 'Site URL',
136
+ 'result' => site_url(),
137
+ ),
138
+ 'version' => array(
139
+ 'label' => __( 'Version', 'google-calendar-events' ),
140
+ 'label_export' => 'Version',
141
+ 'result' => $wp_version,
142
+ ),
143
+ 'locale' => array(
144
+ 'label' => __( 'Locale', 'google-calendar-events' ),
145
+ 'label_export' => 'Locale',
146
+ 'result' => get_locale(),
147
+ ),
148
+ 'wp_timezone' => array(
149
+ 'label' => __( 'Timezone', 'google-calendar-events' ),
150
+ 'label_export' => 'Timezone',
151
+ 'result' => simcal_get_wp_timezone(),
152
+ ),
153
+ 'multisite' => array(
154
+ 'label' => __( 'Multisite', 'google-calendar-events' ),
155
+ 'label_export' => 'Multisite',
156
+ 'result' => $is_multisite ? __( 'Yes', 'google-calendar-events' ) : __( 'No', 'google-calendar-events' ),
157
+ 'result_export' => $is_multisite ? 'Yes' : 'No'
158
+ ),
159
+ 'permalink' => array(
160
+ 'label' => __( 'Permalinks', 'google-calendar-events' ),
161
+ 'label_export' => 'Permalinks',
162
+ 'result' => '<code>' . $permalinks . '</code>',
163
+ 'result_export' => $permalinks,
164
+ ),
165
+ 'memory_limit' => array(
166
+ 'label' => 'WP Memory Limit',
167
+ 'result' => $memory,
168
+ 'result_export' => $memory_export,
169
+ ),
170
+ 'debug_mode' => array(
171
+ 'label' => 'WP Debug Mode',
172
+ 'result' => $debug_mode,
173
+ ),
174
+ 'script_debug' => array(
175
+ 'label' => 'Script Debug',
176
+ 'result' => $script_debug,
177
+ ),
178
+ );
179
+
180
+ /**
181
+ * Active Theme
182
+ * ============
183
+ */
184
+
185
+ include_once ABSPATH . 'wp-admin/includes/theme-install.php';
186
+
187
+ if ( version_compare( $wp_version, '3.4', '<' ) ) {
188
+ $active_theme = get_theme_data( get_stylesheet_directory() . '/style.css' );
189
+ $theme_name = '<a href="' . $active_theme['URI'] . '" target="_blank">' . $active_theme['Name'] . '</a>';
190
+ $theme_version = $active_theme['Version'];
191
+ $theme_author = '<a href="' . $active_theme['AuthorURI'] . '" target="_blank">' . $active_theme['Author'] . '</a>';
192
+ $theme_export = $active_theme['Name'] . ' - ' . $theme_version;
193
+ } else {
194
+ $active_theme = wp_get_theme();
195
+ $theme_name = '<a href="' . $active_theme->ThemeURI . '" target="_blank">' . $active_theme->Name . '</a>';
196
+ $theme_version = $active_theme->Version;
197
+ $theme_author = $active_theme->Author;
198
+ $theme_export = $active_theme->Name . ' - ' . $theme_version;
199
+ }
200
+
201
+ $theme_update_version = $theme_version;
202
+
203
+ $api = themes_api( 'theme_information', array(
204
+ 'slug' => get_template(),
205
+ 'fields' => array( 'sections' => false, 'tags' => false ),
206
+ ) );
207
+ if ( $api && ! is_wp_error( $api ) ) {
208
+ $theme_update_version = $api->version;
209
+ }
210
+
211
+ if ( version_compare( $theme_version, $theme_update_version, '<' ) ) {
212
+ $theme_version = '<mark class="error">' . $theme_version . ' (' . sprintf( __( '%s is available', 'google-calendar-events' ), esc_html( $theme_update_version ) ) . ')</mark>';
213
+ } else {
214
+ $theme_version = '<mark class="ok">' . $theme_version . '</mark>';
215
+ }
216
+
217
+ $theme = '<dl>';
218
+ $theme .= '<dt>' . __( 'Name', 'google-calendar-events' ) . '</dt>';
219
+ $theme .= '<dd>' . $theme_name . '</dd>';
220
+ $theme .= '<dt>' . __( 'Author', 'google-calendar-events' ) . '</dt>';
221
+ $theme .= '<dd>' . $theme_author . '</dd>';
222
+ $theme .= '<dt>' . __( 'Version', 'google-calendar-events' ) . '</dt>';
223
+ $theme .= '<dd>' . $theme_version . '</dd>';
224
+ $theme .= '</dl>';
225
+
226
+ $is_child_theme = is_child_theme();
227
+ $parent_theme = $parent_theme_export = '-';
228
+
229
+ if ( $is_child_theme ) {
230
+ if ( version_compare( $wp_version, '3.4', '<' ) ) {
231
+
232
+ $parent_theme = $parent_theme_export = $active_theme['Template'];
233
+
234
+ } else {
235
+
236
+ $parent = wp_get_theme( $active_theme->Template );
237
+ $parent_theme = '<dl>';
238
+ $parent_theme .= '<dt>' . __( 'Name', 'google-calendar-events' ) . '</dt>';
239
+ $parent_theme .= '<dd>' . $parent->Name . '</dd>';
240
+ $parent_theme .= '<dt>' . __( 'Author', 'google-calendar-events' ) . '</dt>';
241
+ $parent_theme .= '<dd>' . $parent->Author . '</dd>';
242
+ $parent_theme .= '<dt>' . __( 'Version', 'google-calendar-events' ) . '</dt>';
243
+ $parent_theme .= '<dd>' . $parent->Version . '</dd>';
244
+ $parent_theme .= '</dl>';
245
+
246
+ $parent_theme_export = strip_tags( $parent->Name ) . ' - ' . $parent->Version;
247
+ }
248
+ }
249
+
250
+ $sections['theme'] = array(
251
+ 'theme' => array(
252
+ 'label' => __( 'Theme Information', 'google-calendar-events' ),
253
+ 'label_export' => 'Theme',
254
+ 'result' => $theme,
255
+ 'result_export' => $theme_export,
256
+ ),
257
+ 'theme_child' => array(
258
+ 'label' => __( 'Child Theme', 'google-calendar-events' ),
259
+ 'label_export' => 'Child Theme',
260
+ 'result' => $is_child_theme ? __( 'Yes', 'google-calendar-events' ) : __( 'No', 'google-calendar-events' ),
261
+ 'result_export' => $is_child_theme ? 'Yes' : 'No',
262
+ ),
263
+ 'theme_parent' => array(
264
+ 'label' => __( 'Parent Theme', 'google-calendar-events' ),
265
+ 'label_export' => 'Parent Theme',
266
+ 'result' => $parent_theme,
267
+ 'result_export' => $parent_theme_export,
268
+ ),
269
+ );
270
+
271
+ /**
272
+ * Active Plugins
273
+ * ==============
274
+ */
275
+
276
+ include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
277
+
278
+ $active_plugins = (array) get_option( 'active_plugins', array() );
279
+ if ( is_multisite() ) {
280
+ $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
281
+ }
282
+
283
+ foreach ( $active_plugins as $plugin ) {
284
+
285
+ $plugin_data = @get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
286
+
287
+ if ( ! empty( $plugin_data['Name'] ) ) {
288
+
289
+ $plugin_name = $plugin_data['Title'];
290
+ $plugin_author = $plugin_data['Author'];
291
+ $plugin_version = $plugin_update_version = $plugin_data['Version'];
292
+
293
+ // Afraid that querying many plugins may risk a timeout.
294
+ if ( count( $active_plugins ) <= 10 ) {
295
+ $api = plugins_api( 'plugin_information', array(
296
+ 'slug' => $plugin_data['Name'],
297
+ 'fields' => array(
298
+ 'version' => true,
299
+ ),
300
+ ) );
301
+ if ( $api && ! is_wp_error( $api ) ) {
302
+ if ( ! empty( $api->version ) ) {
303
+ $plugin_update_version = $api->version;
304
+ if ( version_compare( $plugin_version, $plugin_update_version, '<' ) ) {
305
+ $plugin_version = '<mark class="error">' . $plugin_version . ' (' . sprintf( __( '%s is available', 'google-calendar-events' ), esc_html( $plugin_update_version ) ) . ')</mark>';
306
+ } else {
307
+ $plugin_version = '<mark class="ok">' . $plugin_version . '</mark>';
308
+ }
309
+ }
310
+ }
311
+ }
312
+
313
+ $plugin = '<dl>';
314
+ $plugin .= '<dt>' . __( 'Author', 'google-calendar-events' ) . '</dt>';
315
+ $plugin .= '<dd>' . $plugin_author . '</dd>';
316
+ $plugin .= '<dt>' . __( 'Version', 'google-calendar-events' ) . '</dt>';
317
+ $plugin .= '<dd>' . $plugin_version . '</dd>';
318
+ $plugin .= '</dl>';
319
+
320
+ $sections['plugins'][ sanitize_key( strip_tags( $plugin_name ) ) ] = array(
321
+ 'label' => $plugin_name,
322
+ 'label_export' => strip_tags( $plugin_data['Title'] ),
323
+ 'result' => $plugin,
324
+ 'result_export' => $plugin_data['Version'],
325
+ );
326
+ }
327
+ }
328
+
329
+ if ( isset( $sections['plugins'] ) ) {
330
+ rsort( $sections['plugins'] );
331
+ }
332
+
333
+ /**
334
+ * Server Environment
335
+ * ==================
336
+ */
337
+
338
+ if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
339
+ $php = '<mark class="error">' . sprintf( __( '%1$s - It is recomendend to upgrade at least to PHP version 5.4 for security reasons. <a href="%2$s" target="_blank">Read more.</a>', 'google-calendar-events' ), PHP_VERSION, 'http://www.wpupdatephp.com/update/' ) . '</mark>';
340
+ } else {
341
+ $php = '<mark class="ok">' . PHP_VERSION . '</mark>';
342
+ }
343
+
344
+ if ( $wpdb->use_mysqli ) {
345
+ $mysql = @mysqli_get_server_info( $wpdb->dbh );
346
+ } else {
347
+ $mysql = @mysql_get_server_info();
348
+ }
349
+
350
+ $host = $_SERVER['SERVER_SOFTWARE'];
351
+ if ( defined( 'WPE_APIKEY' ) ) {
352
+ $host .= ' (WP Engine)';
353
+ } elseif ( defined( 'PAGELYBIN' ) ) {
354
+ $host .= ' (Pagely)';
355
+ }
356
+
357
+ $default_timezone = $server_timezone_export = date_default_timezone_get();
358
+ if ( 'UTC' !== $default_timezone ) {
359
+ $server_timezone = '<mark class="error">' . sprintf( __( 'Server default timezone is %s - it should be UTC', 'google-calendar-events' ), $default_timezone ) . '</mark>';
360
+ } else {
361
+ $server_timezone = '<mark class="ok">UTC</mark>';
362
+ }
363
+
364
+ // WP Remote POST test.
365
+ $response = wp_safe_remote_post( 'https://www.paypal.com/cgi-bin/webscr', array(
366
+ 'timeout' => 60,
367
+ 'body' => array(
368
+ 'cmd' => '_notify-validate',
369
+ ),
370
+ ) );
371
+ if ( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) {
372
+ $wp_post_export = 'Yes';
373
+ $wp_post = '<mark class="ok">' . __( 'Yes', 'google-calendar-events' ) . '</mark>';
374
+ } else {
375
+ $wp_post_export = 'No';
376
+ $wp_post = '<mark class="error">' . __( 'No', 'google-calendar-events' );
377
+ if ( is_wp_error( $response ) ) {
378
+ $error = ' (' . $response->get_error_message() . ')';
379
+ $wp_post .= $error;
380
+ $wp_post_export .= $error;
381
+ } else {
382
+ $error = ' (' . $response['response']['code'] . ')';
383
+ $wp_post .= $error;
384
+ $wp_post_export .= $error;
385
+ }
386
+ $wp_post .= '</mark>';
387
+ }
388
+
389
+ // WP Remote GET test.
390
+ $response = wp_safe_remote_get( get_home_url( '/?p=1' ) );
391
+ if ( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) {
392
+ $wp_get_export = 'Yes';
393
+ $wp_get = '<mark class="ok">' . __( 'Yes', 'google-calendar-events' ) . '</mark>';
394
+ } else {
395
+ $wp_get_export = 'No';
396
+ $wp_get = '<mark class="error">' . __( 'No', 'google-calendar-events' );
397
+ if ( is_wp_error( $response ) ) {
398
+ $error = ' (' . $response->get_error_message() . ')';
399
+ $wp_get .= $error;
400
+ $wp_get_export .= $error;
401
+ } else {
402
+ $error = ' (' . $response['response']['code'] . ')';
403
+ $wp_get .= $error;
404
+ $wp_get_export .= $error;
405
+ }
406
+ $wp_get .= '</mark>';
407
+ }
408
+
409
+ $php_memory_limit = ini_get( 'memory_limit' );
410
+ $php_max_upload_filesize = ini_get( 'upload_max_filesize' );
411
+ $php_post_max_size = ini_get( 'post_max_size' );
412
+ $php_max_execution_time = ini_get( 'max_execution_time' );
413
+ $php_max_input_vars = ini_get( 'max_input_vars' );
414
+
415
+ $sections['server'] = array(
416
+ 'host' => array(
417
+ 'label' => __( 'Web Server', 'google-calendar-events' ),
418
+ 'label_export' => 'Web Server',
419
+ 'result' => $host,
420
+ ),
421
+ 'php_version' => array(
422
+ 'label' => __( 'PHP Version', 'google-calendar-events' ),
423
+ 'label_export' => 'PHP Version',
424
+ 'result' => $php,
425
+ 'result_export' => PHP_VERSION,
426
+ ),
427
+ 'mysql_version' => array(
428
+ 'label' => __( 'MySQL Version', 'google-calendar-events' ),
429
+ 'label_export' => 'MySQL Version',
430
+ 'result' => version_compare( $mysql, '5.5', '>' ) ? '<mark class="ok">' . $mysql . '</mark>' : $mysql,
431
+ 'result_export' => $mysql,
432
+ ),
433
+ 'server_timezone' => array(
434
+ 'label' => __( 'Server Timezone', 'google-calendar-events' ),
435
+ 'label_export' => 'Server Timezone',
436
+ 'result' => $server_timezone,
437
+ 'result_export' => $server_timezone_export,
438
+ ),
439
+ 'display_errors' => array(
440
+ 'label' => 'Display Errors',
441
+ 'result' => ( ini_get( 'display_errors' ) ) ? __( 'Yes', 'google-calendar-events' ) . ' (' . ini_get( 'display_errors' ) . ')' : '-',
442
+ 'result_export' => ( ini_get( 'display_errors' ) ) ? 'Yes' : 'No',
443
+ ),
444
+ 'php_safe_mode' => array(
445
+ 'label' => 'Safe Mode',
446
+ 'result' => ( ini_get( 'safe_mode' ) ) ? __( 'Yes', 'google-calendar-events' ) : __( 'No', 'google-calendar-events' ),
447
+ 'result_export' => ( ini_get( 'safe_mode' ) ) ? 'Yes' : 'No',
448
+ ),
449
+ 'php_memory_limit' => array(
450
+ 'label' => 'Memory Limit',
451
+ 'result' => $php_memory_limit ? $php_memory_limit : '-',
452
+ ),
453
+ 'upload_max_filesize' => array(
454
+ 'label' => 'Upload Max Filesize',
455
+ 'result' => $php_max_upload_filesize ? $php_max_upload_filesize : '-',
456
+ ),
457
+ 'post_max_size' => array(
458
+ 'label' => 'Post Max Size',
459
+ 'result' => $php_post_max_size ? $php_post_max_size : '-',
460
+ ),
461
+ 'max_execution_time' => array(
462
+ 'label' => 'Max Execution Time',
463
+ 'result' => $php_max_execution_time ? $php_max_execution_time : '-',
464
+ ),
465
+ 'max_input_vars' => array(
466
+ 'label' => 'Max Input Vars',
467
+ 'result' => $php_max_input_vars ? $php_max_input_vars : '-',
468
+ ),
469
+ 'fsockopen' => array(
470
+ 'label' => 'fsockopen',
471
+ 'result' => function_exists( 'fsockopen' ) ? __( 'Yes', 'google-calendar-events' ) : __( 'No', 'google-calendar-events' ),
472
+ 'result_export' => function_exists( 'fsockopen' ) ? 'Yes' : 'No',
473
+ ),
474
+ 'curl_init' => array(
475
+ 'label' => 'cURL',
476
+ 'result' => function_exists( 'curl_init' ) ? __( 'Yes', 'google-calendar-events' ) : __( 'No', 'google-calendar-events' ),
477
+ 'result_export' => function_exists( 'curl_init' ) ? 'Yes' : 'No',
478
+ ),
479
+ 'soap' => array(
480
+ 'label' => 'SOAP',
481
+ 'result' => class_exists( 'SoapClient' ) ? __( 'Yes', 'google-calendar-events' ) : __( 'No', 'google-calendar-events' ),
482
+ 'result_export' => class_exists( 'SoapClient' ) ? 'Yes' : 'No',
483
+ ),
484
+ 'suhosin' => array(
485
+ 'label' => 'SUHOSIN',
486
+ 'result' => extension_loaded( 'suhosin' ) ? __( 'Yes', 'google-calendar-events' ) : __( 'No', 'google-calendar-events' ),
487
+ 'result_export' => extension_loaded( 'suhosin' ) ? 'Yes' : 'No',
488
+ ),
489
+ 'wp_remote_post' => array(
490
+ 'label' => 'WP Remote POST',
491
+ 'result' => $wp_post,
492
+ 'result_export' => $wp_post_export,
493
+ ),
494
+ 'wp_remote_get' => array(
495
+ 'label' => 'WP Remote GET',
496
+ 'result' => $wp_get,
497
+ 'result_export' => $wp_get_export,
498
+ ),
499
+ );
500
+
501
+ /**
502
+ * Client Information
503
+ * ==================
504
+ */
505
+
506
+ $user_client = new \Browser();
507
+
508
+ $browser = '<dl>';
509
+ $browser .= '<dt>' . __( 'Name:', 'google-calendar-events' ) . '</dt>';
510
+ $browser .= '<dd>' . $user_client->getBrowser() . '</dd>';
511
+ $browser .= '<dt>' . __( 'Version:', 'google-calendar-events' ) . '</dt>';
512
+ $browser .= '<dd>' . $user_client->getVersion() . '</dd>';
513
+ $browser .= '<dt>' . __( 'User Agent:', 'google-calendar-events' ) . '</dt>';
514
+ $browser .= '<dd>' . $user_client->getUserAgent() . '</dd>';
515
+ $browser .= '<dt>' . __( 'Platform:', 'google-calendar-events' ) . '</dt>';
516
+ $browser .= '<dd>' . $user_client->getPlatform() . '</dd>';
517
+ $browser .= '</dl>';
518
+
519
+ $browser_export = $user_client->getBrowser() . ' ' . $user_client->getVersion() . ' (' . $user_client->getPlatform() . ')';
520
+
521
+ $sections['client'] = array(
522
+ 'user_ip' => array(
523
+ 'label' => __( 'IP Address', 'google-calendar-events' ),
524
+ 'label_export' => 'IP Address',
525
+ 'result' => $_SERVER['SERVER_ADDR'],
526
+ ),
527
+ 'browser' => array(
528
+ 'label' => __( 'Browser', 'google-calendar-events' ),
529
+ 'result' => $browser,
530
+ 'result_export' => $browser_export,
531
+ )
532
+ );
533
+
534
+ /**
535
+ * Final Output
536
+ * ============
537
+ */
538
+
539
+ $panels = apply_filters( 'simcal_system_status_report_panels', $panels );
540
+ $sections = apply_filters( 'simcal_system_status_report_sections', $sections );
541
+
542
+ foreach ( $panels as $panel => $v ) :
543
+
544
+ if ( isset( $sections[ $panel ] ) ) :
545
+
546
+ ?>
547
+ <table class="widefat simcal-system-status-report-panel">
548
+ <thead class="<?php echo $panel; ?>">
549
+ <tr>
550
+ <th colspan="3" data-export="<?php echo $v['export']; ?>"><?php echo $v['label']; ?></th>
551
+ </tr>
552
+ </thead>
553
+ <tbody>
554
+ <?php foreach ( $sections[ $panel ] as $row => $cell ) : ?>
555
+ <tr>
556
+ <?php
557
+ $label_export = isset( $cell['label_export'] ) ? $cell['label_export'] : $cell['label'];
558
+ $result_export = isset( $cell['result_export'] ) ? $cell['result_export'] : $cell['result'];
559
+ ?>
560
+ <td class="tooltip"><?php echo isset( $cell['tooltip'] ) ? ' <i class="simcal-icon-help simcal-help-tip" data-tip="' . $cell['tooltip'] . '"></i> ' : ''; ?></td>
561
+ <td class="label" data-export="<?php echo trim( $label_export ); ?>"><?php echo $cell['label']; ?></td>
562
+ <td class="result" data-export="<?php echo trim( $result_export ); ?>"><?php echo $cell['result']; ?></td>
563
+ </tr>
564
+ <?php endforeach; ?>
565
+ </tbody>
566
+ </table>
567
+ <?php
568
+
569
+ endif;
570
+
571
+ endforeach;
572
+
573
+ $this->inline_scripts();
574
+
575
+ }
576
+
577
+ /**
578
+ * Print inline scripts.
579
+ *
580
+ * @since 3.0.0
581
+ * @access private
582
+ *
583
+ * @return void
584
+ */
585
+ private function inline_scripts() {
586
+
587
+ ?>
588
+ <script type="text/javascript">
589
+
590
+ var report = '';
591
+
592
+ jQuery( '.simcal-system-status-report-panel thead, .simcal-system-status-report-panel tbody' ).each( function() {
593
+
594
+ if ( jQuery( this ).is( 'thead' ) ) {
595
+
596
+ var label = jQuery( this ).find( 'th' ).data( 'export' );
597
+ report = report + '\n### ' + jQuery.trim( label ) + ' ###\n\n';
598
+
599
+ } else {
600
+
601
+ jQuery( 'tr', jQuery( this ) ).each( function() {
602
+
603
+ var label = jQuery( this ).find( 'td:eq(1)' ).data( 'export' );
604
+ var the_name = jQuery.trim( label ).replace( /(<([^>]+)>)/ig, '' ); // Remove HTML
605
+ var image = jQuery( this ).find( 'td:eq(2)' ).find( 'img' ); // Get WP 4.2 emojis
606
+ var prefix = ( undefined === image.attr( 'alt' ) ) ? '' : image.attr( 'alt' ) + ' '; // Remove WP 4.2 emojis
607
+ var the_value = jQuery.trim( prefix + jQuery( this ).find( 'td:eq(2)' ).data( 'export' ) );
608
+ var value_array = the_value.split( ', ' );
609
+ if ( value_array.length > 1 ) {
610
+ var temp_line ='';
611
+ jQuery.each( value_array, function( key, line ) {
612
+ temp_line = temp_line + line + '\n';
613
+ });
614
+ the_value = temp_line;
615
+ }
616
+
617
+ report = report + '' + the_name.trim() + ': ' + the_value.trim() + '\n';
618
+ });
619
+
620
+ }
621
+
622
+ });
623
+
624
+ try {
625
+ jQuery( '#simcal-system-status-report textarea' ).val( report ).focus().select();
626
+ } catch( e ){
627
+ console.log( e );
628
+ }
629
+
630
+ function downloadReport( text, name, type ) {
631
+ var a = document.getElementById( 'simcal-system-status-report-download' );
632
+ var file = new Blob( [text], { type: type } );
633
+ a.href = URL.createObjectURL(file);
634
+ a.download = name;
635
+ }
636
+
637
+ jQuery( '#simcal-system-status-report-download' ).on( 'click', function() {
638
+ var file = new Blob( [ report ], { type: 'text/plain' } );
639
+ jQuery( this ).attr( 'href', URL.createObjectURL( file ) );
640
+ jQuery( this ).attr( 'download', '<?php echo sanitize_title( str_replace( array( 'http://', 'https://' ), '', get_bloginfo( 'url' ) ) . '-system-report-' . date( 'Y-m-d', time() ) ); ?>' );
641
+ } );
642
+
643
+ </script>
644
+ <?php
645
+
646
+ }
647
+
648
+ /**
649
+ * PHP sizes conversions.
650
+ *
651
+ * This function transforms the php.ini notation for numbers (like '2M') to an integer.
652
+ *
653
+ * @since 3.0.0
654
+ * @access private
655
+ *
656
+ * @param string $size
657
+ *
658
+ * @return int|double|string
659
+ */
660
+ private function let_to_num( $size ) {
661
+
662
+ $l = substr( $size, -1 );
663
+ $ret = substr( $size, 0, -1 );
664
+
665
+ // Note: do not insert break or default in this switch loop.
666
+ switch ( strtoupper( $l ) ) {
667
+ case 'P':
668
+ $ret *= 1024;
669
+ // no break
670
+ case 'T':
671
+ $ret *= 1024;
672
+ // no break
673
+ case 'G':
674
+ $ret *= 1024;
675
+ // no break
676
+ case 'M':
677
+ $ret *= 1024;
678
+ // no break
679
+ case 'K':
680
+ $ret *= 1024;;
681
+ // no break
682
+ }
683
+
684
+ return $ret;
685
+ }
686
+
687
+ /**
688
+ * Add sections.
689
+ *
690
+ * @since 3.0.0
691
+ *
692
+ * @return array
693
+ */
694
+ public function add_sections() {
695
+ return array();
696
+ }
697
+
698
+ /**
699
+ * Add fields.
700
+ *
701
+ * @since 3.0.0
702
+ *
703
+ * @return array
704
+ */
705
+ public function add_fields() {
706
+ return array();
707
+ }
708
+
709
+ }
includes/admin/post-types.php ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin Post Types
4
+ *
5
+ * @package SimpleCalendar\Admin
6
+ */
7
+ namespace SimpleCalendar\Admin;
8
+
9
+ use SimpleCalendar\Abstracts\Calendar;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Admin post types.
17
+ *
18
+ * Handles admin views and custom content for post types and taxonomies in admin dashboard screens.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Post_Types {
23
+
24
+ /**
25
+ * Hook in tabs.
26
+ *
27
+ * @since 3.0.0
28
+ */
29
+ public function __construct() {
30
+
31
+ // Add meta boxes to custom content.
32
+ new Meta_Boxes();
33
+
34
+ // Add column headers in calendar feeds admin archives.
35
+ add_filter( 'manage_calendar_posts_columns', array( $this, 'add_calendar_feed_column_headers' ) );
36
+ // Process column contents for calendar feeds.
37
+ add_action( 'manage_calendar_posts_custom_column', array( $this, 'calendar_feed_column_content' ), 10, 2 );
38
+
39
+ // Add actions in calendar feed rows.
40
+ add_filter( 'post_row_actions', array( $this, 'row_actions' ), 10, 2 );
41
+ // Add bulk actions.
42
+ add_action( 'admin_init', array( $this, 'bulk_actions' ) );
43
+ // Add content to edit calendars page.
44
+ add_action( 'load-edit.php', array( $this, 'edit_table_hooks' ) );
45
+
46
+ // Default calendar post type content (default event template).
47
+ add_filter( 'default_content', array( $this, 'default_event_template' ), 10, 2 );
48
+
49
+ // Add a clear cache link in submit post box.
50
+ add_action( 'post_submitbox_misc_actions', array( $this, 'clear_cache_button' ) );
51
+
52
+ // Add media button to post editor for adding a shortcode.
53
+ add_action( 'media_buttons', array( $this, 'add_shortcode_button' ), 100 );
54
+ add_action( 'edit_form_after_editor', array( $this, 'add_shortcode_panel' ), 100 );
55
+ }
56
+
57
+ /**
58
+ * Add column headers to Calendar feeds custom post type page.
59
+ *
60
+ * @since 3.0.0
61
+ *
62
+ * @param array $columns Default columns.
63
+ *
64
+ * @return array Filtered output.
65
+ */
66
+ public function add_calendar_feed_column_headers( $columns ) {
67
+
68
+ // New columns.
69
+ $feed_info = array( 'feed' => __( 'Events Source', 'google-calendar-events' ) );
70
+ $calendar_info = array( 'calendar' => __( 'Calendar Type', 'google-calendar-events' ) );
71
+ $shortcode = array( 'shortcode' => __( 'Shortcode', 'google-calendar-events' ) );
72
+
73
+ // Merge with existing columns and rearrange.
74
+ $columns = array_slice( $columns, 0, 2, true ) + $feed_info + $calendar_info + $shortcode + array_slice( $columns, 2, null, true );
75
+
76
+ return $columns;
77
+ }
78
+
79
+ /**
80
+ * Fill out the Calendar feed post type columns.
81
+ *
82
+ * @since 3.0.0
83
+ *
84
+ * @param string $column_name Column identifier.
85
+ * @param int $post_id The calendar feed post id.
86
+ *
87
+ * @return void
88
+ */
89
+ public function calendar_feed_column_content( $column_name, $post_id ) {
90
+
91
+ switch ( $column_name ) {
92
+
93
+ case 'feed':
94
+
95
+ $feed = simcal_get_feed( $post_id );
96
+ echo isset( $feed->name ) ? $feed->name : '&mdash;';
97
+ break;
98
+
99
+ case 'calendar':
100
+
101
+ $info = '&mdash;';
102
+
103
+ if ( $terms = wp_get_object_terms( $post_id, 'calendar_type' ) ) {
104
+
105
+ $calendar_type = sanitize_title( current( $terms )->name );
106
+ $calendar = simcal_get_calendar( $calendar_type );
107
+
108
+ if ( $calendar instanceof Calendar ) {
109
+ $info = $calendar->name;
110
+ $views = get_post_meta( $post_id, '_calendar_view', true );;
111
+ $view = isset( $views[ $calendar->type ] ) ? $views[ $calendar->type ] : '';
112
+
113
+ if ( isset( $calendar->views[ $view ] ) ) {
114
+ $info .= ' &rarr; ' . $calendar->views[ $view ];
115
+ }
116
+ }
117
+ }
118
+
119
+ echo $info;
120
+ break;
121
+
122
+ case 'shortcode' :
123
+
124
+ simcal_print_shortcode_tip( $post_id );
125
+ break;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Add actions to Calendar feed post type row view.
131
+ *
132
+ * @since 3.0.0
133
+ *
134
+ * @param array $actions Default actions
135
+ * @param \WP_Post $post Post object.
136
+ *
137
+ * @return array Filtered output.
138
+ */
139
+ public function row_actions( $actions, $post ) {
140
+
141
+ // Add a clear feed cache action link.
142
+ if ( $post->post_type == 'calendar' ) {
143
+ $actions['duplicate_feed'] = '<a href="' . esc_url( add_query_arg( array( 'duplicate_feed' => $post->ID ) ) ) . '">' . __( 'Clone', 'google-calendar-events' ) . '</a>';
144
+ $actions['clear_cache'] = '<a href="' . esc_url( add_query_arg( array( 'clear_cache' => $post->ID ) ) ) . '">' . __( 'Clear Cache', 'google-calendar-events' ) . '</a>';
145
+ }
146
+
147
+ return $actions;
148
+ }
149
+
150
+ /**
151
+ * Bulk actions.
152
+ *
153
+ * @since 3.0.0
154
+ */
155
+ public function bulk_actions() {
156
+
157
+ // Clear an individual feed cache.
158
+ // @todo Convert the clear cache request to ajax.
159
+ if ( isset( $_REQUEST['clear_cache'] ) ) {
160
+
161
+ $id = intval( $_REQUEST['clear_cache'] );
162
+
163
+ if ( $id > 0 ) {
164
+ simcal_delete_feed_transients( $id );
165
+ }
166
+
167
+ wp_redirect( remove_query_arg( 'clear_cache' ) );
168
+ }
169
+
170
+ // Duplicate a feed post type.
171
+ if ( isset( $_REQUEST['duplicate_feed'] ) ) {
172
+
173
+ $id = intval( $_REQUEST['duplicate_feed'] );
174
+
175
+ if ( $id > 0 ) {
176
+ $this->duplicate_feed( $id );
177
+ }
178
+
179
+ wp_redirect( remove_query_arg( 'duplicate_feed' ) );
180
+ }
181
+
182
+ $bulk_actions = new Bulk_Actions( 'calendar' );
183
+
184
+ $bulk_actions->register_bulk_action( array(
185
+ 'menu_text' => __( 'Clear cache', 'google-calendar-events' ),
186
+ 'action_name' => 'clear_calendars_cache',
187
+ 'callback' => function( $post_ids ) {
188
+ simcal_delete_feed_transients( $post_ids );
189
+ },
190
+ 'admin_notice' => __( 'Cache cleared.', 'google-calendar-events' ),
191
+ )
192
+ );
193
+
194
+ $bulk_actions->init();
195
+ }
196
+
197
+ /**
198
+ * Edit calendars table hooks.
199
+ *
200
+ * @since 3.0.0
201
+ * @internal
202
+ */
203
+ public function edit_table_hooks() {
204
+
205
+ $screen = simcal_is_admin_screen();
206
+
207
+ if ( 'edit-calendar' == $screen ) {
208
+ add_action( 'in_admin_footer', function() {
209
+
210
+ } );
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Clone a feed post type.
216
+ *
217
+ * @since 3.0.0
218
+ *
219
+ * @param int $post_id
220
+ */
221
+ private function duplicate_feed( $post_id ) {
222
+
223
+ if ( $duplicate = get_post( intval( $post_id ), 'ARRAY_A' ) ) {
224
+
225
+ if ( 'calendar' == $duplicate['post_type'] ) {
226
+
227
+ $duplicate['post_title'] = $duplicate['post_title'] . ' (' . __( 'Copy', 'google-calendar-events' ) . ')';
228
+
229
+ unset( $duplicate['ID'] );
230
+ unset( $duplicate['guid'] );
231
+ unset( $duplicate['comment_count'] );
232
+
233
+ $duplicate_id = wp_insert_post( $duplicate );
234
+
235
+ $taxonomies = get_object_taxonomies( $duplicate['post_type'] );
236
+ foreach ( $taxonomies as $taxonomy ) {
237
+ $terms = wp_get_post_terms( $post_id, $taxonomy, array( 'fields' => 'names' ) );
238
+ wp_set_object_terms( $duplicate_id, $terms, $taxonomy );
239
+ }
240
+
241
+ $custom_fields = get_post_custom( $post_id );
242
+ foreach ( $custom_fields as $key => $value ) {
243
+ add_post_meta( $duplicate_id, $key, maybe_unserialize( $value[0] ) );
244
+ }
245
+ }
246
+
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Default event template builder content.
252
+ *
253
+ * @since 3.0.0
254
+ *
255
+ * @param string $content
256
+ * @param \WP_Post $post
257
+ *
258
+ * @return string
259
+ */
260
+ public function default_event_template( $content, $post ) {
261
+ return 'calendar' == $post->post_type ? simcal_default_event_template() : $content;
262
+ }
263
+
264
+ /**
265
+ * Clear cache button.
266
+ *
267
+ * @since 3.0.0
268
+ */
269
+ public function clear_cache_button() {
270
+
271
+ global $post, $post_type;
272
+
273
+ if ( $post_type == 'calendar' && isset( $post->ID ) ) {
274
+ echo '<a id="simcal-clear-cache" class="button" data-id="' . $post->ID . ' ">' .
275
+ '<i class="simcal-icon-spinner simcal-icon-spin" style="display: none;"></i> ' .
276
+ __( 'Clear cache', 'google-calendar-events' ) .
277
+ '</a>';
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Add a shortcode button.
283
+ *
284
+ * Adds a button to add a calendar shortcode in WordPress content editor.
285
+ * Uses Thickbox. http://codex.wordpress.org/ThickBox
286
+ *
287
+ * @since 3.0.0
288
+ */
289
+ public function add_shortcode_button() {
290
+
291
+ $post_types = array();
292
+
293
+ $settings = get_option( 'simple-calendar_settings_calendars' );
294
+ if ( isset( $settings['general']['attach_calendars_posts'] ) ) {
295
+ $post_types = $settings['general']['attach_calendars_posts'];
296
+ }
297
+
298
+ global $post_type;
299
+
300
+ if ( in_array( $post_type, $post_types ) ) {
301
+
302
+ // Thickbox will ignore height and width, will adjust these in js.
303
+ // @see https://core.trac.wordpress.org/ticket/17249
304
+ ?>
305
+ <a href="#TB_inline?height=250&width=500&inlineId=simcal-insert-shortcode-panel" id="simcal-insert-shortcode-button" class="thickbox button insert-calendar add_calendar">
306
+ <span class="wp-media-buttons-icon dashicons-before dashicons-calendar-alt"></span> <?php _e( 'Add Calendar', 'google-calendar-events' ); ?>
307
+ </a>
308
+ <?php
309
+
310
+ }
311
+
312
+ }
313
+
314
+ /**
315
+ * Panel for the add shortcode media button.
316
+ *
317
+ * Prints the panel for choosing a calendar to insert as a shortcode in a page or post.
318
+ *
319
+ * @since 3.0.0
320
+ */
321
+ public function add_shortcode_panel() {
322
+
323
+ $calendars = simcal_get_calendars();
324
+
325
+ ?>
326
+ <div id="simcal-insert-shortcode-panel" style="display:none;">
327
+ <div class="simcal-insert-shortcode-panel">
328
+ <h1><?php _e( 'Add Calendar', 'google-calendar-events' ); ?></h1>
329
+ <?php _e( 'Add a calendar to your post.', 'google-calendar-events' ); ?>
330
+ <?php if ( ! empty( $calendars ) && is_array( $calendars ) ) : ?>
331
+ <p>
332
+ <label for="simcal-choose-calendar">
333
+ <?php $multiselect = count( $calendars ) > 15 ? ' simcal-field-select-enhanced' : ''; ?>
334
+ <select id="simcal-choose-calendar"
335
+ class="simcal-field simcal-field-select<?php echo $multiselect; ?>"
336
+ name="">
337
+ <?php foreach ( $calendars as $id => $title ) : ?>
338
+ <option value="<?php echo $id ?>"><?php echo $title ?></option>
339
+ <?php endforeach; ?>
340
+ </select>
341
+ </label>
342
+ </p>
343
+ <p><input type="button" value="<?php _e( 'Insert Calendar', 'google-calendar-events' ); ?>" id="simcal-insert-shortcode" class="button button-primary button-large" name="" /></p>
344
+ <?php else : ?>
345
+ <p><em><?php _e( 'Could not find any calendars to add to this post.', 'google-calendar-events' ); ?></em></p>
346
+ <strong><a href="post-new.php?post_type=calendar"><?php _e( 'Please add and configure new calendar first.', 'google-calendar-events' ); ?></a></strong>
347
+ <?php endif; ?>
348
+ </div>
349
+ </div>
350
+ <?php
351
+
352
+ }
353
+
354
+ }
includes/admin/updater.php ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Add-ons Updater.
4
+ *
5
+ * @package SimpleCalendar\Admin
6
+ */
7
+ namespace SimpleCalendar\Admin;
8
+
9
+ /**
10
+ * Add-ons updater.
11
+ *
12
+ * @since 3.0.0
13
+ */
14
+ class Updater {
15
+
16
+ /**
17
+ * API Url.
18
+ *
19
+ * @access private
20
+ * @var string
21
+ */
22
+ private $api_url = '';
23
+
24
+ /**
25
+ * API data.
26
+ *
27
+ * @access private
28
+ * @var array|null
29
+ */
30
+ private $api_data = array();
31
+
32
+ /**
33
+ * Name.
34
+ *
35
+ * @access private
36
+ * @var string
37
+ */
38
+ private $name = '';
39
+
40
+ /**
41
+ * Slug.
42
+ *
43
+ * @access private
44
+ * @var string
45
+ */
46
+ private $slug = '';
47
+
48
+ /**
49
+ * Version.
50
+ *
51
+ * @access private
52
+ * @var string
53
+ */
54
+ private $version = '';
55
+
56
+ /**
57
+ * Constructor.
58
+ *
59
+ * @since 3.0.0
60
+ *
61
+ * @param string $_api_url The URL pointing to the custom API endpoint.
62
+ * @param string $_plugin_file Path to the plugin file.
63
+ * @param array $_api_data Optional data to send with API calls.
64
+ */
65
+ public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
66
+
67
+ $this->api_url = trailingslashit( $_api_url );
68
+ $this->api_data = $_api_data;
69
+ $this->name = plugin_basename( $_plugin_file );
70
+ $this->slug = basename( $_plugin_file, '.php' );
71
+ $this->version = $_api_data['version'];
72
+
73
+ // Set up hooks.
74
+ $this->init();
75
+ add_action( 'admin_init', array( $this, 'show_changelog' ) );
76
+ }
77
+
78
+ /**
79
+ * Hook into WordPress update process.
80
+ *
81
+ * @since 3.0.0
82
+ *
83
+ * @return void
84
+ */
85
+ public function init() {
86
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
87
+ add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
88
+ remove_action( 'after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10 );
89
+ add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
90
+ }
91
+
92
+ /**
93
+ * Check for Updates at the defined API endpoint and modify the update array.
94
+ *
95
+ * This function dives into the update API just when WordPress creates its update array,
96
+ * then adds a custom API call and injects the custom plugin data retrieved from the API.
97
+ * It is reassembled from parts of the native WordPress plugin update code.
98
+ * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
99
+ *
100
+ * @since 3.0.0
101
+ *
102
+ * @param array $_transient_data Update array build by WordPress.
103
+ *
104
+ * @return array|\stdClass Modified update array with custom plugin data.
105
+ */
106
+ public function check_update( $_transient_data ) {
107
+
108
+ global $pagenow;
109
+
110
+ if ( ! is_object( $_transient_data ) ) {
111
+ $_transient_data = new \stdClass();
112
+ }
113
+
114
+ if ( 'plugins.php' == $pagenow && is_multisite() ) {
115
+ return $_transient_data;
116
+ }
117
+
118
+ if ( empty( $_transient_data->response ) || empty( $_transient_data->response[ $this->name ] ) ) {
119
+
120
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug ) );
121
+
122
+ if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
123
+
124
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
125
+ $_transient_data->response[ $this->name ] = $version_info;
126
+ }
127
+
128
+ $_transient_data->last_checked = time();
129
+ $_transient_data->checked[ $this->name ] = $this->version;
130
+ }
131
+
132
+ }
133
+
134
+ return $_transient_data;
135
+ }
136
+
137
+ /**
138
+ * Show update notification row.
139
+ *
140
+ * Needed for multisite subsites, because WordPress won't tell otherwise.
141
+ *
142
+ * @since 3.0.0
143
+ *
144
+ * @param string $file
145
+ * @param array $plugin
146
+ */
147
+ public function show_update_notification( $file, $plugin ) {
148
+
149
+ if ( ! current_user_can( 'update_plugins' ) ) {
150
+ return;
151
+ }
152
+
153
+ if ( ! is_multisite() ) {
154
+ return;
155
+ }
156
+
157
+ if ( $this->name != $file ) {
158
+ return;
159
+ }
160
+
161
+ // Remove our filter on the site transient
162
+ remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
163
+
164
+ $update_cache = get_site_transient( 'update_plugins' );
165
+
166
+ if ( ! is_object( $update_cache ) || empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
167
+
168
+ $cache_key = md5( 'edd_plugin_' .sanitize_key( $this->name ) . '_version_info' );
169
+ $version_info = get_transient( $cache_key );
170
+
171
+ if ( false === $version_info ) {
172
+
173
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug ) );
174
+
175
+ set_transient( $cache_key, $version_info, 3600 );
176
+ }
177
+
178
+ if ( ! is_object( $version_info ) ) {
179
+ return;
180
+ }
181
+
182
+ if ( version_compare( $this->version, $version_info->new_version, '<' ) ) {
183
+ $update_cache->response[ $this->name ] = $version_info;
184
+ }
185
+
186
+ $update_cache->last_checked = time();
187
+ $update_cache->checked[ $this->name ] = $this->version;
188
+
189
+ set_site_transient( 'update_plugins', $update_cache );
190
+
191
+ } else {
192
+
193
+ $version_info = $update_cache->response[ $this->name ];
194
+
195
+ }
196
+
197
+ // Restore our filter
198
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
199
+
200
+ if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
201
+
202
+ // build a plugin list row, with update notification
203
+ $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
204
+ echo '<tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message">';
205
+
206
+ $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
207
+
208
+ if ( empty( $version_info->download_link ) ) {
209
+ printf(
210
+ __( 'There is a new version of %1$s available. <a target="_blank" class="thickbox" href="%2$s">View version %3$s details</a>.', 'google-calendar-events' ),
211
+ esc_html( $version_info->name ),
212
+ esc_url( $changelog_link ),
213
+ esc_html( $version_info->new_version )
214
+ );
215
+ } else {
216
+ printf(
217
+ __( 'There is a new version of %1$s available. <a target="_blank" class="thickbox" href="%2$s">View version %3$s details</a> or <a href="%4$s">update now</a>.', 'google-calendar-events' ),
218
+ esc_html( $version_info->name ),
219
+ esc_url( $changelog_link ),
220
+ esc_html( $version_info->new_version ),
221
+ esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) )
222
+ );
223
+ }
224
+
225
+ echo '</div></td></tr>';
226
+ }
227
+ }
228
+
229
+
230
+ /**
231
+ * Updates information on the "View version x.x details" page with custom data.
232
+ *
233
+ * @since 3.0.0
234
+ *
235
+ * @param mixed $_data
236
+ * @param string $_action
237
+ * @param object $_args
238
+ *
239
+ * @return object
240
+ */
241
+ public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
242
+
243
+ if ( 'plugin_information' != $_action ) {
244
+ return $_data;
245
+ }
246
+
247
+ if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
248
+ return $_data;
249
+ }
250
+
251
+ $to_send = array(
252
+ 'slug' => $this->slug,
253
+ 'is_ssl' => is_ssl(),
254
+ 'fields' => array(
255
+ 'banners' => false, // These will be supported soon hopefully
256
+ 'reviews' => false
257
+ ),
258
+ );
259
+
260
+ $api_response = $this->api_request( 'plugin_information', $to_send );
261
+
262
+ if ( false !== $api_response ) {
263
+ $_data = $api_response;
264
+ }
265
+
266
+ return $_data;
267
+ }
268
+
269
+
270
+ /**
271
+ * Disable SSL verification in order to prevent download update failures
272
+ *
273
+ * @since 3.0.0
274
+ *
275
+ * @param array $args
276
+ * @param string $url
277
+ * @return object|array $array
278
+ */
279
+ public function http_request_args( $args, $url ) {
280
+ // If it is an https request and we are performing a package download, disable ssl verification
281
+ if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
282
+ $args['sslverify'] = false;
283
+ }
284
+ return $args;
285
+ }
286
+
287
+ /**
288
+ * Calls the API and, if successful, returns the object delivered by the API.
289
+ *
290
+ * @since 3.0.0
291
+ *
292
+ * @param string $_action The requested action.
293
+ * @param array $_data Parameters for the API action.
294
+ * @return false|object
295
+ */
296
+ private function api_request( $_action, $_data ) {
297
+
298
+ global $wp_version;
299
+
300
+ $data = array_merge( $this->api_data, $_data );
301
+
302
+ if ( $data['slug'] != $this->slug ) {
303
+ return;
304
+ }
305
+
306
+ if ( empty( $data['license'] ) ) {
307
+ return;
308
+ }
309
+
310
+ if ( $this->api_url == home_url() ) {
311
+ return false; // Don't allow a plugin to ping itself
312
+ }
313
+
314
+ $api_params = array(
315
+ 'edd_action' => 'get_version',
316
+ 'license' => $data['license'],
317
+ 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
318
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
319
+ 'slug' => $data['slug'],
320
+ 'author' => $data['author'],
321
+ 'url' => home_url()
322
+ );
323
+
324
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
325
+
326
+ if ( ! is_wp_error( $request ) ) {
327
+ $request = json_decode( wp_remote_retrieve_body( $request ) );
328
+ }
329
+
330
+ if ( $request && isset( $request->sections ) ) {
331
+ $request->sections = maybe_unserialize( $request->sections );
332
+ } else {
333
+ $request = false;
334
+ }
335
+
336
+ return $request;
337
+ }
338
+
339
+ /**
340
+ * Show changelog.
341
+ *
342
+ * @since 3.0.0
343
+ */
344
+ public function show_changelog() {
345
+
346
+ if ( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
347
+ return;
348
+ }
349
+
350
+ if ( empty( $_REQUEST['plugin'] ) ) {
351
+ return;
352
+ }
353
+
354
+ if ( empty( $_REQUEST['slug'] ) ) {
355
+ return;
356
+ }
357
+
358
+ if ( ! current_user_can( 'update_plugins' ) ) {
359
+ wp_die( __( 'You do not have permission to install plugin updates', 'google-calendar-events' ), __( 'Error', 'google-calendar-events' ), array( 'response' => 403 ) );
360
+ }
361
+
362
+ $response = $this->api_request( 'plugin_latest_version', array( 'slug' => $_REQUEST['slug'] ) );
363
+
364
+ if ( $response && isset( $response->sections['changelog'] ) ) {
365
+ echo '<div style="background:#fff;padding:10px;">' . $response->sections['changelog'] . '</div>';
366
+ }
367
+
368
+ exit;
369
+ }
370
+
371
+ }
includes/admin/upgrade.php DELETED
@@ -1,472 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Upgrade functions
5
- *
6
- * @package GCE
7
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
8
- * @license GPL-2.0+
9
- * @copyright 2014 Phil Derksen
10
- */
11
-
12
- // I put the priority to 20 here so it runs after the gce_feed CPT code and we don't get errors
13
- add_action( 'init', 'gce_upgrade', 20 );
14
-
15
- /**
16
- * Main GCE Upgrade function. Call this and branch of from here depending on what we need to do
17
- *
18
- * @since 2.0.0
19
- */
20
- function gce_upgrade() {
21
-
22
- $version = get_option( 'gce_version' );
23
-
24
- if( ! empty( $version ) ) {
25
-
26
- // Clear out cache when upgrading no matter the version
27
- gce_upgrade_clear_cache();
28
-
29
- // Check if under version 2 and run the v2 upgrade if we are
30
- if( version_compare( $version, '2.0.0-beta1', '<' ) && false === get_option( 'gce_upgrade_has_run' ) ) {
31
- gce_v2_upgrade();
32
- }
33
-
34
- // Version 2.0.4 upgrade
35
- if( version_compare( $version, '2.0.4', '<' ) ) {
36
- gce_v204_upgrade();
37
- }
38
-
39
- // Version 2.0.6 upgrade
40
- if( version_compare( $version, '2.0.6', '<' ) ) {
41
- gce_v206_upgrade();
42
- }
43
-
44
- if( version_compare( $version, '2.1.0', '<' ) ) {
45
- gce_v210_upgrade();
46
- }
47
-
48
- if( version_compare( $version, '2.2.0', '<' ) ) {
49
- gce_v220_upgrade();
50
- }
51
-
52
- if( version_compare( $version, '2.2.2', '<' ) ) {
53
- gce_v222_upgrade();
54
- }
55
- }
56
-
57
- $new_version = Google_Calendar_Events::get_instance()->get_plugin_version();
58
- update_option( 'gce_version', $new_version );
59
-
60
- add_option( 'gce_upgrade_has_run', 1 );
61
- }
62
-
63
- function gce_v222_upgrade() {
64
- // Need to set the new option for always enqueuing scripts as default enabled for upgrading users
65
- $options = get_option( 'gce_settings_general' );
66
-
67
- if( false !== $options ) {
68
- $options['always_enqueue'] = 1;
69
- update_option( 'gce_settings_general', $options );
70
- }
71
- }
72
-
73
- function gce_v220_upgrade() {
74
- // Update feeds
75
- $q = new WP_Query( 'post_type=gce_feed' );
76
-
77
- if( $q->have_posts() ) {
78
- while( $q->have_posts() ) {
79
-
80
- $q->the_post();
81
-
82
- $gce_list_max_num = get_post_meta( get_the_ID(), 'gce_list_max_num', true );
83
- $gce_list_max_length = get_post_meta( get_the_ID(), 'gce_list_max_length', true );
84
- $gce_feed_start_interval = get_post_meta( get_the_ID(), 'gce_feed_start_interval', true );
85
- $gce_feed_start = get_post_meta( get_the_ID(), 'gce_feed_start', true );
86
- $gce_feed_end_interval = get_post_meta( get_the_ID(), 'gce_feed_end_interval', true );
87
- $gce_feed_end = get_post_meta( get_the_ID(), 'gce_feed_end', true );
88
-
89
- update_post_meta( get_the_ID(), 'gce_per_page_num', $gce_list_max_num );
90
- update_post_meta( get_the_ID(), 'gce_events_per_page', $gce_list_max_length );
91
- update_post_meta( get_the_ID(), 'gce_feed_start', $gce_feed_start_interval );
92
- update_post_meta( get_the_ID(), 'gce_feed_start_num', $gce_feed_start );
93
- update_post_meta( get_the_ID(), 'gce_feed_end', $gce_feed_end_interval );
94
- update_post_meta( get_the_ID(), 'gce_feed_end_num', $gce_feed_end );
95
-
96
- // Add new show tooltips option checked as default
97
- update_post_meta( get_the_ID(), 'gce_show_tooltips', 1 );
98
-
99
- }
100
- }
101
-
102
- wp_reset_postdata();
103
-
104
-
105
- // Update widgets for new UI
106
- $widget = get_option( 'widget_gce_widget' );
107
-
108
- if( is_array( $widget ) && ! empty( $widget ) ) {
109
- foreach( $widget as $a => $b ) {
110
- if( ! is_array( $b ) ) {
111
- continue;
112
- }
113
-
114
- foreach( $b as $k => $v ) {
115
- $widget[$a]['per_page_num'] = $widget[$a]['list_max_num'];
116
- $widget[$a]['events_per_page'] = $widget[$a]['list_max_length'];
117
- }
118
- }
119
-
120
- update_option( 'widget_gce_widget', $widget );
121
- }
122
- }
123
-
124
- /*
125
- * Run the upgrade to version 2.1.0
126
- */
127
- function gce_v210_upgrade() {
128
-
129
- $q = new WP_Query( 'post_type=gce_feed' );
130
-
131
- if( $q->have_posts() ) {
132
- while( $q->have_posts() ) {
133
- $q->the_post();
134
-
135
- $url = get_post_meta( get_the_ID(), 'gce_feed_url', true );
136
-
137
- // https://www.google.com/calendar/feeds/umsb0ekhivs1a2ubtq6vlqvcjk%40group.calendar.google.com/public/basic
138
-
139
- $url = str_replace( 'https://www.google.com/calendar/feeds/', '', $url );
140
- $url = str_replace( '/public/basic', '', $url );
141
- $url = str_replace( '%40', '@', $url );
142
-
143
- update_post_meta( get_the_ID(), 'gce_feed_url', $url );
144
- }
145
- }
146
-
147
- wp_reset_postdata();
148
- }
149
-
150
- /*
151
- * Run the upgrade to version 2.0.6
152
- *
153
- * @since 2.0.4
154
- */
155
- function gce_v206_upgrade() {
156
-
157
- // Update feeds
158
- $q = new WP_Query( 'post_type=gce_feed' );
159
-
160
- if( $q->have_posts() ) {
161
- while( $q->have_posts() ) {
162
- $q->the_post();
163
-
164
- update_post_meta( get_the_ID(), 'gce_feed_start', '1' );
165
- update_post_meta( get_the_ID(), 'gce_feed_start_interval', 'months' );
166
- update_post_meta( get_the_ID(), 'gce_feed_end', '2' );
167
- update_post_meta( get_the_ID(), 'gce_feed_end_interval', 'years' );
168
- }
169
- }
170
-
171
- wp_reset_postdata();
172
- }
173
-
174
- /*
175
- * Run the upgrade to version 2.0.4
176
- *
177
- * @since 2.0.4
178
- */
179
- function gce_v204_upgrade() {
180
-
181
- // Update feeds
182
- $q = new WP_Query( 'post_type=gce_feed' );
183
-
184
- if( $q->have_posts() ) {
185
- while( $q->have_posts() ) {
186
- $q->the_post();
187
-
188
- update_post_meta( get_the_ID(), 'gce_paging', '1' );
189
- update_post_meta( get_the_ID(), 'gce_list_max_num', '7' );
190
- update_post_meta( get_the_ID(), 'gce_list_max_length', 'days' );
191
- update_post_meta( get_the_ID(), 'gce_list_start_offset_num', '0' );
192
- update_post_meta( get_the_ID(), 'gce_list_start_offset_direction', 'back' );
193
- }
194
- }
195
-
196
- wp_reset_postdata();
197
-
198
-
199
- // Update widgets
200
- $widget = get_option( 'widget_gce_widget' );
201
-
202
- if( is_array( $widget ) && ! empty( $widget ) ) {
203
- foreach( $widget as $a => $b ) {
204
- if( ! is_array( $b ) ) {
205
- continue;
206
- }
207
-
208
- foreach( $b as $k => $v ) {
209
- $widget[$a]['paging'] = '1';
210
- $widget[$a]['list_max_num'] = '7';
211
- $widget[$a]['list_max_length'] = 'days';
212
- $widget[$a]['list_start_offset_num'] = '0';
213
- $widget[$a]['list_start_offset_direction'] = 'back';
214
- }
215
- }
216
-
217
- update_option( 'widget_gce_widget', $widget );
218
- }
219
- }
220
-
221
- /*
222
- * Run the upgrade to version 2.0.0
223
- *
224
- * @since 2.0.0
225
- */
226
- function gce_v2_upgrade() {
227
- $old_options = get_option( 'gce_options' );
228
-
229
- if( false !== $old_options ) {
230
-
231
- if( ! empty( $old_options ) ) {
232
- foreach( $old_options as $key => $value ) {
233
- convert_to_cpt_posts( $value );
234
- }
235
- }
236
-
237
- update_widget_feed_ids();
238
- }
239
- }
240
-
241
- /**
242
- * Converts the old database options to the new CPT layout for 2.0.0+
243
- *
244
- * @since 2.0.0
245
- */
246
- function convert_to_cpt_posts( $args ) {
247
- // Setup our new post
248
- $post = array(
249
- 'post_name' => $args['title'],
250
- 'post_title' => $args['title'],
251
- 'post_status' => 'publish',
252
- 'post_type' => 'gce_feed'
253
- );
254
-
255
- if( $args['use_builder'] == true ) {
256
- $post['post_content'] = $args['builder'];
257
- }
258
-
259
- $post_id = wp_insert_post( $post );
260
-
261
- create_cpt_meta( $post_id, $args );
262
-
263
- clear_old_transients( $args['id'] );
264
- }
265
-
266
- /**
267
- * Add the CPT post meta based on options set for the old feeds prior to v2
268
- *
269
- * @since 2.0.0
270
- */
271
- function create_cpt_meta( $id, $args ) {
272
-
273
- // Convert the dropdown values to the new values for "Retrieve Events From"
274
- switch( $args['retrieve_from'] ) {
275
- case 'now':
276
- case 'today':
277
- $from = 'today';
278
- break;
279
- case 'week':
280
- $from = 'start_week';
281
- break;
282
- case 'month-start':
283
- $from = 'start_month';
284
- break;
285
- case 'month-end':
286
- $from = 'end_month';
287
- break;
288
- case 'date':
289
- $from = 'custom_date';
290
- break;
291
- default:
292
- $from = 'start_time';
293
- break;
294
- }
295
-
296
- // Convert the dropdown values to the new values for "Retrieve Events Until"
297
- switch( $args['retrieve_until'] ) {
298
- case 'now':
299
- case 'today':
300
- $until = 'today';
301
- break;
302
- case 'week':
303
- $until = 'start_week';
304
- break;
305
- case 'month-start':
306
- $until = 'start_month';
307
- break;
308
- case 'month-end':
309
- $until = 'end_month';
310
- break;
311
- case 'date':
312
- $until = 'custom_date';
313
- break;
314
- default:
315
- $until = 'end_time';
316
- break;
317
- }
318
-
319
- $gce_expand_recurring = ( isset( $args['expand_recurring'] ) ? ( $args['expand_recurring'] == 'true' ? '1' : '0' ) : '1' );
320
-
321
- // An array to hold all of our post meta ids and values so that we can loop through and add as post meta easily
322
- $post_meta_fields = array(
323
- 'gce_feed_url' => $args['url'],
324
- 'gce_retrieve_from' => $from,
325
- 'gce_retrieve_until' => $until,
326
- 'gce_retrieve_max' => $args['max_events'],
327
- 'gce_date_format' => $args['date_format'],
328
- 'gce_time_format' => $args['time_format'],
329
- 'gce_cache' => $args['cache_duration'],
330
- 'gce_multi_day_events' => ( $args['multiple_day'] != 'false' ? '1' : '0' ),
331
- 'gce_display_mode' => 'grid',
332
- 'gce_custom_from' => gce_convert_timestamp( $args['retrieve_from_value'] ),
333
- 'gce_custom_until' => gce_convert_timestamp( $args['retrieve_until_value'] ),
334
- 'old_gce_id' => $args['id'],
335
- 'gce_search_query' => ( isset( $args['query'] ) ? $args['query'] : '' ),
336
- 'gce_expand_recurring' => $gce_expand_recurring
337
- );
338
-
339
- if( $args['use_builder'] == 'false' || $args['use_builder'] == false ) {
340
- $display_meta = array(
341
- 'gce_display_simple' => 1,
342
- 'gce_display_start' => $args['display_start'],
343
- 'gce_display_start_text' => $args['display_start_text'],
344
- 'gce_display_end' => $args['display_end'],
345
- 'gce_display_end_text' => $args['display_end_text'],
346
- 'gce_display_separator' => $args['display_separator'],
347
- 'gce_display_location' => ( $args['display_location'] == 'on' ? '1' : '0' ),
348
- 'gce_display_location_text' => $args['display_location_text'],
349
- 'gce_display_description' => ( $args['display_desc'] == 'on' ? '1' : '0' ),
350
- 'gce_display_description_text' => $args['display_desc_text'],
351
- 'gce_display_description_max' => $args['display_desc_limit'],
352
- 'gce_display_link' => ( $args['display_link'] == 'on' ? '1' : '0' ),
353
- 'gce_display_link_tab' => ( $args['display_link_target'] == 'on' ? '1' : '0' ),
354
- 'gce_display_link_text' => $args['display_link_text']
355
- );
356
-
357
- $post_meta_fields = array_merge( $post_meta_fields, $display_meta );
358
- }
359
-
360
- // Loop through each $post_meta_field and add as an entry
361
- foreach( $post_meta_fields as $k => $v ) {
362
- update_post_meta( $id, $k, $v );
363
- }
364
- }
365
-
366
- function gce_convert_timestamp( $t ) {
367
- // mm/dd/yyyy
368
- return date( 'm/d/Y', $t );
369
- }
370
-
371
- /**
372
- * Remove the old transient values from the database
373
- *
374
- * @since 2.0.0
375
- */
376
- function clear_old_transients( $id ) {
377
-
378
- delete_transient( 'gce_feed_' . $id );
379
- delete_transient( 'gce_feed_' . $id . '_url' );
380
- delete_transient( 'gce_feed_ids' );
381
- }
382
-
383
- /**
384
- * Update widget IDs
385
- *
386
- * @since 2.0.0
387
- */
388
- function update_widget_feed_ids() {
389
-
390
- $widget = get_option( 'widget_gce_widget' );
391
-
392
- if( is_array( $widget ) && ! empty( $widget ) ) {
393
- foreach( $widget as $a => $b ) {
394
- if( ! is_array( $b ) ) {
395
- continue;
396
- }
397
-
398
- foreach( $b as $k => $v ) {
399
-
400
- if( $k != 'id' ) {
401
- continue;
402
- }
403
-
404
- $id = $v;
405
-
406
- //$multi = str_replace( ' ', '', $v );
407
-
408
- $multi = explode( ',', str_replace( ' ', '', $id ) );
409
-
410
- if( is_array( $multi ) ) {
411
-
412
- $new_ids = '';
413
-
414
- foreach( $multi as $m ) {
415
- $q = new WP_Query( "post_type=gce_feed&meta_key=old_gce_id&meta_value=$m&order=ASC" );
416
-
417
- if( $q->have_posts() ) {
418
-
419
- $q->the_post();
420
- // Set our ID to the old ID if found
421
- $m = get_the_ID();
422
-
423
- $new_ids .= $m . ',';
424
- }
425
- }
426
-
427
- wp_reset_postdata();
428
-
429
- $widget[$a][$k] = substr( $new_ids, 0, -1 );
430
- } else {
431
-
432
- $q = new WP_Query( "post_type=gce_feed&meta_key=old_gce_id&meta_value=$id&order=ASC" );
433
-
434
- if( $q->have_posts() ) {
435
-
436
- $q->the_post();
437
- // Set our ID to the old ID if found
438
- $id = get_the_ID();
439
- }
440
-
441
- wp_reset_postdata();
442
-
443
- $widget[$a][$k] = $id;
444
- }
445
- }
446
- }
447
-
448
- update_option( 'widget_gce_widget', $widget );
449
- }
450
-
451
- }
452
-
453
- /**
454
- * Update widget IDs
455
- *
456
- * @since 2.0.6.3
457
- */
458
- function gce_upgrade_clear_cache() {
459
- // Update feeds
460
- $q = new WP_Query( 'post_type=gce_feed' );
461
-
462
- if( $q->have_posts() ) {
463
- while( $q->have_posts() ) {
464
-
465
- $q->the_post();
466
-
467
- delete_transient( 'gce_feed_' . get_the_ID() );
468
- }
469
- }
470
-
471
- wp_reset_postdata();
472
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/admin/welcome.php ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Welcome Page Class
4
+ *
5
+ * Adapted from analogue code found in WoCommerce, EDD and WordPress itself.
6
+ *
7
+ * @package SimpleCalendar/Admin
8
+ */
9
+ namespace SimpleCalendar\Admin;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Welcome page.
17
+ *
18
+ * Shows a feature overview for the new version (major) and credits.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Welcome {
23
+
24
+ /**
25
+ * Install type.
26
+ *
27
+ * @access public
28
+ * @var array
29
+ */
30
+ public $install = '';
31
+
32
+ /**
33
+ * Hook in tabs.
34
+ *
35
+ * @since 3.0.0
36
+ */
37
+ public function __construct() {
38
+
39
+ $this->install = isset( $_GET['simcal_install'] ) ? esc_attr( $_GET['simcal_install'] ) : '';
40
+
41
+ add_action( 'admin_menu', array( $this, 'welcome_page_tabs' ) );
42
+ add_action( 'admin_head', array( $this, 'remove_submenu_pages' ) );
43
+ }
44
+
45
+ /**
46
+ * Add page screens.
47
+ *
48
+ * @since 3.0.0
49
+ */
50
+ public function welcome_page_tabs() {
51
+
52
+ $welcome_page_name = __( 'About Simple Calendar', 'google-calendar-events' );
53
+ $welcome_page_title = __( 'Welcome to Simple Calendar', 'google-calendar-events' );
54
+
55
+ $page = isset( $_GET['page'] ) ? $_GET['page'] : 'simple-calendar_about';
56
+
57
+ switch ( $page ) {
58
+
59
+ case 'simple-calendar_about' :
60
+ $page = add_dashboard_page(
61
+ $welcome_page_title,
62
+ $welcome_page_name,
63
+ 'manage_options',
64
+ 'simple-calendar_about',
65
+ array( $this, 'about_screen' )
66
+ );
67
+ add_action( 'admin_print_styles-' . $page, array( $this, 'styles' ) );
68
+ break;
69
+
70
+ case 'simple-calendar_credits' :
71
+ $page = add_dashboard_page(
72
+ $welcome_page_title,
73
+ $welcome_page_name,
74
+ 'manage_options',
75
+ 'simple-calendar_credits',
76
+ array( $this, 'credits_screen' )
77
+ );
78
+ add_action( 'admin_print_styles-' . $page, array( $this, 'styles' ) );
79
+ break;
80
+
81
+ case 'simple-calendar_translators' :
82
+ $page = add_dashboard_page(
83
+ $welcome_page_title,
84
+ $welcome_page_name,
85
+ 'manage_options',
86
+ 'simple-calendar_translators',
87
+ array( $this, 'translators_screen' )
88
+ );
89
+ add_action( 'admin_print_styles-' . $page, array( $this, 'styles' ) );
90
+ break;
91
+ }
92
+
93
+ }
94
+
95
+ /**
96
+ * Remove dashboard page links.
97
+ *
98
+ * @since 3.0.0
99
+ */
100
+ public function remove_submenu_pages() {
101
+ remove_submenu_page( 'index.php', 'simple-calendar_about' );
102
+ remove_submenu_page( 'index.php', 'simple-calendar_credits' );
103
+ remove_submenu_page( 'index.php', 'simple-calendar_translators' );
104
+ }
105
+
106
+ /**
107
+ * Load styles.
108
+ *
109
+ * @since 3.0.0
110
+ */
111
+ public function styles() {
112
+ // TODO Nothing in activation.css yet
113
+ /*
114
+ wp_enqueue_style(
115
+ 'simcal-activation',
116
+ SIMPLE_CALENDAR_ASSETS . '/css/activation.css',
117
+ array(),
118
+ SIMPLE_CALENDAR_VERSION
119
+ );
120
+ */
121
+ }
122
+
123
+ /**
124
+ * Main nav links at top & bottom.
125
+ *
126
+ * @since 3.0.0
127
+ */
128
+ public function main_nav_links() {
129
+
130
+ ?>
131
+ <p>
132
+ <a href="<?php echo admin_url( 'edit.php?post_type=calendar' ); ?>"
133
+ class="button button-primary"
134
+ ><?php _e( 'Calendars', 'google-calendar-events' ); ?></a>
135
+ <a href="<?php echo admin_url( 'admin.php?page=simple-calendar_settings' ); ?>"
136
+ class="button button-primary"
137
+ ><?php _e( 'Settings', 'google-calendar-events' ); ?></a>
138
+ <a href="<?php echo simcal_ga_campaign_url( simcal_get_url( 'add-ons' ), 'core-plugin', 'welcome-page' ); ?>"
139
+ class="docs button button-primary" target="_blank"
140
+ ><?php _e( 'Add-ons', 'google-calendar-events' ); ?></a>
141
+ <a href="<?php echo simcal_ga_campaign_url( simcal_get_url( 'docs' ), 'core-plugin', 'welcome-page' ); ?>"
142
+ class="docs button button-primary" target="_blank"
143
+ ><?php _e( 'Documentation', 'google-calendar-events' ); ?></a>
144
+ </p>
145
+ <?php
146
+
147
+ }
148
+
149
+ /**
150
+ * Intro shown on every about page screen.
151
+ *
152
+ * @since 3.0.0
153
+ */
154
+ private function intro() {
155
+
156
+ ?>
157
+ <h1>
158
+ <?php
159
+ /* translators: %s prints the current version of the plugin. */
160
+ printf( __( 'Welcome to Simple Calendar %s', 'google-calendar-events' ), SIMPLE_CALENDAR_VERSION );
161
+ ?>
162
+ </h1>
163
+
164
+ <div class="about-text">
165
+ <?php
166
+
167
+ if ( 'fresh' == $this->install ) {
168
+ $message = __( 'Thanks, all done!', 'google-calendar-events' );
169
+ } elseif ( 'update' == $this->install ) {
170
+ $message = __( 'Thanks for updating to the latest version!', 'google-calendar-events' );
171
+ } else {
172
+ $message = __( 'Thanks for installing!', 'google-calendar-events' );
173
+ }
174
+
175
+ echo $message;
176
+
177
+ /* translators: %s prints the current version of the plugin. */
178
+ printf( ' ' . __( "Simple Calendar %s has many new display options and is much easier to configure. We think you'll really enjoy using it.", 'google-calendar-events' ), SIMPLE_CALENDAR_VERSION );
179
+
180
+ ?>
181
+ </div>
182
+
183
+ <div class="simcal-badge">&nbsp;</div>
184
+
185
+ <?php $this->main_nav_links(); ?>
186
+
187
+ <h2 class="nav-tab-wrapper">
188
+ <a class="nav-tab <?php if ( $_GET['page'] == 'simple-calendar_about' ) {
189
+ echo 'nav-tab-active';
190
+ } ?>"
191
+ href="<?php echo esc_url( admin_url( add_query_arg( array( 'page' => 'simple-calendar_about' ), 'index.php' ) ) ); ?>"
192
+ ><?php _e( "What's New", 'google-calendar-events' ); ?></a>
193
+ <a class="nav-tab <?php if ( $_GET['page'] == 'simple-calendar_credits' ) {
194
+ echo 'nav-tab-active';
195
+ } ?>"
196
+ href="<?php echo esc_url( admin_url( add_query_arg( array( 'page' => 'simple-calendar_credits' ), 'index.php' ) ) ); ?>"
197
+ ><?php _e( 'Credits', 'google-calendar-events' ); ?></a>
198
+ <a class="nav-tab <?php if ( $_GET['page'] == 'simple-calendar_translators' ) {
199
+ echo 'nav-tab-active';
200
+ } ?>"
201
+ href="<?php echo esc_url( admin_url( add_query_arg( array( 'page' => 'simple-calendar_translators' ), 'index.php' ) ) ); ?>"
202
+ ><?php _e( 'Translators', 'google-calendar-events' ); ?></a>
203
+ </h2>
204
+ <?php
205
+
206
+ }
207
+
208
+ /**
209
+ * Output the about screen.
210
+ *
211
+ * @since 3.0.0
212
+ */
213
+ public function about_screen() {
214
+ $welcome_image_about_path = SIMPLE_CALENDAR_ASSETS . '/images/welcome';
215
+ $welcome_gcal_pro_link = simcal_ga_campaign_url( simcal_get_url( 'gcal-pro' ), 'core-plugin', 'welcome-page' );
216
+
217
+ ?>
218
+ <div id="simcal-welcome">
219
+ <div class="wrap about-wrap whats-new-wrap">
220
+
221
+ <?php $this->intro(); ?>
222
+
223
+ <h3><?php _e( 'Modern calendar displays with easily updateable event text and color options.', 'google-calendar-events' ); ?></h3>
224
+ <img src="<?php echo $welcome_image_about_path . '/grid-view-custom-colors.png'; ?>" />
225
+
226
+ <h3><?php _e( 'Mobile responsive and widget ready.', 'google-calendar-events' ); ?></h3>
227
+ <img src="<?php echo $welcome_image_about_path . '/list-view-widget.png'; ?>" />
228
+ <img src="<?php echo $welcome_image_about_path . '/grid-view-widget-dark-theme.png'; ?>" />
229
+
230
+ <h3><?php _e( 'Simpler, more intuitive calendar settings.', 'google-calendar-events' ); ?></h3>
231
+ <img src="<?php echo $welcome_image_about_path . '/calendar-settings-appearance.png'; ?>" />
232
+
233
+ <h3><?php echo sprintf( __( 'Extendible with add-ons like <a href="%s" target="_blank">Google Calendar Pro</a>.', 'google-calendar-events' ), $welcome_gcal_pro_link ); ?></h3>
234
+ <a href="<?php echo $welcome_gcal_pro_link; ?>" target="_blank"><img src="<?php echo $welcome_image_about_path . '/google-calendar-pro-list-view-annotated.png'; ?>" /></a>
235
+
236
+ <hr/>
237
+
238
+ <?php $this->main_nav_links(); ?>
239
+
240
+ </div>
241
+ </div>
242
+ <?php
243
+
244
+ }
245
+
246
+ /**
247
+ * Output the credits screen.
248
+ *
249
+ * @since 3.0.0
250
+ */
251
+ public function credits_screen() {
252
+
253
+ ?>
254
+ <div id="simcal-welcome">
255
+ <div class="wrap about-wrap credits-wrap">
256
+ <?php $this->intro(); ?>
257
+ <p class="about-description">
258
+ <?php
259
+
260
+ printf(
261
+ __( "Simple Calendar is created by a worldwide team of developers. If you'd like to contribute please visit our <a href='%s' target='_blank'>GitHub repo</a>.", 'google-calendar-events' ),
262
+ simcal_get_url( 'github' )
263
+ );
264
+
265
+ ?>
266
+ </p>
267
+ <?php echo $this->contributors(); ?>
268
+ </div>
269
+ </div>
270
+ <?php
271
+
272
+ }
273
+
274
+ /**
275
+ * Output the translators screen.
276
+ *
277
+ * @since 3.0.0
278
+ */
279
+ public function translators_screen() {
280
+
281
+ ?>
282
+ <div id="simcal-welcome">
283
+ <div class="wrap about-wrap translators-wrap">
284
+ <?php $this->intro(); ?>
285
+ <p class="about-description">
286
+ <?php _e( 'Simple Calendar has been kindly translated into several other languages by contributors from all over the world.', 'google-calendar-events' ); ?>
287
+ </p>
288
+ <p class="about-description">
289
+ <a href="https://translate.wordpress.org/projects/wp-plugins/google-calendar-events" target="_blank"><?php _e( 'Click here to help translate', 'google-calendar-events' ); ?></a>
290
+ </p>
291
+ <?php
292
+
293
+ // Transifex API is not open and requires authentication,
294
+ // Otherwise something like this would be possible:
295
+ // `json_decode( 'https://www.transifex.com/api/2/project/simple-calendar/languages/', true );`
296
+ // Since this is not possible, this has to be done manually.
297
+
298
+ // @TODO switch to WordPress language packs and try to pull list of translators from there
299
+
300
+ ?>
301
+ </div>
302
+ </div>
303
+ <?php
304
+
305
+ }
306
+
307
+ /**
308
+ * Render Contributors List.
309
+ *
310
+ * @since 3.0.0
311
+ *
312
+ * @return string $contributor_list HTML formatted list of contributors.
313
+ */
314
+ public function contributors() {
315
+
316
+ $contributors = $this->get_contributors();
317
+
318
+ if ( empty( $contributors ) ) {
319
+ return '';
320
+ }
321
+
322
+ $contributor_list = '<ul class="wp-people-group">';
323
+
324
+ foreach ( $contributors as $contributor ) {
325
+
326
+ // Skip contributor bots
327
+ $contributor_bots = array( 'gitter-badger' );
328
+ if ( in_array( $contributor->login, $contributor_bots ) ) {
329
+ continue;
330
+ }
331
+
332
+ $contributor_list .= '<li class="wp-person">';
333
+ $contributor_list .= sprintf(
334
+ '<a href="%s" title="%s" target="_blank">%s</a>',
335
+ esc_url( 'https://github.com/' . $contributor->login ),
336
+ esc_html( sprintf( __( 'View %s', 'google-calendar-events' ), $contributor->login ) ),
337
+ sprintf( '<img src="%s" width="64" height="64" class="gravatar" alt="%s" />', esc_url( $contributor->avatar_url ), esc_html( $contributor->login ) )
338
+ );
339
+ $contributor_list .= sprintf(
340
+ '<a class="web" href="%s" target="_blank">%s</a>',
341
+ esc_url( 'https://github.com/' . $contributor->login ),
342
+ esc_html( $contributor->login )
343
+ );
344
+ $contributor_list .= '</li>';
345
+
346
+ }
347
+
348
+ $contributor_list .= '</ul>';
349
+
350
+ return $contributor_list;
351
+ }
352
+
353
+ /**
354
+ * Retrieve list of contributors from GitHub.
355
+ *
356
+ * @since 3.0.0
357
+ *
358
+ * @return mixed
359
+ */
360
+ public function get_contributors() {
361
+
362
+ $contributors = get_transient( '_simple-calendar_contributors' );
363
+ if ( false !== $contributors ) {
364
+ return $contributors;
365
+ }
366
+
367
+ $response = wp_safe_remote_get(
368
+ 'https://api.github.com/repos/moonstonemedia/Simple-Calendar/contributors'
369
+ );
370
+ if ( is_wp_error( $response ) || 200 != wp_remote_retrieve_response_code( $response ) ) {
371
+ return array();
372
+ }
373
+
374
+ $contributors = json_decode( wp_remote_retrieve_body( $response ) );
375
+ if ( ! is_array( $contributors ) ) {
376
+ return array();
377
+ }
378
+
379
+ set_transient( '_simple-calendar_contributors', $contributors, HOUR_IN_SECONDS );
380
+
381
+ return $contributors;
382
+ }
383
+
384
+ }
includes/ajax.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Front End Ajax
4
+ *
5
+ * @package SimpleCalendar
6
+ */
7
+ namespace SimpleCalendar;
8
+
9
+ use SimpleCalendar\Abstracts\Calendar_View;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Front facing ajax.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Ajax {
21
+
22
+ /**
23
+ * Hook in tabs.
24
+ *
25
+ * @since 3.0.0
26
+ */
27
+ public function __construct() {
28
+ add_action( 'init', array( $this, 'add_callbacks' ), 100 );
29
+ }
30
+
31
+ /**
32
+ * Add ajax callbacks.
33
+ *
34
+ * @since 3.0.0
35
+ */
36
+ public function add_callbacks() {
37
+
38
+ $calendars = simcal_get_calendar_types();
39
+
40
+ foreach ( $calendars as $calendar => $views ) {
41
+
42
+ foreach ( $views as $view ) {
43
+
44
+ $the_view = simcal_get_calendar_view( 0, $calendar . '-' . $view );
45
+
46
+ if ( $the_view instanceof Calendar_View ) {
47
+ $the_view->add_ajax_actions();
48
+ }
49
+ }
50
+ }
51
+
52
+ do_action( 'simcal_add_ajax_callbacks' );
53
+ }
54
+
55
+ }
includes/assets.php ADDED
@@ -0,0 +1,381 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Front End Assets
4
+ *
5
+ * @package SimpleCalendar;
6
+ */
7
+ namespace SimpleCalendar;
8
+
9
+ use SimpleCalendar\Abstracts\Calendar_View;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Front end scripts and styles.
17
+ *
18
+ * Loads scripts and styles based on the requested calendar view.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Assets {
23
+
24
+ /**
25
+ * Load minified assets.
26
+ *
27
+ * @access private
28
+ * @var string
29
+ */
30
+ private $min = '.min';
31
+
32
+ /**
33
+ * Scripts.
34
+ *
35
+ * @access private
36
+ * @var array
37
+ */
38
+ private $scripts = array();
39
+
40
+ /**
41
+ * Styles.
42
+ *
43
+ * @access private
44
+ * @var array
45
+ */
46
+ private $styles = array();
47
+
48
+ /**
49
+ * Disable scripts.
50
+ *
51
+ * @access public
52
+ * @var bool
53
+ */
54
+ public $disable_scripts = false;
55
+
56
+ /**
57
+ * Disable styles.
58
+ *
59
+ * @access public
60
+ * @var bool
61
+ */
62
+ public $disable_styles = false;
63
+
64
+ /**
65
+ * Disable styles.
66
+ *
67
+ * @access public
68
+ * @var bool
69
+ */
70
+ public $always_enqueue = false;
71
+
72
+ /**
73
+ * Hook in tabs.
74
+ *
75
+ * @since 3.0.0
76
+ */
77
+ public function __construct() {
78
+
79
+ $this->min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG == true ) ? '' : '.min';
80
+
81
+ $settings = get_option( 'simple-calendar_settings_advanced' );
82
+ if ( isset( $settings['assets']['disable_js'] ) ) {
83
+ $this->disable_scripts = 'yes' == $settings['assets']['disable_js'] ? true : false;
84
+ }
85
+
86
+ if ( isset( $settings['assets']['disable_css'] ) ) {
87
+ $this->disable_styles = 'yes' == $settings['assets']['disable_css'] ? true : false;
88
+ }
89
+
90
+ if ( isset( $settings['assets']['always_enqueue'] ) ) {
91
+ $this->always_enqueue = 'yes' == $settings['assets']['always_enqueue'] ? true : false;
92
+ }
93
+
94
+ add_action( 'init', array( $this, 'register' ), 20 );
95
+ add_action( 'init', array( $this, 'enqueue' ), 40 );
96
+ add_action( 'wp_print_styles', array( $this, 'disable' ), 100 );
97
+ }
98
+
99
+ /**
100
+ * Register scripts and styles.
101
+ *
102
+ * @since 3.0.0
103
+ */
104
+ public function register() {
105
+ do_action( 'simcal_register_assets', $this->min );
106
+ }
107
+
108
+ /**
109
+ * Enqueue scripts and styles.
110
+ *
111
+ * @since 3.0.0
112
+ */
113
+ public function enqueue() {
114
+
115
+ add_action( 'wp_enqueue_scripts', array( $this, 'load' ), 10 );
116
+
117
+ do_action( 'simcal_enqueue_assets', $this->min );
118
+
119
+ if ( false === $this->disable_scripts ) {
120
+ $min = $this->min;
121
+ // Improves compatibility with themes and plugins using Isotope and Masonry.
122
+ add_action( 'wp_enqueue_scripts',
123
+ function () use ( $min ) {
124
+ if ( wp_script_is( 'simcal-qtip', 'enqueued' ) ) {
125
+ wp_enqueue_script(
126
+ 'simplecalendar-imagesloaded',
127
+ SIMPLE_CALENDAR_ASSETS . 'js/vendor/imagesloaded' . $min . '.js',
128
+ array( 'simcal-qtip' ),
129
+ '3.1.8',
130
+ true
131
+ );
132
+ }
133
+ }, 1000 );
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Load scripts and styles.
139
+ *
140
+ * @since 3.0.0
141
+ */
142
+ public function load() {
143
+
144
+ $id = 0;
145
+ $scripts = $styles = array();
146
+
147
+ if ( is_singular() ) {
148
+
149
+ global $post, $post_type;
150
+
151
+ if ( 'calendar' == $post_type ) {
152
+
153
+ $id = get_queried_object_id();
154
+
155
+ } else {
156
+
157
+ $id = absint( get_post_meta( $post->ID, '_simcal_attach_calendar_id', true ) );
158
+
159
+ if ( $id === 0 ) {
160
+
161
+ preg_match_all( '/' . get_shortcode_regex() . '/s', $post->post_content, $matches, PREG_SET_ORDER );
162
+
163
+ if ( ! empty( $matches ) && is_array( $matches ) ) {
164
+ foreach ( $matches as $shortcode ) {
165
+ if ( 'calendar' === $shortcode[2] || 'gcal' === $shortcode[2] ) {
166
+ $atts = shortcode_parse_atts( $shortcode[3] );
167
+ $id = isset( $atts['id'] ) ? intval( $atts['id'] ) : 0;
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+
175
+ if ( $id > 0 ) {
176
+
177
+ $view = simcal_get_calendar_view( $id );
178
+
179
+ if ( $view instanceof Calendar_View ) {
180
+ $scripts = $view->scripts( $this->min );
181
+ $styles = $view->styles( $this->min );
182
+ }
183
+ } else if ( $this->always_enqueue ) {
184
+ $scripts = $this->get_default_scripts();
185
+ $styles = $this->get_default_styles();
186
+ }
187
+
188
+ $this->get_widgets_assets();
189
+
190
+ $this->scripts = apply_filters( 'simcal_front_end_scripts', $scripts, $this->min );
191
+ $this->load_scripts( $this->scripts );
192
+
193
+ $this->styles = apply_filters( 'simcal_front_end_styles', $styles, $this->min );
194
+ $this->load_styles( $this->styles );
195
+ }
196
+
197
+ /**
198
+ * Get widgets assets.
199
+ *
200
+ * @since 3.0.0
201
+ */
202
+ public function get_widgets_assets() {
203
+
204
+ $widgets = get_option( 'widget_gce_widget' );
205
+
206
+ if ( ! empty( $widgets ) && is_array( $widgets ) ) {
207
+
208
+ foreach ( $widgets as $settings ) {
209
+
210
+ if ( ! empty( $settings ) && is_array( $settings ) ) {
211
+
212
+ if ( isset( $settings['calendar_id'] ) ) {
213
+
214
+ $view = simcal_get_calendar_view( absint( $settings['calendar_id'] ) );
215
+
216
+ if ( $view instanceof Calendar_View ) {
217
+ add_filter( 'simcal_front_end_scripts', function ( $scripts, $min ) use ( $view ) {
218
+ return array_merge( $scripts, $view->scripts( $min ) );
219
+ }, 100, 2 );
220
+ add_filter( 'simcal_front_end_styles', function ( $styles, $min ) use ( $view ) {
221
+ return array_merge( $styles, $view->styles( $min ) );
222
+ }, 100, 2 );
223
+ }
224
+
225
+ }
226
+
227
+ }
228
+ }
229
+
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Disable scripts and styles.
235
+ *
236
+ * @since 3.0.0
237
+ */
238
+ public function disable() {
239
+ if ( true === $this->disable_scripts ) {
240
+ $scripts = apply_filters( 'simcal_front_end_scripts', $this->scripts, $this->min );
241
+ foreach ( $scripts as $script => $v ) {
242
+ wp_dequeue_script( $script );
243
+ }
244
+ }
245
+ if ( true === $this->disable_styles ) {
246
+ $styles = apply_filters( 'simcal_front_end_styles', $this->styles, $this->min );
247
+ foreach ( $styles as $style => $v ) {
248
+ wp_dequeue_style( $style );
249
+ }
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Scripts.
255
+ *
256
+ * @since 3.0.0
257
+ *
258
+ * @param array $scripts
259
+ */
260
+ public function load_scripts( $scripts ) {
261
+
262
+ if ( ! empty( $scripts ) && is_array( $scripts ) ) {
263
+
264
+ foreach ( $scripts as $script => $v ) {
265
+
266
+ if ( ! empty( $v['src'] ) ) {
267
+
268
+ $src = esc_url( $v['src'] );
269
+ $deps = isset( $v['deps'] ) ? $v['deps'] : array();
270
+ $ver = isset( $v['ver'] ) ? $v['ver'] : SIMPLE_CALENDAR_VERSION;
271
+ $in_footer = isset( $v['in_footer'] ) ? $v['in_footer'] : false;
272
+
273
+ wp_enqueue_script( $script, $src, $deps, $ver, $in_footer );
274
+
275
+ if ( ! empty( $v['localize'] ) && is_array( $v['localize'] ) ) {
276
+ foreach ( $v['localize'] as $object => $l10n ) {
277
+ wp_localize_script( $script, $object, $l10n );
278
+ }
279
+ }
280
+
281
+ } elseif ( is_string( $v ) && ! empty( $v ) ) {
282
+
283
+ wp_enqueue_script( $v );
284
+ }
285
+ }
286
+
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Styles.
292
+ *
293
+ * @since 3.0.0
294
+ *
295
+ * @param array $styles
296
+ */
297
+ public function load_styles( $styles ) {
298
+
299
+ if ( ! empty( $styles ) && is_array( $styles ) ) {
300
+
301
+ foreach ( $styles as $style => $v ) {
302
+
303
+ if ( ! empty( $v['src'] ) ) {
304
+
305
+ $src = esc_url( $v['src'] );
306
+ $deps = isset( $v['deps'] ) ? $v['deps'] : array();
307
+ $ver = isset( $v['ver'] ) ? $v['ver'] : SIMPLE_CALENDAR_VERSION;
308
+ $media = isset( $v['media'] ) ? $v['media'] : 'all';
309
+
310
+ wp_enqueue_style( $style, $src, $deps, $ver, $media );
311
+
312
+ } elseif ( is_string( $v ) && ! empty( $v ) ) {
313
+
314
+ wp_enqueue_style( $v );
315
+ }
316
+
317
+ }
318
+
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Return the default scripts that are loaded. Used mainly for the always enqueue scripts option.
324
+ *
325
+ * This can be improved.
326
+ */
327
+ public function get_default_scripts() {
328
+ return array(
329
+ 'simcal-qtip' => array(
330
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'js/vendor/qtip' . $this->min . '.js',
331
+ 'deps' => array( 'jquery' ),
332
+ 'ver' => '2.2.1',
333
+ 'in_footer' => true,
334
+ ),
335
+ 'simcal-default-calendar' => array(
336
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'js/default-calendar' . $this->min . '.js',
337
+ 'deps' => array(
338
+ 'jquery',
339
+ 'simcal-qtip',
340
+ ),
341
+ 'var' => SIMPLE_CALENDAR_VERSION,
342
+ 'in_footer' => true,
343
+ 'localize' => array(
344
+ 'simcal_default_calendar' => simcal_common_scripts_variables(),
345
+ ),
346
+ ),
347
+ );
348
+ }
349
+
350
+ /**
351
+ * Return the default styles that are loaded. Used mainly for the always enqueue scripts option.
352
+ *
353
+ * This can be improved.
354
+ */
355
+ public function get_default_styles() {
356
+ return array(
357
+ 'simcal-qtip' => array(
358
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'css/vendor/qtip' . $this->min . '.css',
359
+ 'ver' => '2.2.1',
360
+ 'media' => 'all',
361
+ ),
362
+ 'simcal-default-calendar-grid' => array(
363
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'css/default-calendar-grid' . $this->min . '.css',
364
+ 'deps' => array(
365
+ 'simcal-qtip',
366
+ ),
367
+ 'ver' => SIMPLE_CALENDAR_VERSION,
368
+ 'media' => 'all',
369
+ ),
370
+ 'simcal-default-calendar-list' => array(
371
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'css/default-calendar-list' . $this->min . '.css',
372
+ 'deps' => array(
373
+ 'simcal-qtip',
374
+ ),
375
+ 'ver' => SIMPLE_CALENDAR_VERSION,
376
+ 'media' => 'all',
377
+ ),
378
+ );
379
+ }
380
+
381
+ }
includes/autoload.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Autoloader
4
+ *
5
+ * @package SimpleCalendar
6
+ */
7
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit;
10
+ }
11
+
12
+ if ( ! function_exists( 'SimpleCalendar_Autoload' ) ) {
13
+
14
+ /**
15
+ * Plugin autoloader.
16
+ *
17
+ * Pattern (with or without <Subnamespace>):
18
+ * <Namespace>/<Subnamespace>.../Class_Name (or Classname)
19
+ * 'includes/subdir.../class-name.php' or '...classname.php'
20
+ *
21
+ * @since 3.0.0
22
+ *
23
+ * @param $class
24
+ */
25
+ function SimpleCalendar_Autoload( $class ) {
26
+
27
+ // Do not load unless in plugin domain.
28
+ $namespace = 'SimpleCalendar';
29
+ if ( strpos( $class, $namespace ) !== 0 ) {
30
+ return;
31
+ }
32
+
33
+ // Converts Class_Name (class convention) to class-name (file convention).
34
+ $class_name = implode( '-', array_map( 'lcfirst', explode( '_', strtolower( $class ) ) ) );
35
+
36
+ // Remove the root namespace.
37
+ $unprefixed = substr( $class_name, strlen( $namespace ) );
38
+
39
+ // Build the file path.
40
+ $file_path = str_replace( '\\', DIRECTORY_SEPARATOR, $unprefixed );
41
+ $file = dirname( __FILE__ ) . '/' . $file_path . '.php';
42
+
43
+ if ( file_exists( $file ) ) {
44
+ require $file;
45
+ }
46
+
47
+ }
48
+
49
+ // Register the autoloader.
50
+ spl_autoload_register( 'SimpleCalendar_Autoload' );
51
+
52
+ }
includes/calendars/admin/default-calendar-admin.php ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Default Calendar - Admin
4
+ *
5
+ * @package SimpleCalendar/Feeds
6
+ */
7
+ namespace SimpleCalendar\Calendars\Admin;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Google Calendar feed admin.
15
+ *
16
+ * @since 3.0.0
17
+ */
18
+ class Default_Calendar_Admin {
19
+
20
+ /**
21
+ * Hook in tabs.
22
+ *
23
+ * @since 3.0.0
24
+ */
25
+ public function __construct() {
26
+
27
+ if ( simcal_is_admin_screen() !== false ) {
28
+ add_action( 'simcal_settings_meta_calendar_panel', array( $this, 'add_settings_meta_calendar_panel' ), 10, 1 );
29
+ }
30
+ add_action( 'simcal_process_settings_meta', array( $this, 'process_meta' ), 10, 1 );
31
+ }
32
+
33
+ /**
34
+ * Feed settings page fields.
35
+ *
36
+ * @since 3.0.0
37
+ *
38
+ * @return array
39
+ */
40
+ public function settings_fields() {
41
+ return array();
42
+ }
43
+
44
+ /**
45
+ * Extend the calendar section panel of the settings meta box.
46
+ *
47
+ * @since 3.0.0
48
+ *
49
+ * @param int $post_id
50
+ */
51
+ public function add_settings_meta_calendar_panel( $post_id ) {
52
+
53
+ ?>
54
+ <table id="default-calendar-settings">
55
+ <thead>
56
+ <tr><th colspan="2"><?php _e( 'Default calendar', 'google-calendar-events' ); ?></th></tr>
57
+ </thead>
58
+ <tbody class="simcal-panel-section">
59
+ <tr class="simcal-panel-field simcal-default-calendar-grid" style="display: none;">
60
+ <th><label for="_default_calendar_event_bubbles_action"><?php _e( 'Event bubbles', 'google-calendar-events' ); ?></label></th>
61
+ <td>
62
+ <?php
63
+
64
+ $bubbles = get_post_meta( $post_id, '_default_calendar_event_bubble_trigger', true );
65
+
66
+ simcal_print_field( array(
67
+ 'type' => 'radio',
68
+ 'inline' => 'inline',
69
+ 'name' => '_default_calendar_event_bubble_trigger',
70
+ 'id' => '_default_calendar_event_bubble_trigger',
71
+ 'tooltip' => __( 'Open event bubbles in calendar grid by clicking or hovering on event titles. On mobile devices it will always default to tapping.', 'google-calendar-events' ),
72
+ 'value' => $bubbles ? $bubbles : 'hover',
73
+ 'default' => 'hover',
74
+ 'options' => array(
75
+ 'click' => __( 'Click', 'google-calendar-events' ),
76
+ 'hover' => __( 'Hover', 'google-calendar-events' ),
77
+ ),
78
+ ) );
79
+
80
+ ?>
81
+ </td>
82
+ </tr>
83
+ <tr class="simcal-panel-field simcal-default-calendar-grid" style="display: none;">
84
+ <th><label for="_default_calendar_trim_titles"><?php _e( 'Trim event titles', 'google-calendar-events' ); ?></label></th>
85
+ <td>
86
+ <?php
87
+
88
+ $trim = get_post_meta( $post_id, '_default_calendar_trim_titles', true );
89
+
90
+ simcal_print_field( array(
91
+ 'type' => 'checkbox',
92
+ 'name' => '_default_calendar_trim_titles',
93
+ 'id' => '_default_calendar_trim_titles',
94
+ 'class' => array(
95
+ 'simcal-field-show-next',
96
+ ),
97
+ 'value' => 'yes' == $trim ? 'yes' : 'no',
98
+ 'attributes' => array(
99
+ 'data-show-next-if-value' => 'yes',
100
+ ),
101
+ ) );
102
+
103
+ simcal_print_field( array(
104
+ 'type' => 'standard',
105
+ 'subtype' => 'number',
106
+ 'name' => '_default_calendar_trim_titles_chars',
107
+ 'id' => '_default_calendar_trim_titles_chars',
108
+ 'tooltip' => __( 'Shorten event titles in calendar grid to a specified length in characters.', 'google-calendar-events' ),
109
+ 'class' => array(
110
+ 'simcal-field-tiny',
111
+ ),
112
+ 'value' => 'yes' == $trim ? strval( max( absint( get_post_meta( $post_id, '_default_calendar_trim_titles_chars', true ) ), 1 ) ) : '20',
113
+ 'attributes' => array(
114
+ 'min' => '1',
115
+ ),
116
+ ) );
117
+
118
+ ?>
119
+ </td>
120
+ </tr>
121
+ <tr class="simcal-panel-field simcal-default-calendar-list" style="display: none;">
122
+ <th><label for="_default_calendar_list_grouped_span"><?php _e( 'Span', 'google-calendar-events' ); ?></label></th>
123
+ <td>
124
+ <?php
125
+
126
+ $list_span = max( absint( get_post_meta( $post_id, '_default_calendar_list_range_span', true ) ), 1 );
127
+
128
+ simcal_print_field( array(
129
+ 'type' => 'standard',
130
+ 'subtype' => 'number',
131
+ 'name' => '_default_calendar_list_range_span',
132
+ 'id' => '_default_calendar_list_range_span',
133
+ 'class' => array(
134
+ 'simcal-field-tiny',
135
+ 'simcal-field-inline',
136
+ ),
137
+ 'value' => strval( $list_span ),
138
+ 'attributes' => array(
139
+ 'min' => '1',
140
+ ),
141
+ ) );
142
+
143
+ $list_type = get_post_meta( $post_id, '_default_calendar_list_range_type', true );
144
+
145
+ simcal_print_field( array(
146
+ 'type' => 'select',
147
+ 'name' => '_default_calendar_list_range_type',
148
+ 'id' => '_default_calendar_list_range_type',
149
+ 'tooltip' => __( 'Range of events to show on each calendar page.', 'google-calendar-events' ),
150
+ 'class' => array(
151
+ 'simcal-field-inline',
152
+ ),
153
+ 'value' => $list_type,
154
+ 'options' => array(
155
+ 'monthly' => __( 'Month(s)', 'google-calendar-events' ),
156
+ 'weekly' => __( 'Week(s)', 'google-calendar-events' ),
157
+ 'daily' => __( 'Day(s)', 'google-calendar-events' ),
158
+ 'events' => __( 'Event(s)', 'google-calendar-events' ),
159
+ ),
160
+ ) );
161
+
162
+ ?>
163
+ </td>
164
+ </tr>
165
+ <tr class="simcal-panel-field simcal-default-calendar-grid simcal-default-calendar-list" style="display: none;">
166
+ <th><label for="_default_calendar_limit_visible_events"><?php _e( 'Limit visible events', 'google-calendar-events' ); ?></label></th>
167
+ <td>
168
+ <?php
169
+
170
+ $limit = get_post_meta( $post_id, '_default_calendar_limit_visible_events', true );
171
+
172
+ simcal_print_field( array(
173
+ 'type' => 'checkbox',
174
+ 'name' => '_default_calendar_limit_visible_events',
175
+ 'id' => '_default_calendar_limit_visible_events',
176
+ 'value' => 'yes' == $limit ? 'yes' : 'no',
177
+ 'class' => array(
178
+ 'simcal-field-show-next',
179
+ ),
180
+ 'attributes' => array(
181
+ 'data-show-next-if-value' => 'yes',
182
+ )
183
+ ) );
184
+
185
+ $visible_events = absint( get_post_meta( $post_id, '_default_calendar_visible_events', true ) );
186
+ $visible_events = $visible_events > 0 ? $visible_events : 3;
187
+
188
+ simcal_print_field( array(
189
+ 'type' => 'standard',
190
+ 'subtype' => 'number',
191
+ 'name' => '_default_calendar_visible_events',
192
+ 'id' => '_default_calendar_visible_events',
193
+ 'tooltip' => __( 'Limit the number of initial visible events on each day to a set maximum.', 'google-calendar-events' ),
194
+ 'class' => array(
195
+ 'simcal-field-tiny',
196
+ ),
197
+ 'value' => $visible_events,
198
+ 'attributes' => array(
199
+ 'min' => '1',
200
+ )
201
+ ) );
202
+
203
+ ?>
204
+ </td>
205
+ </tr>
206
+ <tr class="simcal-panel-field simcal-default-calendar-grid simcal-default-calendar-list" style="display: none;">
207
+ <th><label for="_default_calendar_event_bubbles_action"><?php _e( 'Expand multi day events', 'google-calendar-events' ); ?></label></th>
208
+ <td>
209
+ <?php
210
+
211
+ simcal_print_field( array(
212
+ 'type' => 'checkbox',
213
+ 'name' => '_default_calendar_expand_multi_day_events',
214
+ 'id' => '_default_calendar_expand_multi_day_events',
215
+ 'tooltip' => __( 'Show events spanning multiple days on each day.', 'google-calendar-events' ),
216
+ 'value' => get_post_meta( $post_id, '_default_calendar_expand_multi_day_events', true ),
217
+ ) );
218
+
219
+ ?>
220
+ </td>
221
+ </tr>
222
+ </tbody>
223
+ <?php
224
+
225
+ $settings = get_option( 'simple-calendar_settings_calendars' );
226
+ $default_theme = isset( $settings['default-calendar']['theme'] ) ? $settings['default-calendar']['theme'] : 'light';
227
+ $default_today_color = /*isset( $settings['default-calendar']['today_color'] ) ? $settings['default-calendar']['today_color'] :*/ '#FF0000';
228
+ $default_days_events_color = /*isset( $settings['default-calendar']['days_events_color'] ) ? $settings['default-calendar']['days_events_color'] :*/ '#000000';
229
+
230
+ ?>
231
+ <tbody class="simcal-panel-section">
232
+ <tr class="simcal-panel-field simcal-default-calendar-grid simcal-default-calendar-list" style="display: none;">
233
+ <th><label for="_default_calendar_style_theme"><?php _e( 'Theme', 'google-calendar-events' ); ?></label></th>
234
+ <td>
235
+ <?php
236
+
237
+ $saved = get_post_meta( $post_id, '_default_calendar_style_theme', true );
238
+ $value = ! $saved ? $default_theme : $saved;
239
+
240
+ simcal_print_field( array(
241
+ 'type' => 'select',
242
+ 'name' => '_default_calendar_style_theme',
243
+ 'id' => '_default_calendar_style_theme',
244
+ 'value' => $value,
245
+ 'tooltip' => __( 'Choose a calendar theme to match your site theme.', 'google-calendar-events' ),
246
+ 'options' => array(
247
+ 'light' => __( 'Light', 'google-calendar-events' ),
248
+ 'dark' => __( 'Dark', 'google-calendar-events' ),
249
+ ),
250
+ ) );
251
+
252
+ ?>
253
+ </td>
254
+ </tr>
255
+ <tr class="simcal-panel-field simcal-default-calendar-grid simcal-default-calendar-list" style="display: none;">
256
+ <th><label for="_default_calendar_style_today"><?php _e( 'Today', 'google-calendar-events' ); ?></label></th>
257
+ <td>
258
+ <?php
259
+
260
+ $saved = get_post_meta( $post_id, '_default_calendar_style_today', true );
261
+ $value = ! $saved ? $default_today_color : $saved;
262
+
263
+ simcal_print_field( array(
264
+ 'type' => 'standard',
265
+ 'subtype' => 'color-picker',
266
+ 'name' => '_default_calendar_style_today',
267
+ 'id' => '_default_calendar_style_today',
268
+ 'value' => $value,
269
+ ) );
270
+
271
+ ?>
272
+ </td>
273
+ </tr>
274
+ <tr class="simcal-panel-field simcal-default-calendar-grid simcal-default-calendar-list" style="display: none;">
275
+ <th><label for="_default_calendar_style_days_events"><?php _e( 'Days with events', 'google-calendar-events' ); ?></label></th>
276
+ <td>
277
+ <?php
278
+
279
+ $saved = get_post_meta( $post_id, '_default_calendar_style_days_events', true );
280
+ $value = ! $saved ? $default_days_events_color : $saved;
281
+
282
+ simcal_print_field( array(
283
+ 'type' => 'standard',
284
+ 'subtype' => 'color-picker',
285
+ 'name' => '_default_calendar_style_days_events',
286
+ 'id' => '_default_calendar_style_days_events',
287
+ 'value' => $value,
288
+ ) );
289
+
290
+ ?>
291
+ </td>
292
+ </tr>
293
+ </tbody>
294
+ <?php
295
+
296
+ ?>
297
+ </table>
298
+ <?php
299
+
300
+ }
301
+
302
+ /**
303
+ * Process meta fields.
304
+ *
305
+ * @since 3.0.0
306
+ *
307
+ * @param int $post_id
308
+ */
309
+ public function process_meta( $post_id ) {
310
+
311
+ // Theme.
312
+ $theme = isset( $_POST['_default_calendar_style_theme'] ) ? sanitize_key( $_POST['_default_calendar_style_theme'] ) : 'light';
313
+ update_post_meta( $post_id, '_default_calendar_style_theme', $theme );
314
+
315
+ // Today color.
316
+ $today_color = isset( $_POST['_default_calendar_style_today'] ) ? sanitize_text_field( $_POST['_default_calendar_style_today'] ) : '#FF000';
317
+ update_post_meta( $post_id, '_default_calendar_style_today', $today_color );
318
+
319
+ // Days with events color.
320
+ $days_events_color = isset( $_POST['_default_calendar_style_days_events'] ) ? sanitize_text_field( $_POST['_default_calendar_style_days_events'] ) : '#000000';
321
+ update_post_meta( $post_id, '_default_calendar_style_days_events', $days_events_color );
322
+
323
+ // List range span.
324
+ $span = isset( $_POST['_default_calendar_list_range_span'] ) ? max( absint( $_POST['_default_calendar_list_range_span'] ), 1 ) : 1;
325
+ update_post_meta( $post_id, '_default_calendar_list_range_span', $span );
326
+
327
+ // List range type.
328
+ $group = isset( $_POST['_default_calendar_list_range_type'] ) ? sanitize_key( $_POST['_default_calendar_list_range_type'] ) : 'monthly';
329
+ update_post_meta( $post_id, '_default_calendar_list_range_type', $group );
330
+
331
+ // Limit number of initially visible daily events.
332
+ $limit = isset( $_POST['_default_calendar_limit_visible_events'] ) ? 'yes' : 'no';
333
+ update_post_meta( $post_id, '_default_calendar_limit_visible_events', $limit );
334
+ $number = isset( $_POST['_default_calendar_visible_events'] ) ? absint( $_POST['_default_calendar_visible_events'] ) : 3;
335
+ update_post_meta( $post_id, '_default_calendar_visible_events', $number );
336
+
337
+ // Grid event bubbles action.
338
+ $bubbles = isset( $_POST['_default_calendar_event_bubble_trigger'] ) ? esc_attr( $_POST['_default_calendar_event_bubble_trigger'] ) : 'hover';
339
+ update_post_meta( $post_id, '_default_calendar_event_bubble_trigger', $bubbles );
340
+
341
+ // Trim event titles characters length.
342
+ $trim = isset( $_POST['_default_calendar_trim_titles'] ) ? 'yes' : 'no';
343
+ update_post_meta( $post_id, '_default_calendar_trim_titles', $trim );
344
+ $chars = isset( $_POST['_default_calendar_trim_titles_chars'] ) ? max( absint( $_POST['_default_calendar_trim_titles_chars'] ), 1 ) : 20;
345
+ update_post_meta( $post_id, '_default_calendar_trim_titles_chars', $chars );
346
+
347
+ // Expand multiple day events on each day.
348
+ $multi_day = isset( $_POST['_default_calendar_expand_multi_day_events'] ) ? 'yes' : 'no';
349
+ update_post_meta( $post_id, '_default_calendar_expand_multi_day_events', $multi_day );
350
+
351
+ }
352
+
353
+ }
includes/calendars/default-calendar.php ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Default Calendar
4
+ *
5
+ * @package SimpleCalendar\Calendars
6
+ */
7
+ namespace SimpleCalendar\Calendars;
8
+
9
+ use Carbon\Carbon;
10
+ use SimpleCalendar\Abstracts\Calendar;
11
+ use SimpleCalendar\Abstracts\Calendar_View;
12
+ use SimpleCalendar\Calendars\Admin\Default_Calendar_Admin;
13
+ use SimpleCalendar\Calendars\Views;
14
+ use SimpleCalendar\Events\Event;
15
+
16
+ if ( ! defined( 'ABSPATH' ) ) {
17
+ exit;
18
+ }
19
+
20
+ /**
21
+ * Default Calendar.
22
+ *
23
+ * The default calendar view bundled with the plugin.
24
+ *
25
+ * @since 3.0.0
26
+ */
27
+ class Default_Calendar extends Calendar {
28
+
29
+ /**
30
+ * Limit visibility of daily events.
31
+ *
32
+ * @access public
33
+ * @var int
34
+ */
35
+ public $events_limit = -1;
36
+
37
+ /**
38
+ * Trim characters event titles in grid.
39
+ *
40
+ * @access public
41
+ * @var int
42
+ */
43
+ public $trim_titles = -1;
44
+
45
+ /**
46
+ * Event bubbles action trigger.
47
+ *
48
+ * @access public
49
+ * @var string
50
+ */
51
+ public $event_bubble_trigger = 'click';
52
+
53
+ /**
54
+ * Grouped list type.
55
+ *
56
+ * @access public
57
+ * @var string
58
+ */
59
+ public $group_type = '';
60
+
61
+ /**
62
+ * Grouped list span.
63
+ *
64
+ * @access public
65
+ * @var int
66
+ */
67
+ public $group_span = 1;
68
+
69
+ /**
70
+ * Skin theme.
71
+ *
72
+ * @access public
73
+ * @var string
74
+ */
75
+ public $theme = 'light';
76
+
77
+ /**
78
+ * Today color.
79
+ *
80
+ * @access public
81
+ * @var string
82
+ */
83
+ public $today_color = '#FF0000';
84
+
85
+ /**
86
+ * Days with events color.
87
+ *
88
+ * @access public
89
+ * @var string
90
+ */
91
+ public $days_events_color = '#000000';
92
+
93
+ /**
94
+ * Constructor.
95
+ *
96
+ * @since 3.0.0
97
+ *
98
+ * @param int|object|\WP_Post|Calendar $calendar
99
+ */
100
+ public function __construct( $calendar ) {
101
+
102
+ $this->type = 'default-calendar';
103
+ $this->name = __( 'Default', 'google-calendar-events' );
104
+ $this->views = apply_filters( 'simcal_default_calendar_views', array(
105
+ 'grid' => __( 'Grid', 'google-calendar-events' ),
106
+ 'list' => __( 'List', 'google-calendar-events' ),
107
+ ) );
108
+
109
+ parent::__construct( $calendar );
110
+
111
+ if ( ! is_null( $this->post ) ) {
112
+
113
+ $this->set_properties( $this->view->get_type() );
114
+
115
+ $id = $this->id;
116
+ $theme = $this->theme;
117
+
118
+ add_filter( 'simcal_calendar_class', function( $class, $post_id ) use ( $theme, $id ) {
119
+ if ( in_array( 'default-calendar', $class ) && $post_id === $id ) {
120
+ array_push( $class, 'default-calendar-' . $theme );
121
+ }
122
+ return $class;
123
+ }, 10, 2 );
124
+
125
+ }
126
+
127
+ // Calendar settings handling.
128
+ if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
129
+ $admin = new Default_Calendar_Admin();
130
+ $this->settings = $admin->settings_fields();
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Set properties.
136
+ *
137
+ * @since 3.0.0
138
+ * @access private
139
+ *
140
+ * @param $view
141
+ */
142
+ private function set_properties( $view ) {
143
+
144
+ // Set styles.
145
+ if ( 'dark' == get_post_meta( $this->id, '_default_calendar_style_theme', true ) ) {
146
+ $this->theme = 'dark';
147
+ }
148
+ if ( $today_color = get_post_meta( $this->id, '_default_calendar_style_today', true ) ) {
149
+ $this->today_color = esc_attr( $today_color );
150
+ }
151
+ if ( $day_events_color = get_post_meta( $this->id, '_default_calendar_style_days_events', true ) ) {
152
+ $this->days_events_color = esc_attr( $day_events_color );
153
+ }
154
+
155
+ // Hide too many events.
156
+ if ( 'yes' == get_post_meta( $this->id, '_default_calendar_limit_visible_events', true ) ) {
157
+ $this->events_limit = absint( get_post_meta( $this->id, '_default_calendar_visible_events', true ) );
158
+ }
159
+
160
+ // Expand multiple day events.
161
+ if ( 'yes' == get_post_meta( $this->id, '_default_calendar_expand_multi_day_events', true ) ) {
162
+ $this->events = $this->expand_multiple_days_events();
163
+ }
164
+
165
+ if ( 'grid' == $view ) {
166
+
167
+ // Use hover to open event bubbles.
168
+ if ( 'hover' == get_post_meta( $this->id, '_default_calendar_event_bubble_trigger', true ) ) {
169
+ $this->event_bubble_trigger = 'hover';
170
+ }
171
+
172
+ // Trim long event titles.
173
+ if ( 'yes' == get_post_meta( $this->id, '_default_calendar_trim_titles', true ) ) {
174
+ $this->trim_titles = max( absint( get_post_meta( $this->id, '_default_calendar_trim_titles_chars', true ) ), 1 );
175
+ }
176
+
177
+ } else {
178
+
179
+ // List range.
180
+ $this->group_type = esc_attr( get_post_meta( $this->id, '_default_calendar_list_range_type', true ) );
181
+ $this->group_span = max( absint( get_post_meta( $this->id, '_default_calendar_list_range_span', true ) ), 1 );
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Expand multiple day events.
187
+ *
188
+ * @since 3.0.0
189
+ * @access private
190
+ *
191
+ * @return array
192
+ */
193
+ private function expand_multiple_days_events() {
194
+
195
+ $old_events = $this->events;
196
+ $new_events = array();
197
+
198
+ if ( ! empty( $old_events ) ) {
199
+
200
+ foreach ( $old_events as $events ) {
201
+ foreach ( $events as $event ) {
202
+ if ( $event instanceof Event ) {
203
+ if ( false !== $event->multiple_days ) {
204
+ $days = $event->multiple_days;
205
+ for ( $d = 1; $d <= $days; $d++ ) {
206
+ $new_events[ intval( $event->start + ( $d * DAY_IN_SECONDS ) - 1 ) ][] = $event;
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ }
213
+
214
+ }
215
+
216
+ $events = $old_events + $new_events;
217
+ ksort( $events, SORT_NUMERIC );
218
+ return $events;
219
+ }
220
+
221
+ /**
222
+ * Get a view.
223
+ *
224
+ * Returns one of this calendar's views.
225
+ *
226
+ * @since 3.0.0
227
+ *
228
+ * @param string $view
229
+ *
230
+ * @return null|Calendar_View
231
+ */
232
+ public function get_view( $view = '' ) {
233
+
234
+ $view = ! empty( $view ) ? $view : 'grid';
235
+
236
+ do_action( 'simcal_calendar_get_view', $this->type, $view );
237
+
238
+ if ( 'grid' == $view ) {
239
+ return new Views\Default_Calendar_Grid( $this );
240
+ } elseif ( 'list' == $view ) {
241
+ return new Views\Default_Calendar_List( $this );
242
+ }
243
+
244
+ return null;
245
+ }
246
+
247
+ }
includes/calendars/views/default-calendar-grid.php ADDED
@@ -0,0 +1,594 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Default Calendar - Grid View
4
+ *
5
+ * @package SimpleCalendar/Calendars
6
+ */
7
+ namespace SimpleCalendar\Calendars\Views;
8
+
9
+ use Carbon\Carbon;
10
+ use Mexitek\PHPColors\Color;
11
+ use SimpleCalendar\Abstracts\Calendar;
12
+ use SimpleCalendar\Abstracts\Calendar_View;
13
+ use SimpleCalendar\Events\Event;
14
+ use SimpleCalendar\Calendars\Default_Calendar;
15
+
16
+ if ( ! defined( 'ABSPATH' ) ) {
17
+ exit;
18
+ }
19
+
20
+ /**
21
+ * Default Calendar: Grid View.
22
+ *
23
+ * @since 3.0.0
24
+ */
25
+ class Default_Calendar_Grid implements Calendar_View {
26
+
27
+ /**
28
+ * Calendar.
29
+ *
30
+ * @access public
31
+ * @var Default_Calendar
32
+ */
33
+ public $calendar = null;
34
+
35
+ /**
36
+ * Current display start.
37
+ *
38
+ * @access private
39
+ * @var int
40
+ */
41
+ private $start = 0;
42
+
43
+ /**
44
+ * Current display end.
45
+ *
46
+ * @access private
47
+ * @var int
48
+ */
49
+ private $end = 0;
50
+
51
+ /**
52
+ * Constructor.
53
+ *
54
+ * @since 3.0.0
55
+ *
56
+ * @param string|Calendar $calendar
57
+ */
58
+ public function __construct( $calendar = '' ) {
59
+ $this->calendar = $calendar;
60
+ }
61
+
62
+ /**
63
+ * Get the view parent calendar type.
64
+ *
65
+ * @since 3.0.0
66
+ *
67
+ * @return string
68
+ */
69
+ public function get_parent() {
70
+ return 'default-calendar';
71
+ }
72
+
73
+ /**
74
+ * Get the view type.
75
+ *
76
+ * @since 3.0.0
77
+ *
78
+ * @return string
79
+ */
80
+ public function get_type() {
81
+ return 'grid';
82
+ }
83
+
84
+ /**
85
+ * Get the view name.
86
+ *
87
+ * @since 3.0.0
88
+ *
89
+ * @return string
90
+ */
91
+ public function get_name() {
92
+ return __( 'Grid', 'google-calendar-events' );
93
+ }
94
+
95
+ /**
96
+ * Add ajax actions.
97
+ *
98
+ * @since 3.0.0
99
+ */
100
+ public function add_ajax_actions() {
101
+ add_action( 'wp_ajax_simcal_default_calendar_draw_grid', array( $this, 'draw_grid_ajax' ) );
102
+ add_action( 'wp_ajax_nopriv_simcal_default_calendar_draw_grid', array( $this, 'draw_grid_ajax' ) );
103
+ }
104
+
105
+ /**
106
+ * Default calendar grid scripts.
107
+ *
108
+ * Scripts to load when this view is displayed.
109
+ *
110
+ * @since 3.0.0
111
+ *
112
+ * @param string $min
113
+ *
114
+ * @return array
115
+ */
116
+ public function scripts( $min = '' ) {
117
+ return array(
118
+ 'simcal-qtip' => array(
119
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'js/vendor/qtip' . $min . '.js',
120
+ 'deps' => array( 'jquery' ),
121
+ 'ver' => '2.2.1',
122
+ 'in_footer' => true,
123
+ ),
124
+ 'simcal-default-calendar' => array(
125
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'js/default-calendar' . $min . '.js',
126
+ 'deps' => array(
127
+ 'jquery',
128
+ 'simcal-qtip',
129
+ ),
130
+ 'var' => SIMPLE_CALENDAR_VERSION,
131
+ 'in_footer' => true,
132
+ 'localize' => array(
133
+ 'simcal_default_calendar' => simcal_common_scripts_variables(),
134
+ ),
135
+ ),
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Default calendar grid styles.
141
+ *
142
+ * Stylesheets to load when this view is displayed.
143
+ *
144
+ * @since 3.0.0
145
+ *
146
+ * @param string $min = ''
147
+ *
148
+ * @return array
149
+ */
150
+ public function styles( $min = '' ) {
151
+ return array(
152
+ 'simcal-qtip' => array(
153
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'css/vendor/qtip' . $min . '.css',
154
+ 'ver' => '2.2.1',
155
+ 'media' => 'all',
156
+ ),
157
+ 'simcal-default-calendar-grid' => array(
158
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'css/default-calendar-grid' . $min . '.css',
159
+ 'deps' => array(
160
+ 'simcal-qtip',
161
+ ),
162
+ 'ver' => SIMPLE_CALENDAR_VERSION,
163
+ 'media' => 'all',
164
+ ),
165
+ );
166
+ }
167
+
168
+ /**
169
+ * Default calendar grid markup.
170
+ *
171
+ * @since 3.0.0
172
+ */
173
+ public function html() {
174
+
175
+ $calendar = $this->calendar;
176
+
177
+ if ( $calendar instanceof Default_Calendar ) {
178
+
179
+ ?>
180
+ <table class="simcal-calendar-grid"
181
+ data-event-bubble-trigger="<?php echo $calendar->event_bubble_trigger; ?>">
182
+ <thead class="simcal-calendar-head">
183
+ <tr>
184
+ <?php if ( ! $calendar->static ) { ?>
185
+ <th class="simcal-nav" colspan="1">
186
+ <button class="simcal-nav-button simcal-month-nav simcal-prev" title="<?php _e( 'Previous Month', 'google-calendar-events' ); ?>"><i class="simcal-icon-left"></i></button>
187
+ </th>
188
+ <?php } ?>
189
+ <th colspan="<?php echo $calendar->static ? '7' : '5'; ?>"
190
+ class="simcal-nav simcal-current"
191
+ data-calendar-current="<?php echo $calendar->start; ?>">
192
+ <?php
193
+
194
+ echo '<h3>';
195
+
196
+ // Display month and year according to user date format preference.
197
+
198
+ $year_pos = strcspn( $calendar->date_format, 'Y y' );
199
+ $month_pos = strcspn( $calendar->date_format, 'F M m n' );
200
+
201
+ $current = array( 'month' => 'F', 'year' => 'Y' );
202
+
203
+ if ( $year_pos < $month_pos ) {
204
+ $current = array_reverse( $current );
205
+ }
206
+
207
+ foreach ( $current as $k => $v ) {
208
+ echo ' <span class="simcal-current-' . $k , '">' . date_i18n( $v, $calendar->start ) . '</span> ';
209
+ }
210
+
211
+ echo '</h3>';
212
+
213
+ ?>
214
+ </th>
215
+ <?php if ( ! $calendar->static ) { ?>
216
+ <th class="simcal-nav" colspan="1">
217
+ <button class="simcal-nav-button simcal-month-nav simcal-next" title="<?php _e( 'Next Month', 'google-calendar-events' ); ?>"><i class="simcal-icon-right"></i></button>
218
+ </th>
219
+ <?php } ?>
220
+ </tr>
221
+ <tr>
222
+ <?php
223
+
224
+ // Print day names in short or long form for different viewport sizes.
225
+
226
+ $week_starts = $calendar->week_starts;
227
+ $week_days_short = simcal_get_calendar_names_i18n( 'day', 'short' );
228
+ $week_days_full = simcal_get_calendar_names_i18n( 'day', 'full' );
229
+
230
+ for ( $i = $week_starts; $i <= 6; $i ++ ) :
231
+
232
+ ?>
233
+ <th class="simcal-week-day simcal-week-day-<?php echo $i ?>"
234
+ data-screen-small="<?php echo substr( $week_days_short[ $i ], 0, 1 ); ?>"
235
+ data-screen-medium="<?php echo $week_days_short[ $i ]; ?>"
236
+ data-screen-large="<?php echo $week_days_full[ $i ]; ?>"><?php echo $week_days_short[ $i ]; ?></th>
237
+ <?php
238
+
239
+ endfor;
240
+
241
+ if ( $week_starts !== 0 ) :
242
+ for ( $i = 0; $i < $week_starts; $i ++ ) :
243
+
244
+ ?>
245
+ <th class="simcal-week-day simcal-week-day-<?php echo $i ?>"
246
+ data-screen-small="<?php echo substr( $week_days_short[ $i ], 0, 1 ); ?>"
247
+ data-screen-medium="<?php echo $week_days_short[ $i ]; ?>"
248
+ data-screen-large="<?php echo $week_days_full[ $i ]; ?>"><?php echo $week_days_short[ $i ]; ?></th>
249
+ <?php
250
+
251
+ endfor;
252
+ endif;
253
+
254
+ ?>
255
+ </tr>
256
+ </thead>
257
+
258
+ <?php echo $this->draw_month( date( 'n', $calendar->start ), date( 'Y', $calendar->start ) ); ?>
259
+
260
+ </table>
261
+ <?php
262
+
263
+ echo '<div class="simcal-ajax-loader simcal-spinner-top" style="display: none;"><i class="simcal-icon-spinner simcal-icon-spin"></i></div>';
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Make a calendar grid.
269
+ *
270
+ * Outputs an html calendar according to month and year passed in arguments.
271
+ * Loosely inspired by: http://davidwalsh.name/php-calendar
272
+ * Adjusted by timezone and with an arbitrary week start day.
273
+ *
274
+ * @since 3.0.0
275
+ * @access private
276
+ *
277
+ * @param int $month The month to print (two digits).
278
+ * @param int $year The corresponding year (four digits).
279
+ * @param int $id The calendar id.
280
+ *
281
+ * @return string
282
+ */
283
+ private function draw_month( $month, $year, $id = 0 ) {
284
+
285
+ $calendar = $this->calendar;
286
+ if ( empty( $calendar ) ) {
287
+ $calendar = simcal_get_calendar( intval( $id ) );
288
+ if ( ! $calendar ) {
289
+ return '';
290
+ }
291
+ }
292
+ date_default_timezone_set( $calendar->timezone );
293
+ $events = $calendar->events;
294
+
295
+ // Variables to cycle days in current month and find today in calendar.
296
+ $now = $calendar->now;
297
+ $current = Carbon::create( $year, $month, 1, 0, 0, 59, $calendar->timezone );
298
+ $current_min = $current->getTimestamp();
299
+ $current_max = $current->endOfDay()->getTimestamp();
300
+
301
+ // Calendar grid variables.
302
+ $week_starts = $calendar->week_starts;
303
+ $week_of_year = $current->weekOfYear; // Relative count of the week number of the year.
304
+ $month_starts = $current->dayOfWeek; // Day upon which the month starts.
305
+ $days_in_month = $current->daysInMonth; // Number of days in the given month.
306
+
307
+ // Set current month events timestamp boundaries.
308
+ $this->start = $current_min;
309
+ $this->end = $current->endOfMonth()->timestamp;
310
+
311
+ // Get daily events for this month.
312
+ if ( $events && is_array( $events ) ) {
313
+
314
+ // Filter events within the boundaries previously set above.
315
+ $timestamps = array_keys( $events );
316
+ $lower_bound = array_filter( $timestamps, array( $this, 'filter_events_before' ) );
317
+ $higher_bound = array_filter( $lower_bound, array( $this, 'filter_events_after' ) );
318
+ $filtered = array_intersect_key( $events, array_combine( $higher_bound, $higher_bound ) );
319
+
320
+ // Put resulting events in an associative array, with day of the month as key for easy retrieval in calendar days loop.
321
+ $day_events = array();
322
+ foreach ( $filtered as $timestamp => $events_in_day ) {
323
+ foreach ( $events_in_day as $event ) {
324
+ if ( $event instanceof Event ){
325
+ $day = intval( Carbon::createFromTimestamp( $timestamp, $event->timezone )->endOfDay()->day );
326
+ $day_events[ $day ][] = $event;
327
+ }
328
+ }
329
+ }
330
+
331
+ ksort( $day_events, SORT_NUMERIC );
332
+ }
333
+
334
+ ob_start();
335
+
336
+ echo '<tbody class="simcal-month simcal-month-' . $month . '">' . "\n";
337
+ echo "\t" . '<tr class="simcal-week simcal-week-' . $week_of_year . '">';
338
+
339
+ $days_in_row = 0;
340
+ // Week may start on an arbitrary day (sun, 0 - sat, 6).
341
+ $week_day = $week_starts;
342
+
343
+ // This fixes a possible bug when a month starts by Sunday (0).
344
+ if ( 0 !== $week_starts ) {
345
+ $b = $month_starts === 0 ? 7 : $month_starts;
346
+ } else {
347
+ $b = $month_starts;
348
+ }
349
+
350
+ // Void days in first week.
351
+ for ( $a = $week_starts; $a < $b; $a++ ) :
352
+
353
+ echo '<td class="simcal-day simcal-day-void"></td>';
354
+
355
+ // Reset day of the week count (sun, 0 - sat, 6).
356
+ if ( $week_day === 6 ) {
357
+ $week_day = -1;
358
+ }
359
+ $week_day++;
360
+
361
+ $days_in_row++;
362
+
363
+ endfor;
364
+
365
+ // Actual days of the month.
366
+ for ( $day = 1; $day <= $days_in_month; $day++ ) :
367
+
368
+ $count = 0;
369
+ $calendar_classes = array();
370
+ $day_classes = 'simcal-day-' . $day . ' simcal-weekday-' . $week_day;
371
+
372
+ $border_style = $bg_color = $color = '';
373
+
374
+ // Is this the present, the past or the future, Doc?
375
+ if ( $current_min <= $now && $current_max >= $now ) {
376
+ $day_classes .= ' simcal-today simcal-present simcal-day';
377
+ $the_color = new Color( $calendar->today_color );
378
+ $bg_color = '#' . $the_color->getHex();
379
+ $color = $the_color->isDark() ? '#ffffff' : '#000000';
380
+ $border_style = ' style="border: 1px solid ' . $bg_color . ';"';
381
+ } elseif ( $current_max < $now ) {
382
+ $day_classes .= ' simcal-past simcal-day';
383
+ } elseif ( $current_min > $now ) {
384
+ $day_classes .= ' simcal-future simcal-day';
385
+ }
386
+
387
+ // Print events for the current day in loop, if found any.
388
+ if ( isset( $day_events[ $day ] ) ) :
389
+
390
+ $list_events = '<ul class="simcal-events"' . $border_style . '>';
391
+
392
+ foreach ( $day_events[ $day ] as $event ) :
393
+
394
+ $event_classes = $event_visibility = '';
395
+
396
+ if ( $event instanceof Event ) :
397
+
398
+ // Store the calendar id where the event belongs (useful in grouped calendar feeds)
399
+ $calendar_class = 'simcal-events-calendar-' . strval( $event->calendar );
400
+ $calendar_classes[] = $calendar_class ;
401
+
402
+ $recurring = $event->recurrence ? 'simcal-event-recurring ' : '';
403
+ $has_location = $event->venue ? 'simcal-event-has-location ' : '';
404
+
405
+ $event_classes .= 'simcal-event ' . $recurring . $has_location . $calendar_class . ' simcal-tooltip';
406
+
407
+ // Toggle some events visibility if more than optional limit.
408
+ if ( ( $calendar->events_limit > -1 ) && ( $count >= $calendar->events_limit ) ) :
409
+ $event_classes .= ' simcal-event-toggled';
410
+ $event_visibility = ' style="display: none"';
411
+ endif;
412
+
413
+ // Event title in list.
414
+ $title = ! empty( $event->title ) ? trim( $event->title ) : __( 'Event', 'google-calendar-events' );
415
+ if ( $calendar->trim_titles >= 1 ) {
416
+ $title = strlen( $title ) > $calendar->trim_titles ? substr( $title, 0, $calendar->trim_titles ) . '&hellip;' : $title;
417
+ }
418
+
419
+ // Event color.
420
+ $bullet = '';
421
+ $event_color = $event->get_color();
422
+ if ( ! empty( $event_color ) ) {
423
+ $bullet = '<span style="color: ' . $event_color . ';">&#9632;</span> ';
424
+ }
425
+
426
+ // Event contents.
427
+ $list_events .= "\t" . '<li class="' . $event_classes . '"' . $event_visibility . ' itemprop="event" itemscope itemtype="http://schema.org/Event">' . "\n";
428
+ $list_events .= "\t\t" . '<span class="simcal-event-title">' . $bullet . $title . '</span>' . "\n";
429
+ $list_events .= "\t\t" . '<div class="simcal-event-details simcal-tooltip-content" style="display: none;">' . $calendar->get_event_html( $event ) . '</div>' . "\n";
430
+ $list_events .= "\t" . '</li>' . "\n";
431
+
432
+ $count ++;
433
+
434
+ endif;
435
+
436
+ endforeach;
437
+
438
+ if ( ( $current_min <= $now ) && ( $current_max >= $now ) ) {
439
+ $day_classes .= ' simcal-today-has-events';
440
+ }
441
+ $day_classes .= ' simcal-day-has-events simcal-day-has-' . strval( $count ) . '-events';
442
+
443
+ if ( $calendar_classes ) {
444
+ $day_classes .= ' ' . trim( implode( ' ', array_unique( $calendar_classes ) ) );
445
+ }
446
+
447
+ $list_events .= '</ul>' . "\n";
448
+
449
+ // Optional button to toggle hidden events in list.
450
+ if ( ( $calendar->events_limit > -1 ) && ( $count > $calendar->events_limit ) ) :
451
+ $list_events .= '<button class="simcal-events-toggle"><i class="simcal-icon-down simcal-icon-animate"></i></button>';
452
+ endif;
453
+
454
+ else :
455
+
456
+ $border_style = ! empty( $the_color ) ? $border_style : '';
457
+
458
+ // Empty cell for day with no events.
459
+ $list_events = '<span class="simcal-no-events"' . $border_style . '></span>';
460
+
461
+ endif;
462
+
463
+ // The actual days with numbers and events in each row cell.
464
+ echo '<td class="' . $day_classes . '" data-events-count="' . strval( $count ) . '">' . "\n";
465
+
466
+ if ( $color ) {
467
+ $day_style = ' style="background-color: ' . $bg_color . '; color: ' . $color .'"';
468
+ } elseif ( $count > 0 ) {
469
+ $the_color = new Color( $calendar->days_events_color );
470
+ $color = ! $color ? ( $the_color->isDark() ? '#ffffff' : '#000000' ) : $color;
471
+ $bg_color = ! $bg_color ? '#' . $the_color->getHex() : $bg_color;
472
+ $day_style = ' style="background-color: ' . $bg_color . '; color: ' . $color .'"';
473
+ } else {
474
+ $day_style = '';
475
+ }
476
+
477
+ echo "\t" . '<div>' . "\n";
478
+ echo "\t\t" . '<span class="simcal-day-label simcal-day-number"' . $day_style . '>' . $day . '</span>' . "\n";
479
+ echo "\t\t" . $list_events . "\n";
480
+ echo "\t\t";
481
+ echo '<span class="simcal-events-dots" style="display: none;">';
482
+
483
+ // Event bullets for calendar mobile mode.
484
+ for( $i = 0; $i < $count; $i++ ) {
485
+ echo '<b> &bull; </b>';
486
+ }
487
+
488
+ echo '</span>' . "\n";
489
+ echo "\t" . '</div>' . "\n";
490
+ echo '</td>' . "\n";
491
+
492
+ // Reset day of the week count (sun, 0 - sat, 6).
493
+ if ( $week_day === 6 ) {
494
+ $week_day = - 1;
495
+ }
496
+ $week_day++;
497
+
498
+ // Reset count of days for this row (0-6).
499
+ if ( $days_in_row === 6 ) :
500
+
501
+ // Close the week row.
502
+ echo '</tr>';
503
+
504
+ // Open a new week row.
505
+ if ( $day < $days_in_month ) {
506
+ echo '<tr class="simcal-week simcal-week-' . $week_of_year++ . '">' . "\n";
507
+ }
508
+
509
+ $days_in_row = -1;
510
+
511
+ endif;
512
+
513
+ $days_in_row++;
514
+
515
+ $current_min = Carbon::createFromTimestamp( $current_min, $calendar->timezone )->addDay()->getTimestamp();
516
+ $current_max = Carbon::createFromTimestamp( $current_max, $calendar->timezone )->addDay()->getTimestamp();
517
+
518
+ endfor;
519
+
520
+ // Void days at the end of the month.
521
+ $remainder_days = 6 - $days_in_row;
522
+ if ( $remainder_days >= 1 ) {
523
+
524
+ for ( $i = 0; $i <= $remainder_days; $i ++ ) {
525
+
526
+ echo '<td class="simcal-day simcal-day-void"></td>' . "\n";
527
+
528
+ $week_day++;
529
+ }
530
+
531
+ } elseif ( $days_in_row === 6 ) {
532
+ echo '<td class="simcal-day simcal-day-void"></td>' . "\n";
533
+ }
534
+
535
+ echo "\t" . '</tr>' . "\n";
536
+ echo '</tbody>' . "\n";
537
+
538
+ date_default_timezone_set( $calendar->site_timezone );
539
+
540
+ return ob_get_clean();
541
+ }
542
+
543
+ /**
544
+ * Ajax callback to request a new month.
545
+ *
546
+ * @since 3.0.0
547
+ */
548
+ public function draw_grid_ajax() {
549
+
550
+ if ( isset( $_POST['month'] ) && isset( $_POST['year'] ) && isset( $_POST['id'] ) ) {
551
+
552
+ $month = absint( $_POST['month'] );
553
+ $year = absint( $_POST['year'] );
554
+ $id = absint( $_POST['id'] );
555
+
556
+ wp_send_json_success( $this->draw_month( $month, $year, $id ) );
557
+
558
+ } else {
559
+
560
+ wp_send_json_error( 'Missing arguments in default calendar grid ajax request.' );
561
+
562
+ }
563
+
564
+ }
565
+
566
+ /**
567
+ * Array filter callback.
568
+ *
569
+ * @since 3.0.0
570
+ * @access private
571
+ *
572
+ * @param int $event Timestamp.
573
+ *
574
+ * @return bool
575
+ */
576
+ private function filter_events_before( $event ) {
577
+ return intval( $event ) > intval( $this->start );
578
+ }
579
+
580
+ /**
581
+ * Array filter callback.
582
+ *
583
+ * @since 3.0.0
584
+ * @access private
585
+ *
586
+ * @param int $event Timestamp.
587
+ *
588
+ * @return bool
589
+ */
590
+ private function filter_events_after( $event ) {
591
+ return intval( $event ) < intval( $this->end );
592
+ }
593
+
594
+ }
includes/calendars/views/default-calendar-list.php ADDED
@@ -0,0 +1,636 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Default Calendar - List View
4
+ *
5
+ * @package SimpleCalendar/Calendars
6
+ */
7
+ namespace SimpleCalendar\Calendars\Views;
8
+
9
+ use Carbon\Carbon;
10
+ use Mexitek\PHPColors\Color;
11
+ use SimpleCalendar\Abstracts\Calendar;
12
+ use SimpleCalendar\Abstracts\Calendar_View;
13
+ use SimpleCalendar\Calendars\Default_Calendar;
14
+ use SimpleCalendar\Events\Event;
15
+
16
+ if ( ! defined( 'ABSPATH' ) ) {
17
+ exit;
18
+ }
19
+
20
+ /**
21
+ * Default Calendar: List View.
22
+ *
23
+ * @since 3.0.0
24
+ */
25
+ class Default_Calendar_List implements Calendar_View {
26
+
27
+ /**
28
+ * Calendar.
29
+ *
30
+ * @access public
31
+ * @var Default_Calendar
32
+ */
33
+ public $calendar = null;
34
+
35
+ /**
36
+ * Current display start.
37
+ *
38
+ * @access private
39
+ * @var int
40
+ */
41
+ private $start = 0;
42
+
43
+ /**
44
+ * Current display end.
45
+ *
46
+ * @access private
47
+ * @var int
48
+ */
49
+ private $end = 0;
50
+
51
+ /**
52
+ * Previous display start.
53
+ *
54
+ * @access private
55
+ * @var int
56
+ */
57
+ private $prev = 0;
58
+
59
+ /**
60
+ * Next display start.
61
+ *
62
+ * @access private
63
+ * @var int
64
+ */
65
+ private $next = 0;
66
+
67
+ /**
68
+ * Constructor.
69
+ *
70
+ * @since 3.0.0
71
+ *
72
+ * @param string|Calendar $calendar
73
+ */
74
+ public function __construct( $calendar = '' ) {
75
+ $this->calendar = $calendar;
76
+ }
77
+
78
+ /**
79
+ * Get the view parent calendar type.
80
+ *
81
+ * @since 3.0.0
82
+ *
83
+ * @return string
84
+ */
85
+ public function get_parent() {
86
+ return 'default-calendar';
87
+ }
88
+
89
+ /**
90
+ * Get the view type.
91
+ *
92
+ * @since 3.0.0
93
+ *
94
+ * @return string
95
+ */
96
+ public function get_type() {
97
+ return 'list';
98
+ }
99
+
100
+ /**
101
+ * Get the view name.
102
+ *
103
+ * @since 3.0.0
104
+ *
105
+ * @return string
106
+ */
107
+ public function get_name() {
108
+ return __( 'List', 'google-calendar-events' );
109
+ }
110
+
111
+ /**
112
+ * Add ajax actions.
113
+ *
114
+ * @since 3.0.0
115
+ */
116
+ public function add_ajax_actions() {
117
+ add_action( 'wp_ajax_simcal_default_calendar_draw_list', array( $this, 'draw_list_ajax' ) );
118
+ add_action( 'wp_ajax_nopriv_simcal_default_calendar_draw_list', array( $this, 'draw_list_ajax' ) );
119
+ }
120
+
121
+ /**
122
+ * Default calendar list scripts.
123
+ *
124
+ * Scripts to load when this view is displayed.
125
+ *
126
+ * @since 3.0.0
127
+ *
128
+ * @param string $min
129
+ *
130
+ * @return array
131
+ */
132
+ public function scripts( $min = '' ) {
133
+ return array(
134
+ 'simcal-qtip' => array(
135
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'js/vendor/qtip' . $min . '.js',
136
+ 'deps' => array( 'jquery' ),
137
+ 'ver' => '2.2.1',
138
+ 'in_footer' => true,
139
+ ),
140
+ 'simcal-default-calendar' => array(
141
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'js/default-calendar' . $min . '.js',
142
+ 'deps' => array(
143
+ 'jquery',
144
+ 'simcal-qtip',
145
+ ),
146
+ 'var' => SIMPLE_CALENDAR_VERSION,
147
+ 'in_footer' => true,
148
+ 'localize' => array(
149
+ 'simcal_default_calendar' => simcal_common_scripts_variables(),
150
+ ),
151
+ ),
152
+ );
153
+ }
154
+
155
+ /**
156
+ * Default calendar list styles.
157
+ *
158
+ * Stylesheets to load when this view is displayed.
159
+ *
160
+ * @since 3.0.0
161
+ *
162
+ * @param string $min = ''
163
+ *
164
+ * @return array
165
+ */
166
+ public function styles( $min = '' ) {
167
+ return array(
168
+ 'simcal-default-calendar-list' => array(
169
+ 'src' => SIMPLE_CALENDAR_ASSETS . 'css/default-calendar-list' . $min . '.css',
170
+ 'ver' => SIMPLE_CALENDAR_VERSION,
171
+ 'media' => 'all',
172
+ ),
173
+ );
174
+ }
175
+
176
+ /**
177
+ * Default calendar list markup.
178
+ *
179
+ * @since 3.0.0
180
+ */
181
+ public function html() {
182
+
183
+ $calendar = $this->calendar;
184
+
185
+ if ( $calendar instanceof Default_Calendar ) {
186
+
187
+ $disabled = $calendar->static === true ? ' disabled="disabled"' : '';
188
+
189
+ echo '<div class="simcal-calendar-list">';
190
+
191
+ echo '<nav class="simcal-calendar-head">' . "\n";
192
+
193
+ echo "\t" . '<div class="simcal-nav">' . "\n";
194
+ echo "\t\t" . '<button class="simcal-nav-button simcal-prev" title="' . __( 'Previous', 'google-calendar-events' ) . '"' . $disabled . '>' . "\n";
195
+ echo "\t\t\t" . '<i class="simcal-icon-left"></i>' . "\n";
196
+ echo "\t\t" . '</button>' . "\n";
197
+ echo "\t" . '</div>' . "\n";
198
+
199
+ echo "\t" . '<div class="simcal-nav simcal-current" data-calendar-current="' . $calendar->start . '">' . "\n";
200
+ echo "\t\t" . '<h3 class="simcal-current-label"> </h3>' . "\n";
201
+ echo "\t" . '</div>' . "\n";
202
+
203
+ echo "\t" . '<div class="simcal-nav">';
204
+ echo "\t\t" . '<button class="simcal-nav-button simcal-next" title="' . __( 'Next', 'google-calendar-events' ) . '"' . $disabled . '>';
205
+ echo "\t\t\t" . '<i class="simcal-icon-right"></i>' . "\n";
206
+ echo "\t\t" . '</button>' . "\n";
207
+ echo "\t" . '</div>' . "\n";
208
+
209
+ echo '</nav>' . "\n";
210
+
211
+ echo $this->draw_list( $calendar->start );
212
+
213
+ echo '<div class="simcal-ajax-loader simcal-spinner-top" style="display: none;"><i class="simcal-icon-spinner simcal-icon-spin"></i></div>';
214
+
215
+ echo '</div>';
216
+ }
217
+
218
+ }
219
+
220
+ /**
221
+ * Get events for current display.
222
+ *
223
+ * @since 3.0.0
224
+ * @access private
225
+ *
226
+ * @param int $timestamp
227
+ *
228
+ * @return array
229
+ */
230
+ private function get_events( $timestamp ) {
231
+
232
+ $calendar = $this->calendar;
233
+ $timezone = $calendar->timezone;
234
+
235
+ if ( ! $calendar->group_type || ! $calendar->group_span ) {
236
+ return array();
237
+ }
238
+
239
+ $current = Carbon::createFromTimestamp( $timestamp, $timezone );
240
+ $prev = clone $current;
241
+ $next = clone $current;
242
+
243
+ $this->start = $current->getTimestamp();
244
+
245
+ $interval = $span = max( absint( $calendar->group_span ), 1 );
246
+
247
+ if ( 'monthly' == $calendar->group_type ) {
248
+ $this->prev = $prev->subMonths( $span )->getTimestamp();
249
+ $this->next = $next->addMonths( $span )->getTimestamp();
250
+ } elseif ( 'weekly' == $calendar->group_type ) {
251
+ $week = new Carbon( $calendar->timezone );
252
+ $week->setTimestamp( $timestamp );
253
+ $week->setWeekStartsAt( $calendar->week_starts );
254
+ $this->prev = $prev->subWeeks( $span )->getTimestamp();
255
+ $this->next = $next->addWeeks( $span )->getTimestamp();
256
+ } elseif ( 'daily' == $calendar->group_type ) {
257
+ $this->prev = $prev->subDays( $span )->getTimestamp();
258
+ $this->next = $next->addDays( $span )->getTimestamp();
259
+ }
260
+
261
+ $events = $calendar->events;
262
+ $daily_events = $paged_events = $flattened_events = array();
263
+
264
+ if ( 'events' != $calendar->group_type ) {
265
+
266
+ $this->end = $this->next - 1;
267
+
268
+ $timestamps = array_keys( $events );
269
+ $lower_bound = array_filter( $timestamps, array( $this, 'filter_events_before' ) );
270
+ $higher_bound = array_filter( $lower_bound, array( $this, 'filter_events_after' ) );
271
+ $filtered = array_intersect_key( $events, array_combine( $higher_bound, $higher_bound ) );
272
+ foreach ( $filtered as $timestamp => $events ) {
273
+ $paged_events[ intval( $timestamp ) ] = $events;
274
+ }
275
+
276
+ } else {
277
+
278
+ foreach ( $events as $timestamp => $e ) {
279
+ $second = 0;
280
+ foreach ( $e as $event ) {
281
+ $flattened_events[ intval( $timestamp + $second ) ][] = $event;
282
+ $second++;
283
+ }
284
+ }
285
+ ksort( $flattened_events, SORT_NUMERIC );
286
+
287
+ $keys = array_keys( $flattened_events );
288
+ $current = 0;
289
+ foreach ( $keys as $timestamp ) {
290
+ if ( $timestamp <= $this->start ) {
291
+ $current++;
292
+ }
293
+ }
294
+
295
+ $paged_events = array_slice( $flattened_events, $current, $interval, true );
296
+
297
+ $events_end = isset( $keys[ $current + $interval ] ) ? $keys[ $current + $interval ] : $calendar->end;
298
+ $this->end = $events_end > $calendar->end ? $calendar->end : $events_end;
299
+ // -1 adjusts the interval count to index count, which starts at 0.
300
+ $this->prev = isset( $keys[ $current - $interval - 1 ] ) ? $keys[ $current - $interval - 1 ] : $calendar->earliest_event;
301
+ $this->next = isset( $keys[ $current + $interval - 1 ] ) ? $keys[ $current + $interval - 1 ] : $this->end;
302
+
303
+ }
304
+
305
+ // Put resulting events in an associative array, with Ymd date as key for easy retrieval in calendar days loop.
306
+ foreach ( $paged_events as $timestamp => $events ) {
307
+ if ( $timestamp < $this->end ) {
308
+ $date = Carbon::createFromTimestamp( $timestamp, $calendar->timezone )->endOfDay()->format( 'Ymd' );
309
+ $daily_events[ intval( $date ) ][] = $events;
310
+ }
311
+ }
312
+ ksort( $daily_events, SORT_NUMERIC );
313
+
314
+ return $daily_events;
315
+ }
316
+
317
+ /**
318
+ * Get calendar list heading.
319
+ *
320
+ * Parses calender date format and adapts to current display range.
321
+ *
322
+ * @since 3.0.0
323
+ * @access private
324
+ *
325
+ * @return array
326
+ */
327
+ private function get_heading() {
328
+
329
+ $calendar = $this->calendar;
330
+ $start = Carbon::createFromTimestamp( $this->start, $calendar->timezone );
331
+ $end = Carbon::createFromTimestamp( $this->end, $calendar->timezone );
332
+ $date_format = $this->calendar->date_format;
333
+ $date_order = simcal_get_date_format_order( $date_format );
334
+
335
+ if ( ( $start->day == $end->day ) && ( $start->month == $end->month ) && ( $start->year == $end->year ) ) {
336
+ // Start and end on the same day.
337
+ // e.g. 1 February 2020
338
+ $large = $small = date_i18n( $calendar->date_format , $this->start );
339
+ if ( ( $date_order['d'] !== false ) && ( $date_order['m'] !== false ) ) {
340
+ if ( $date_order['m'] > $date_order['d'] ) {
341
+ if ( $date_order['y'] !== false && $date_order['y'] > $date_order['m'] ) {
342
+ $small = date_i18n( 'Y, d M', $this->start );
343
+ } else {
344
+ $small = date_i18n( 'd M Y', $this->start );
345
+ }
346
+ } else {
347
+ if ( $date_order['y'] !== false && $date_order['y'] > $date_order['m'] ) {
348
+ $small = date_i18n( 'Y, M d', $this->start );
349
+ } else {
350
+ $small = date_i18n( 'M d Y', $this->start );
351
+ }
352
+ }
353
+ }
354
+ } elseif ( ( $start->month == $end->month ) && ( $start->year == $end->year ) ) {
355
+ // Start and end days on the same month.
356
+ // e.g. August 2020
357
+ if ( $date_order['y'] === false ) {
358
+ // August.
359
+ $large = $small = date_i18n( 'F', $this->start );
360
+ } else {
361
+ if ( $date_order['y'] < $date_order['m'] ) {
362
+ // 2020 August.
363
+ $large = date_i18n( 'Y F', $this->start );
364
+ $small = date_i18n( 'Y M', $this->start );
365
+ } else {
366
+ // August 2020.
367
+ $large = date_i18n( 'F Y', $this->start );
368
+ $small = date_i18n( 'M Y', $this->start );
369
+ }
370
+ }
371
+ } elseif ( $start->year == $end->year ) {
372
+ // Start and end days on months of the same year.
373
+ // e.g. August - September 2020
374
+ if ( $date_order['y'] === false ) {
375
+ // August - September.
376
+ $large = date_i18n( 'F', $this->start ) . ' - ' . date_i18n( 'F', $this->end );
377
+ $small = date_i18n( 'M', $this->start ) . ' - ' . date_i18n( 'M', $this->end );
378
+ } else {
379
+ if ( $date_order['y'] < $date_order['m'] ) {
380
+ // 2020, August - September.
381
+ $large = $small = date( 'Y', $this->start ) . ', ';
382
+ $large .= date_i18n( 'F', $this->start ) . ' - ' . date_i18n( 'F', $this->end );
383
+ $small .= date_i18n( 'M', $this->start ) . ' - ' . date_i18n( 'M', $this->end );
384
+ } else {
385
+ // August - September, 2020.
386
+ $large = date_i18n( 'F', $this->start ) . ' - ' . date_i18n( 'F', $this->end ) . ', ';
387
+ $small = date_i18n( 'M', $this->start ) . ' - ' . date_i18n( 'M', $this->end ) . ' ';
388
+ $year = date( 'Y', $this->start );
389
+ $large .= $year;
390
+ $small .= $year;
391
+ }
392
+ }
393
+ } else {
394
+ $large = $small = date( 'Y', $this->start ) . ' - ' . date( 'Y', $this->end );
395
+ }
396
+
397
+ return array(
398
+ 'small' => $small,
399
+ 'large' => $large,
400
+ );
401
+ }
402
+
403
+ /**
404
+ * Make a calendar list of events.
405
+ *
406
+ * Outputs a list of events according to events for the specified range.
407
+ *
408
+ * @since 3.0.0
409
+ * @access private
410
+ *
411
+ * @param int $timestamp
412
+ * @param int $id
413
+ *
414
+ * @return string
415
+ */
416
+ private function draw_list( $timestamp, $id = 0 ) {
417
+
418
+ $calendar = $this->calendar;
419
+
420
+ if ( empty( $calendar ) ) {
421
+ $calendar = $this->calendar = simcal_get_calendar( intval( $id ) );
422
+ if ( ! $calendar instanceof Default_Calendar ) {
423
+ return '';
424
+ }
425
+ }
426
+
427
+ date_default_timezone_set( $calendar->timezone );
428
+
429
+ $now = $calendar->now;
430
+ $current_events = $this->get_events( $timestamp );
431
+ $day_format = explode( ' ', $calendar->date_format );
432
+
433
+ ob_start();
434
+
435
+ // Draw the events.
436
+ $data_heading = '';
437
+ $heading = $this->get_heading();
438
+ foreach ( $heading as $k => $v ) {
439
+ $data_heading .= ' data-heading-' . $k . '="' . $v . '"';
440
+ }
441
+
442
+ echo '<div class="simcal-events-list-container"' .
443
+ ' data-prev="' . $this->prev . '"' .
444
+ ' data-next="' . $this->next . '"' .
445
+ $data_heading . '>';
446
+
447
+ if ( ! empty( $current_events ) && is_array( $current_events ) ) :
448
+
449
+ foreach ( $current_events as $ymd => $events ) :
450
+
451
+ $day_ts = Carbon::createFromFormat( 'Ymd', $ymd, $calendar->timezone )->getTimestamp();
452
+
453
+ $date = new Carbon( 'now', $calendar->timezone );
454
+ $date->setLocale( substr( get_locale(), 0, 2 ) );
455
+ $date->setTimestamp( $day_ts );
456
+
457
+ if ( $date->isToday() ) {
458
+ $the_color = new Color( $calendar->today_color );
459
+ } else {
460
+ $the_color = new Color( $calendar->days_events_color );
461
+ }
462
+
463
+ $bg_color = '#' . $the_color->getHex();
464
+ $color = $the_color->isDark() ? '#ffffff' : '#000000';
465
+ $border_style = ' style="border-bottom: 1px solid ' . $bg_color . ';" ';
466
+ $bg_style = ' style="background-color: ' . $bg_color . '; color: ' . $color . ';"';
467
+
468
+ echo "\t" . '<dt class="simcal-day-label"' . $border_style . '>';
469
+ echo '<span' . $bg_style .'>';
470
+ foreach ( $day_format as $format ) {
471
+ echo $format ? '<span class="simcal-date-format" data-date-format="' . $format . '">' . date_i18n( $format, $day_ts ) . '</span> ' : ' ';
472
+ }
473
+ echo '</span>';
474
+ echo '</dt>' . "\n";
475
+
476
+ $list_events = '<ul class="simcal-events">' . "\n";
477
+
478
+ $calendar_classes = array();
479
+ $day_classes = 'simcal-weekday-' . date( 'w', $day_ts );
480
+
481
+ // Is this the present, the past or the future, Doc?
482
+ if ( $timestamp <= $now && $timestamp >= $now ) {
483
+ $day_classes .= ' simcal-today simcal-present simcal-day';
484
+ } elseif ( $timestamp < $now ) {
485
+ $day_classes .= ' simcal-past simcal-day';
486
+ } elseif ( $this->end > $now ) {
487
+ $day_classes .= ' simcal-future simcal-day';
488
+ }
489
+
490
+ $count = 0;
491
+
492
+ foreach ( $events as $day_events ) :
493
+ foreach ( $day_events as $event ) :
494
+ if ( $event instanceof Event ) :
495
+
496
+ $event_classes = $event_visibility = '';
497
+
498
+ $calendar_class = 'simcal-events-calendar-' . strval( $event->calendar );
499
+ $calendar_classes[] = $calendar_class;
500
+
501
+ $recurring = $event->recurrence ? 'simcal-event-recurring ' : '';
502
+ $has_location = $event->venue ? 'simcal-event-has-location ' : '';
503
+
504
+ $event_classes .= 'simcal-event ' . $recurring . $has_location . $calendar_class;
505
+
506
+ // Toggle some events visibility if more than optional limit.
507
+ if ( ( $calendar->events_limit > - 1 ) && ( $count >= $calendar->events_limit ) ) :
508
+ $event_classes .= ' simcal-event-toggled';
509
+ $event_visibility = ' style="display: none"';
510
+ endif;
511
+
512
+ $event_color = '';
513
+ $bullet = '';
514
+ $event_color = $event->get_color();
515
+ if ( ! empty( $event_color ) ) {
516
+ $side = is_rtl() ? 'right' : 'left';
517
+ $event_color = ' style="border-' . $side . ': 4px solid ' . $event_color . '; padding-' . $side . ': 8px;"';
518
+ }
519
+
520
+ $list_events .= "\t" . '<li class="' . $event_classes . '"' . $event_visibility . $event_color . ' itemprop="event" itemscope itemtype="http://schema.org/Event">' . "\n";
521
+ $list_events .= "\t\t" . '<div class="simcal-event-details">' . $calendar->get_event_html( $event ) . '</div>' . "\n";
522
+ $list_events .= "\t" . '</li>' . "\n";
523
+
524
+ $count ++;
525
+
526
+ // Event falls within today.
527
+ if ( ( $this->end <= $now ) && ( $this->start >= $now ) ) :
528
+ $day_classes .= ' simcal-today-has-events';
529
+ endif;
530
+ $day_classes .= ' simcal-day-has-events simcal-day-has-' . strval( $count ) . '-events';
531
+
532
+ if ( $calendar_classes ) :
533
+ $day_classes .= ' ' . trim( implode( ' ', array_unique( $calendar_classes ) ) );
534
+ endif;
535
+
536
+ endif;
537
+ endforeach;
538
+ endforeach;
539
+
540
+ $list_events .= '</ul>' . "\n";
541
+
542
+ // If events visibility is limited, print the button toggle.
543
+ if ( ( $calendar->events_limit > -1 ) && ( $count > $calendar->events_limit ) ) :
544
+ $list_events .= '<button class="simcal-events-toggle"><i class="simcal-icon-down simcal-icon-animate"></i></button>';
545
+ endif;
546
+
547
+ // Print final list of events for the current day.
548
+ echo '<dd class="' . $day_classes . '" data-events-count="' . strval( $count ) . '">' . "\n";
549
+ echo "\t" . $list_events . "\n";
550
+ echo '</dd>' . "\n";
551
+
552
+ endforeach;
553
+
554
+ else :
555
+
556
+ echo "\t" . '<p>';
557
+
558
+ $message = get_post_meta( $calendar->id, '_no_events_message', true );
559
+
560
+ if ( 'events' == $calendar->group_type ) {
561
+ echo ! empty( $message ) ? $message : __( 'Nothing to show.', 'google-calendar-events' );
562
+ } else {
563
+ if ( ! empty( $message ) ) {
564
+ echo $message;
565
+ } else {
566
+ $from = Carbon::createFromTimestamp( $this->start, $calendar->timezone )->getTimestamp();
567
+ $to = Carbon::createFromTimestamp( $this->end, $calendar->timezone )->getTimestamp();
568
+ echo apply_filters( 'simcal_no_events_message', sprintf(
569
+ __( 'Nothing from %1$s to %2$s.', 'google-calendar-events' ),
570
+ date_i18n( $calendar->date_format, $from ),
571
+ date_i18n( $calendar->date_format, $to )
572
+ ), $calendar->id, $from, $to );
573
+ }
574
+ }
575
+
576
+ echo "\t" . '</p>' . "\n";
577
+
578
+ endif;
579
+
580
+ echo '</div>';
581
+
582
+ date_default_timezone_set( $calendar->site_timezone );
583
+
584
+ return ob_get_clean();
585
+ }
586
+
587
+ /**
588
+ * Ajax callback to request a new page.
589
+ *
590
+ * @since 3.0.0
591
+ */
592
+ public function draw_list_ajax() {
593
+
594
+ if ( isset( $_POST['ts'] ) && isset( $_POST['id'] ) ) {
595
+
596
+ $ts = absint( $_POST['ts'] );
597
+ $id = absint( $_POST['id'] );
598
+
599
+ wp_send_json_success( $this->draw_list( $ts, $id ) );
600
+
601
+ } else {
602
+
603
+ wp_send_json_error( 'Missing arguments in default calendar list ajax request.' );
604
+
605
+ }
606
+ }
607
+
608
+ /**
609
+ * Array filter callback.
610
+ *
611
+ * @since 3.0.0
612
+ * @access private
613
+ *
614
+ * @param int $event Timestamp.
615
+ *
616
+ * @return bool
617
+ */
618
+ private function filter_events_before( $event ) {
619
+ return intval( $event ) > intval( $this->start );
620
+ }
621
+
622
+ /**
623
+ * Array filter callback.
624
+ *
625
+ * @since 3.0.0
626
+ * @access private
627
+ *
628
+ * @param int $event Timestamp.
629
+ *
630
+ * @return bool
631
+ */
632
+ private function filter_events_after( $event ) {
633
+ return intval( $event ) < intval( $this->end );
634
+ }
635
+
636
+ }
includes/class-gce-display.php DELETED
@@ -1,413 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Class GCE_Display
5
- *
6
- * @package GCE
7
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
8
- * @license GPL-2.0+
9
- * @copyright 2014 Phil Derksen
10
- */
11
-
12
- class GCE_Display {
13
-
14
- public $id = '';
15
-
16
- public $title = '';
17
-
18
- public $sort = '';
19
-
20
- public $feeds = array();
21
-
22
- public $merged_feeds = array();
23
-
24
- public function __construct( $ids, $title_text = null, $sort_order = 'asc' ) {
25
-
26
- $this->id = $ids;
27
- $this->title = $title_text;
28
- $this->sort = $sort_order;
29
-
30
- foreach( $ids as $id ) {
31
- $this->feeds[$id] = new GCE_Feed( $id );
32
- }
33
-
34
- //Merge the feeds together into one array of events
35
- foreach ( $this->feeds as $feed_id => $feed ) {
36
- $this->merged_feeds = array_merge( $this->merged_feeds, $feed->events );
37
- }
38
-
39
- // Sort the items into date order
40
- if ( ! empty( $this->merged_feeds ) ) {
41
- usort( $this->merged_feeds, array( $this, 'compare' ) );
42
- }
43
- }
44
-
45
- /**
46
- * Comparison function for use when sorting merged feed data (with usort)
47
- *
48
- * @since 2.0.0
49
- */
50
- function compare( $event1, $event2 ) {
51
- //Sort ascending or descending
52
- if ( 'asc' == $this->sort )
53
- return $event1->start_time - $event2->start_time;
54
-
55
- return $event2->start_time - $event1->start_time;
56
- }
57
-
58
- /**
59
- * Returns array of days with events, with sub-arrays of events for that day
60
- *
61
- * @since 2.0.0
62
- */
63
- private function get_event_days() {
64
- $event_days = array();
65
-
66
- if ( $this->merged_feeds ) {
67
- foreach ( $this->merged_feeds as $event ) {
68
- foreach ( $event->get_days() as $day ) {
69
- $event_days[ $day ][] = $event;
70
- }
71
- }
72
- }
73
-
74
- return $event_days;
75
- }
76
-
77
- /**
78
- * Return the markup for the 'Grid' display
79
- *
80
- * @since 2.0.0
81
- */
82
- public function get_grid ( $year = null, $month = null, $widget = false, $paging = null ) {
83
- require_once 'php-calendar.php';
84
-
85
- $time_now = current_time( 'timestamp' );
86
-
87
- //If year and month have not been passed as paramaters, use current month and year
88
- if( ! isset( $year ) ) {
89
- $year = date( 'Y', $time_now );
90
- }
91
-
92
- if( ! isset( $month ) ) {
93
- $month = date( 'm', $time_now );
94
- }
95
-
96
- //Get timestamps for the start and end of current month
97
- $current_month_start = mktime( 0, 0, 0, date( 'm', $time_now ), 1, date( 'Y', $time_now ) );
98
- $current_month_end = mktime( 0, 0, 0, date( 'm', $time_now ) + 1, 1, date( 'Y', $time_now ) );
99
-
100
- //Get timestamps for the start and end of the month to be displayed in the grid
101
- $display_month_start = mktime( 0, 0, 0, $month, 1, $year );
102
- $display_month_end = mktime( 0, 0, 0, $month + 1, 1, $year );
103
-
104
- //It should always be possible to navigate to the current month, even if it doesn't have any events
105
- //So, if the display month is before the current month, set $nav_next to true, otherwise false
106
- //If the display month is after the current month, set $nav_prev to true, otherwise false
107
- $nav_next = ( $display_month_start < $current_month_start );
108
- $nav_prev = ( $display_month_start >= $current_month_end );
109
-
110
- //Get events data
111
- $event_days = $this->get_event_days();
112
-
113
- //Array of previous and next link stuff for use in gce_generate_calendar (below)
114
- foreach( $event_days as $key => $event_day ) {
115
- if( $paging === null ) {
116
- $paging = get_post_meta( $event_day[0]->feed->id, 'gce_paging', true );
117
- }
118
- }
119
-
120
- $start = mktime( 0, 0, 0, date( 'm', $time_now ), date( 'd', $time_now ), date( 'Y', $time_now ) );
121
-
122
- $i = 1;
123
- foreach ( $event_days as $key => $event_day ) {
124
-
125
- //If event day is in the month and year specified (by $month and $year)
126
- if ( $key >= $display_month_start && $key < $display_month_end ) {
127
-
128
- // Add gce-has-events
129
- $css_classes = array( 'gce-has-events' );
130
-
131
- //Create markup for display
132
- $markup = '<div class="gce-event-info">';
133
-
134
- //If title option has been set for display, add it
135
- if ( isset( $this->title ) ) {
136
- $markup .= '<div class="gce-tooltip-title">' . esc_html( $this->title ) . ' ' . date_i18n( $event_day[0]->feed->date_format, $key ) . '</div>';
137
- }
138
-
139
- $markup .= '<ul>';
140
-
141
- foreach ( $event_day as $num_in_day => $event ) {
142
-
143
- $feed_id = absint( $event->feed->id );
144
- $markup .= '<li class="gce-tooltip-feed-' . esc_attr( $feed_id ) . '">' . $event->get_event_markup( 'tooltip', $num_in_day, $i ) . '</li>';
145
-
146
- //Add CSS class for the feed from which this event comes. If there are multiple events from the same feed on the same day, the CSS class will only be added once.
147
- $css_classes['feed-' . $feed_id] = 'gce-feed-' . $feed_id;
148
-
149
- $i++;
150
- }
151
-
152
- $markup .= '</ul></div>';
153
-
154
- //If number of CSS classes is greater than 2 ('gce-has-events' plus one specific feed class) then there must be events from multiple feeds on this day, so add gce-multiple CSS class
155
- if ( count( $css_classes ) > 2 )
156
- $css_classes[] = 'gce-multiple';
157
-
158
- $count_events = count( $event_day );
159
- // Marks the count for the number of events happening this day.
160
- $css_classes[] = 'gce-has-' . strval( $count_events ) . '-events';
161
-
162
- //If event day is today, add gce-today CSS class, otherwise add past or future class
163
- if ( $key == $start ) {
164
- $css_classes[] = 'gce-today gce-today-has-events';
165
- } elseif ( $key < $start ) {
166
- $css_classes[] = 'gce-day-past';
167
- } else {
168
- $css_classes[] = 'gce-day-future';
169
- }
170
-
171
- //Change array entry to array of link href, CSS classes, and markup for use in gce_generate_calendar (below)
172
- $event_days[$key] = array( null, implode( ' ', $css_classes ), $markup );
173
- } elseif ( $key < $display_month_start ) {
174
- //This day is before the display month, so set $nav_prev to true. Remove the day from $event_days, as it's no use for displaying this month
175
- $nav_prev = true;
176
- unset( $event_days[$key] );
177
- } else {
178
- //This day is after the display month, so set $nav_next to true. Remove the day from $event_days, as it's no use for displaying this month
179
- $nav_next = true;
180
- unset( $event_days[$key] );
181
- }
182
- }
183
-
184
- //Ensures that gce-today CSS class is added even if there are no events for 'today'. A bit messy :(
185
- if ( ! isset( $event_days[$start] ) )
186
- $event_days[$start] = array( null, 'gce-today gce-today-no-events', null );
187
-
188
- $pn = array();
189
-
190
- if( $paging ) {
191
- // Add previous / next functionality
192
- //If there are events to display in a previous month, add previous month link
193
- $prev_key = __( 'Back', 'gce' );
194
- $prev = date( 'm-Y', mktime( 0, 0, 0, $month - 1, 1, $year ) );
195
-
196
- //If there are events to display in a future month, add next month link
197
- $next_key = __( 'Next', 'gce' );
198
- $next = date( 'm-Y', mktime( 0, 0, 0, $month + 1, 1, $year ) );
199
-
200
- //Array of previous and next link stuff for use in gce_generate_calendar (below)
201
- $pn = array( $prev_key => $prev, $next_key => $next );
202
- }
203
-
204
- $start_day = get_option( 'start_of_week' );
205
-
206
- //Generate the calendar markup and return it
207
- return gce_generate_calendar( $year, $month, $event_days, 1, null, $start_day, $pn, $widget );
208
- }
209
-
210
- /**
211
- * Return the markup for the 'List' display
212
- *
213
- * @since 2.0.0
214
- */
215
- public function get_list( $grouped = false, $start = null, $paging = null, $paging_interval = null, $start_offset = null, $max_events = null, $paging_type = null, $max_num = null ) {
216
- $paging_type = $paging_type;
217
-
218
- $max_length = null;
219
-
220
- if( $paging_type == 'events' ) {
221
- $max_length = 'events';
222
- }
223
-
224
- if( $start == null ) {
225
- $start = mktime( 0, 0, 0, date( 'm', current_time( 'timestamp' ) ), 1, date( 'Y', current_time( 'timestamp' ) ) );
226
- }
227
-
228
- // Get all the event days
229
- $event_days = $this->get_event_days();
230
-
231
- $an_event_feed_id = current( $event_days );
232
- $an_event_feed_id = $an_event_feed_id[0]->feed->id;
233
-
234
- if( $paging_interval == null ) {
235
- $max_num = get_post_meta( $an_event_feed_id, 'gce_per_page_num', true );
236
-
237
- if( $paging_type == null ) {
238
- $max_length = get_post_meta( $an_event_feed_id, 'gce_events_per_page', true );
239
- $paging_type = $max_length;
240
- }
241
- }
242
-
243
- $use_range = ( $paging_interval == 'date-range' ? true : false );
244
-
245
- if( $use_range ) {
246
- $max_length = 'date-range';
247
- }
248
-
249
- if( $paging === null ) {
250
- $paging = get_post_meta( $an_event_feed_id, 'gce_paging', true );
251
- }
252
-
253
- if( $start_offset === null ) {
254
- $start_offset_num = get_post_meta( $an_event_feed_id, 'gce_list_start_offset_num', true );
255
- $start_offset_direction = get_post_meta( $an_event_feed_id, 'gce_list_start_offset_direction', true );
256
- }
257
-
258
- if( empty( $max_num ) || $max_num == 0 ) {
259
- $max_num = 7;
260
- }
261
-
262
- if( $paging_type == 'days' ) {
263
- if( $paging_interval == null ) {
264
- $paging_interval = $max_num * 86400;
265
- }
266
- } else if( $paging_type == 'week' ) {
267
- $paging_interval = 604800;
268
-
269
- // Set week start here too
270
- $start_of_week = get_option( 'start_of_week' );
271
- $start = mktime( 0, 0, 0, date( 'm' ), ( date( 'j' ) - date( 'w' ) + $start_of_week ), date( 'Y' ) );
272
- } else if( $paging_type == 'month' ) {
273
- $paging_interval = 2629743;
274
-
275
- // Set month start here too
276
- $start = mktime( 0, 0, 0, date( 'm', current_time( 'timestamp' ) ), 1, date( 'Y', current_time( 'timestamp' ) ) );
277
- }
278
-
279
- if( $start_offset === null ) {
280
- if( $start_offset_direction == 'back' ) {
281
- $start_offset_direction = -1;
282
- } else {
283
- $start_offset_direction = 1;
284
- }
285
-
286
- $start_offset = $start_offset_num * 86400 * $start_offset_direction;
287
-
288
- $start = $start + $start_offset;
289
- }
290
-
291
- $start = mktime( 0, 0, 0, date( 'm', $start ), date( 'd', $start ), date( 'Y', $start ) );
292
-
293
- $end_time = $start + $paging_interval;
294
-
295
- $i = 1;
296
-
297
- $feeds = implode( $this->id, '-' );
298
-
299
- $markup = '<div class="gce-list" data-gce-start-offset="' . esc_attr( $start_offset ) . '" data-gce-start="' . esc_attr( ( $start + $paging_interval ) ) . '" data-gce-paging-interval="' . esc_attr( $paging_interval ) . '" data-gce-paging="' . esc_attr( $paging ) . '" data-gce-feeds="' . esc_attr( $feeds ) . '" data-gce-title="' . esc_attr( stripslashes( $this->title ) ) . '" data-gce-grouped="' . esc_attr( $grouped ) . '" data-gce-sort="' . esc_attr( $this->sort ) . '">' . "\n";
300
-
301
- if( ( $paging != 0 ) && $max_length != 'events' ) {
302
-
303
- $prev_text = __( 'Back', 'gce' );
304
- $next_text = __( 'Next', 'gce' );
305
-
306
- $prev_text = apply_filters( 'gce_prev_text', $prev_text );
307
- $next_text = apply_filters( 'gce_next_text', $next_text );
308
-
309
- $p = '<div class="gce-prev"><a href="#" class="gce-change-month-list" title="' . esc_attr__( 'Previous', 'gce' ) . '" data-gce-paging-direction="back" data-gce-paging-type="' . esc_attr( $paging_type ) . '">'. esc_html( $prev_text ) . '</a></div>';
310
- $n = '<div class="gce-next"><a href="#" class="gce-change-month-list" title="' . esc_attr__( 'Next', 'gce' ) . '" data-gce-paging-direction="forward" data-gce-paging-type="' . esc_attr( $paging_type ) . '">' . esc_html( $next_text ) . '</a></div>';
311
-
312
- $markup .= '<div class="gce-navbar">' .
313
- $p .
314
- $n .
315
- '<div class="gce-month-title"></div>' .
316
- '</div>' . "\n";
317
- }
318
- $max_count = 1;
319
- $has_events = false;
320
- $event_counter = 0;
321
-
322
- if( $max_length == 'events' ) {
323
- if( $start_offset === null || $start_offset == 0 ) {
324
- $time_now = current_time( 'timestamp' );
325
- } else {
326
-
327
- $time_now = current_time( 'timestamp' );
328
-
329
- $time_now = mktime( 0, 0, 0, date( 'm', $time_now ), date( 'j', $time_now ), date( 'Y', $time_now ) ) + $start_offset;
330
-
331
- }
332
-
333
-
334
- if( $max_events == null ) {
335
- $max_events = $max_num;
336
- }
337
- }
338
-
339
- if ( $grouped ) {
340
- $markup .=
341
- '<div class="gce-list-grouped">' ;
342
- }
343
-
344
- if( $max_length != 'events' ) {
345
- $max_events = INF;
346
- }
347
- else {
348
- $end_time = INF;
349
- }
350
-
351
- $show_title = true;
352
-
353
- foreach ( $event_days as $key => $event_day ) {
354
-
355
- $day_markup = '';
356
-
357
- foreach ( $event_day as $num_in_day => $event ) {
358
- //Create the markup for this event
359
- if( ( $max_length != 'events' && (( $event->start_time >= $start && // Condition for limited by days
360
- $event->end_time <= $end_time ) ||
361
- ( $event->day_type == 'MWD' &&
362
- $event->start_time >= $start &&
363
- $event->start_time <= $end_time )
364
- )
365
- ) ||
366
- ( $max_length == 'events' && ( $event->end_time >= $time_now && // Condition for limited by events
367
- $event_counter < $max_events )
368
- ) ||
369
- ( $max_length == 'date-range' )
370
- ) {
371
- if( $show_title && $grouped ) {
372
- $day_markup .= '<div class="gce-list-title">' . esc_html( stripslashes( $this->title ) ) . ' ' . date_i18n( $event->feed->date_format, $key ) . '</div>';
373
- $show_title = false;
374
- }
375
-
376
- $day_markup .=
377
- '<div class="gce-feed gce-feed-' . esc_attr( $event->feed->id ) . '">' .
378
- //If this isn't a grouped list, generate a per-event title with date.
379
- ( ( ! $grouped ) ? ( ( isset( $this->title ) && $this->title !== '' ) )
380
- ? '<div class="gce-list-title">' . esc_html( stripslashes( $this->title ) ) . ' ' . date_i18n( $event->feed->date_format, $event->start_time ) . '</div>'
381
- : '' : '' ) .
382
- //Add the event markup
383
- $event->get_event_markup( 'list', $num_in_day, $i ) .
384
- '</div>';
385
-
386
- $has_events = true;
387
- $i++;
388
- $event_counter++;
389
- }
390
- }
391
-
392
- if ( $day_markup != '' ) {
393
- $markup .= '<div class="gce-event-day">' . $day_markup . '</div>';
394
- }
395
-
396
- $show_title = true;
397
-
398
- $max_count++;
399
- }
400
-
401
- if ( $grouped ) {
402
- $markup .= '</div>';
403
- }
404
-
405
- if( ! $has_events ) {
406
- $markup .= apply_filters( 'gce_no_events_message_text', __( 'No events to display.', 'gce' ) );
407
- }
408
-
409
- $markup .= '</div>';
410
-
411
- return $markup;
412
- }
413
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class-gce-event.php DELETED
@@ -1,598 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Class GCE_Event
5
- *
6
- * @package GCE
7
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
8
- * @license GPL-2.0+
9
- * @copyright 2014 Phil Derksen
10
- */
11
-
12
- class GCE_Event {
13
-
14
- public $feed;
15
-
16
- /**
17
- * Class constructor
18
- *
19
- * @since 2.0.0
20
- */
21
- function __construct( GCE_Feed $feed, $id, $title, $description, $location, $start_time, $end_time, $link ) {
22
-
23
- $this->feed = $feed;
24
- $this->id = $id;
25
- $this->title = $title;
26
- $this->description = $description;
27
- $this->location = $location;
28
- $this->start_time = $start_time;
29
- $this->end_time = $end_time;
30
- $this->link = $link;
31
-
32
- //Calculate which day type this event is (SWD = single whole day, SPD = single part day, MWD = multiple whole day, MPD = multiple part day)
33
- if ( ( $start_time + 86400 ) <= $end_time ) {
34
- if ( ( $start_time + 86400 ) == $end_time ) {
35
- $this->day_type = 'SWD';
36
- } else {
37
- if ( ( '12:00 am' == date( 'g:i a', $start_time ) ) && ( '12:00 am' == date( 'g:i a', $end_time ) ) ) {
38
- if( $end_time - $start_time > 86400 ) {
39
- $this->day_type = 'MWD';
40
- } else {
41
- $this->day_type = 'SWD';
42
- }
43
- } else {
44
- $this->day_type = 'MPD';
45
- }
46
- }
47
- } else {
48
- if( date( 'D', $start_time ) == date( 'D', $end_time ) ) {
49
- $this->day_type = 'SPD';
50
- } else {
51
- $this->day_type = 'MPD';
52
- }
53
- }
54
- }
55
-
56
- /**
57
- * Returns an array of days (as UNIX timestamps) that this events spans
58
- *
59
- * @since 2.0.0
60
- */
61
- function get_days() {
62
-
63
- if( $this->start_time !== null ) {
64
- //Round start date to nearest day
65
- $start_time = mktime( 0, 0, 0, date( 'm', $this->start_time ), date( 'd', $this->start_time ) , date( 'Y', $this->start_time ) );
66
-
67
- $days = array();
68
-
69
- //If multiple day events should be handled, and this event is a multi-day event, add multiple day event to required days
70
- if ( $this->feed->multiple_day_events && ( 'MPD' == $this->day_type || 'MWD' == $this->day_type ) ) {
71
- $on_next_day = true;
72
- $next_day = $start_time;
73
-
74
- while ( $on_next_day ) {
75
- //If the end time of the event is after 00:00 on the next day (therefore, not doesn't end on this day)
76
- if ( $this->end_time > $next_day ) {
77
- $days[] = $next_day;
78
- } else {
79
- $on_next_day = false;
80
- }
81
- $next_day += 86400;
82
- }
83
- } else {
84
- //Add event into array of events for that day
85
- $days[] = $start_time;
86
- }
87
-
88
- return $days;
89
- } else {
90
- return array();
91
- }
92
- }
93
-
94
- /**
95
- * Returns the markup for this event, so that it can be used in the construction of a grid / list
96
- *
97
- * @since 2.0.0
98
- */
99
- function get_event_markup( $display_type, $num_in_day, $num ) {
100
- //Set the display type (either tooltip or list)
101
- $this->type = $display_type;
102
-
103
- //Set which number event this is in day (first in day etc)
104
- $this->num_in_day = $num_in_day;
105
-
106
- //Set the position of this event in array of events currently being processed
107
- $this->pos = $num;
108
-
109
- $this->time_now = current_time( 'timestamp' );
110
-
111
- // First check if we use the builder or not
112
- $use_simple = get_post_meta( $this->feed->id, 'gce_display_simple', true );
113
-
114
- if( empty( $use_simple ) ) {
115
- return $this->use_builder();
116
- }
117
-
118
- // Setup the markup to return
119
- $display_options['display_start'] = get_post_meta( $this->feed->id, 'gce_display_start', true );
120
- $display_options['display_start_text'] = get_post_meta( $this->feed->id, 'gce_display_start_text', true );
121
- $display_options['display_end'] = get_post_meta( $this->feed->id, 'gce_display_end', true );
122
- $display_options['display_end_text'] = get_post_meta( $this->feed->id, 'gce_display_end_text', true );
123
- $display_options['display_location'] = get_post_meta( $this->feed->id, 'gce_display_location', true );
124
- $display_options['display_location_text'] = get_post_meta( $this->feed->id, 'gce_display_location_text', true );
125
- $display_options['display_desc'] = get_post_meta( $this->feed->id, 'gce_display_description', true );
126
- $display_options['display_desc_text'] = get_post_meta( $this->feed->id, 'gce_display_description_text', true );
127
- $display_options['display_desc_limit'] = get_post_meta( $this->feed->id, 'gce_display_description_max', true );
128
- $display_options['display_link'] = get_post_meta( $this->feed->id, 'gce_display_link', true );
129
- $display_options['display_link_text'] = get_post_meta( $this->feed->id, 'gce_display_link_text', true );
130
- $display_options['display_separator'] = get_post_meta( $this->feed->id, 'gce_display_separator', true );
131
- $display_options['display_link_target'] = get_post_meta( $this->feed->id, 'gce_display_link_tab', true );
132
-
133
- $markup = '<p class="gce-' . esc_attr( $this->type ) . '-event">' . esc_html( $this->title ) . '</p>';
134
-
135
- $start_end = array();
136
-
137
- //If start date / time should be displayed, set up array of start date and time
138
- if ( ! empty( $display_options['display_start'] ) && 'none' != $display_options['display_start'] ) {
139
- $sd = $this->start_time;
140
- $start_end['start'] = array(
141
- 'time' => date_i18n( $this->feed->time_format, $sd ),
142
- 'date' => date_i18n( $this->feed->date_format, $sd )
143
- );
144
- }
145
-
146
- //If end date / time should be displayed, set up array of end date and time
147
- if ( ! empty( $display_options['display_end'] ) && 'none' != $display_options['display_end'] ) {
148
- $ed = $this->end_time;
149
- $start_end['end'] = array(
150
- 'time' => date_i18n( $this->feed->time_format, $ed ),
151
- 'date' => date_i18n( $this->feed->date_format, $ed )
152
- );
153
- }
154
-
155
- //Add the correct start / end, date / time information to $markup
156
- foreach ( $start_end as $start_or_end => $info ) {
157
- $markup .= '<p class="gce-' . esc_attr( $this->type ) . '-' . $start_or_end . '"><span>' . esc_html( $display_options['display_' . $start_or_end . '_text'] ) . '</span> ';
158
-
159
- if( ! empty( $display_options['display_' . $start_or_end] ) ) {
160
- switch ( $display_options['display_' . $start_or_end] ) {
161
- case 'time': $markup .= esc_html( $info['time'] );
162
- break;
163
- case 'date': $markup .= esc_html( $info['date'] );
164
- break;
165
- case 'time-date': $markup .= esc_html( $info['time'] . $display_options['display_separator'] . $info['date'] );
166
- break;
167
- case 'date-time': $markup .= esc_html( $info['date'] . $display_options['display_separator'] . $info['time'] );
168
- }
169
- }
170
-
171
- $markup .= '</p>';
172
- }
173
-
174
- //If location should be displayed (and is not empty) add to $markup
175
- if ( ! empty( $display_options['display_location'] ) ) {
176
- $event_location = $this->location;
177
- if ( '' != $event_location )
178
- $markup .= '<p class="gce-' . esc_attr( $this->type ) . '-loc"><span>' . esc_html( $display_options['display_location_text'] ) . '</span> ' . esc_html( $event_location ) . '</p>';
179
- }
180
-
181
- //If description should be displayed (and is not empty) add to $markup
182
- if ( ! empty( $display_options['display_desc'] ) ) {
183
- $event_desc = $this->description;
184
-
185
- if ( '' != $event_desc ) {
186
- //Limit number of words of description to display, if required
187
- if ( '' != $display_options['display_desc_limit'] ) {
188
- preg_match( '/([\S]+\s*){0,' . $display_options['display_desc_limit'] . '}/', $this->description, $event_desc );
189
- $event_desc = trim( $event_desc[0] );
190
- }
191
-
192
- $markup .= '<p class="gce-' . esc_attr( $this->type ) . '-desc"><span>' . esc_html( $display_options['display_desc_text'] ) . '</span> ' . make_clickable( nl2br( esc_html( $event_desc ) ) ) . '</p>';
193
- }
194
- }
195
-
196
- //If link should be displayed add to $markup
197
- if ( ! empty( $display_options['display_link'] ) ) {
198
-
199
- $target = ( ! empty( $display_options['display_link_target'] ) ? 'target="blank"' : '' );
200
-
201
- $ctz = gce_get_wp_timezone();
202
-
203
- if ( isset( $this->feed->id ) ) {
204
- $tz_option = esc_attr( get_post_meta( $this->feed->id, '_feed_timezone_setting', true ) );
205
- if ( 'use_calendar' == $tz_option ) {
206
- $ctz = '';
207
- }
208
- }
209
-
210
- // Check if it is a hangouts link first
211
- if( strpos( $this->link, 'plus.google.com/events/' ) !== false ) {
212
- $link = $this->link;
213
- } else {
214
- $link = $this->link . ( ! empty( $ctz ) ? '&ctz=' . $ctz : '' );
215
- }
216
-
217
- $markup .= '<p class="gce-' . esc_attr( $this->type ) . '-link"><a href="' . esc_url( $link ) . '" ' . esc_attr( $target ) . '>' . esc_html( $display_options['display_link_text'] ) . '</a></p>';
218
- }
219
-
220
- return $markup;
221
-
222
- }
223
-
224
- //Return the event markup using the builder
225
- function use_builder() {
226
- //Array of valid shortcodes
227
- $shortcodes = array(
228
- //Event / feed information shortcodes
229
-
230
- 'event-title', //The event title
231
- 'start-time', //The start time of the event (uses the time format from the feed options, if it is set. Otherwise uses the default WordPress time format)
232
- 'start-date', //The start date of the event (uses the date format from the feed options, if it is set. Otherwise uses the default WordPress date format)
233
- 'start-custom', //The start time / date of the event (uses a custom PHP date format, specified in the 'format' attribute)
234
- 'start-human', //The difference between the start time of the event and the time now, in human-readable format, such as '1 hour', '4 days', '15 mins'
235
- 'end-time', //The end time of the event (uses the time format from the feed options, if it is set. Otherwise uses the default WordPress time format)
236
- 'end-date', //The end date of the event (uses the date format from the feed options, if it is set. Otherwise uses the default WordPress date format)
237
- 'end-custom', //The end time / date of the event (uses a custom PHP date format, specified in the 'format' attribute)
238
- 'end-human', //The difference between the end time of the event and the time now, in human-readable format, such as '1 hour', '4 days', '15 mins'
239
- 'location', //The event location
240
- 'description', //The event deescription (number of words can be limited by the 'limit' attribute)
241
- 'link', //Anything within this shortcode (including further shortcodes) will be linked to the Google Calendar page for this event
242
- 'url', //The raw link URL to the Google Calendar page for this event (can be used to construct more customized links)
243
- 'feed-id', //The ID of this feed (Can be useful for constructing feed specific CSS classes)
244
- 'feed-title', //The feed title
245
- 'maps-link', //Anything within this shortcode (including further shortcodes) will be linked to a Google Maps page based on whatever is specified for the event location
246
- 'length', //How long the events lasts, in human-readable format
247
- 'event-num', //The position of the event in the current list, or the position of the event in the current month (for grids)
248
- 'event-id', //The event UID (unique identifier assigned by Google)
249
- 'cal-id', //The calendar ID
250
-
251
- //Anything between the opening and closing tags of the following logical shortcodes (including further shortcodes) will only be displayed if:
252
-
253
- 'if-all-day', //This is an all-day event
254
- 'if-not-all-day', //This is not an all-day event
255
- 'if-title', //The event has a title
256
- 'if-description', //The event has a description
257
- 'if-location', //The event has a location
258
- 'if-not-location',//The event does not have a location
259
- 'if-tooltip', //The current display type is 'tooltip'
260
- 'if-list', //The current display type is 'list'
261
- 'if-now', //The event is taking place now (after the start time, but before the end time)
262
- 'if-not-now', //The event is not taking place now (may have ended or not yet started)
263
- 'if-started', //The event has started (and even if it has ended)
264
- 'if-not-started', //The event has not yet started
265
- 'if-ended', //The event has ended
266
- 'if-not-ended', //The event has not ended (and even if it hasn't started)
267
- 'if-first', //The event is the first in the day
268
- 'if-not-first', //The event is not the first in the day
269
- 'if-multi-day', //The event spans multiple days
270
- 'if-single-day' //The event does not span multiple days
271
- );
272
-
273
- $this->regex = '/(.?)\[(' . implode( '|', $shortcodes ) . ')\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?(.?)/s';
274
-
275
- return $this->look_for_shortcodes( $this->feed->get_builder() );
276
- }
277
-
278
- //Look through the EDB markup for shortcodes
279
- function look_for_shortcodes( $markup ) {
280
- return preg_replace_callback( $this->regex, array( $this, 'process_shortcode' ), $markup );
281
- }
282
-
283
- //Parse a shortcode, returning the appropriate event information
284
- //Much of this code is 'borrowed' from WordPress' own shortcode handling stuff!
285
- function process_shortcode( $m ) {
286
- if ( '[' == $m[1] && ']' == $m[6] )
287
- return substr( $m[0], 1, -1 );
288
-
289
- //Extract any attributes contained in the shortcode
290
- extract( shortcode_atts( array(
291
- 'newwindow' => 'false',
292
- 'format' => '',
293
- 'limit' => '0',
294
- 'html' => 'false',
295
- 'markdown' => 'false',
296
- 'precision' => '1',
297
- 'offset' => '0',
298
- 'autolink' => 'true'
299
- ), shortcode_parse_atts( $m[3] ) ) );
300
-
301
- //Sanitize the attributes
302
- $newwindow = ( 'true' === $newwindow );
303
- $format = esc_attr( $format );
304
- $limit = absint( $limit );
305
- $html = ( 'true' === $html );
306
- $markdown = ( 'true' === $markdown );
307
- $precision = absint( $precision );
308
- $offset = intval( $offset );
309
- $autolink = ( 'true' === $autolink );
310
-
311
- //Do the appropriate stuff depending on which shortcode we're looking at. See valid shortcode list (above) for explanation of each shortcode
312
- switch ( $m[2] ) {
313
- case 'event-title':
314
- $title = esc_html( trim( $this->title ) );
315
-
316
- if ( $markdown && function_exists( 'Markdown' ) )
317
- $title = Markdown( $title );
318
-
319
- if ( $html )
320
- $title = wp_kses_post( html_entity_decode( $title ) );
321
-
322
- return $m[1] . $title . $m[6];
323
-
324
- case 'start-time':
325
- return $m[1] . date_i18n( $this->feed->time_format, $this->start_time + $offset ) . $m[6];
326
-
327
- case 'start-date':
328
- return $m[1] . date_i18n( $this->feed->date_format, $this->start_time + $offset ) . $m[6];
329
-
330
- case 'start-custom':
331
- return $m[1] . date_i18n( $format, $this->start_time + $offset ) . $m[6];
332
-
333
- case 'start-human':
334
- return $m[1] . $this->gce_human_time_diff( $this->start_time + $offset, $this->time_now, $precision ) . $m[6];
335
-
336
- case 'end-time':
337
- return $m[1] . date_i18n( $this->feed->time_format, $this->end_time + $offset ) . $m[6];
338
-
339
- case 'end-date':
340
- return $m[1] . date_i18n( $this->feed->date_format, $this->end_time + $offset ) . $m[6];
341
-
342
- case 'end-custom':
343
- return $m[1] . date_i18n( $format, $this->end_time + $offset ) . $m[6];
344
-
345
- case 'end-human':
346
- return $m[1] . $this->gce_human_time_diff( $this->end_time + $offset, $this->time_now, $precision ) . $m[6];
347
-
348
- case 'location':
349
- $location = esc_html( trim( $this->location ) );
350
-
351
- if ( $markdown && function_exists( 'Markdown' ) )
352
- $location = Markdown( $location );
353
-
354
- if ( $html )
355
- $location = wp_kses_post( html_entity_decode( $location ) );
356
-
357
- return $m[1] . $location . $m[6];
358
-
359
- case 'description':
360
-
361
- $description = trim( $this->description );
362
-
363
- if ( $limit > 0 ) {
364
- if ( str_word_count( $description, 0 ) > $limit ) {
365
- $words = str_word_count( $description, 2 );
366
- $pos = array_keys( $words );
367
- $description = substr( $description, 0, $pos[ $limit ] ) . '...';
368
- }
369
- //preg_match( '/([\S]+\s*){0,' . $limit . '}/', $this->description, $description );
370
- //$description = trim( $description[0] );
371
- }
372
-
373
- if ( $markdown || $html ) {
374
-
375
- if ( $markdown && function_exists( 'Markdown' ) ) {
376
- $description = Markdown( $description );
377
- }
378
-
379
- if ( $html ) {
380
- $description = wp_kses_post( $description );
381
- }
382
-
383
- } else {
384
-
385
- $description = nl2br( esc_html( $description ) );
386
-
387
- }
388
-
389
- if ( $autolink ) {
390
- $description = make_clickable( $description );
391
- }
392
-
393
- return $m[1] . $description . $m[6];
394
-
395
- case 'link':
396
-
397
- $new_window = ( $newwindow ) ? ' target="_blank"' : '';
398
-
399
- $ctz = gce_get_wp_timezone();
400
-
401
- if ( isset( $this->feed->id ) ) {
402
- $tz_option = esc_attr( get_post_meta( $this->feed->id, '_feed_timezone_setting', true ) );
403
- if ( 'use_calendar' == $tz_option ) {
404
- $ctz = '';
405
- }
406
- }
407
-
408
- // Check if it is a hangouts link first
409
- if( strpos( $this->link, 'plus.google.com/events/' ) !== false ) {
410
- $link = $this->link;
411
- } else {
412
- $link = $this->link . ( ! empty( $ctz ) ? '&ctz=' . $ctz : '' );
413
- }
414
-
415
- return $m[1] . '<a href="' . esc_url( $link ) . '"' . $new_window . '>' . $this->look_for_shortcodes( $m[5] ) . '</a>' . $m[6];
416
-
417
- case 'url':
418
- return $m[1] . esc_url( $this->link ) . $m[6];
419
-
420
- case 'feed-id':
421
- return $m[1] . intval( $this->feed->id ) . $m[6];
422
-
423
- case 'feed-title':
424
- return $m[1] . esc_html( $this->feed->title ) . $m[6];
425
-
426
- case 'maps-link':
427
- $new_window = ( $newwindow ) ? ' target="_blank"' : '';
428
- return $m[1] . '<a href="' . esc_url( '//maps.google.com?q=' . urlencode( $this->location ) ) . '"' . $new_window . '>' . $this->look_for_shortcodes( $m[5] ) . '</a>' . $m[6];
429
-
430
- case 'length':
431
- return $m[1] . $this->gce_human_time_diff( $this->start_time, $this->end_time, $precision ) . $m[6];
432
-
433
- case 'event-num':
434
- return $m[1] . intval( $this->pos ) . $m[6];
435
-
436
- case 'event-id':
437
- return $m[1] . esc_html( $this->id ) . $m[6];
438
-
439
- case 'cal-id':
440
- //$cal_id = explode( '/', $this->feed->feed_url );
441
- return $m[1] . $this->feed->calendar_id . $m[6];
442
-
443
- case 'if-all-day':
444
- if ( 'SWD' == $this->day_type || 'MWD' == $this->day_type )
445
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
446
-
447
- return '';
448
-
449
- case 'if-not-all-day':
450
- if ( 'SPD' == $this->day_type || 'MPD' == $this->day_type )
451
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
452
-
453
- return '';
454
-
455
- case 'if-title':
456
- if ( '' != $this->title )
457
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
458
-
459
- return '';
460
-
461
- case 'if-description':
462
- if ( '' != $this->description )
463
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
464
-
465
- return '';
466
-
467
- case 'if-location':
468
- if ( '' != $this->location )
469
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
470
-
471
- return '';
472
-
473
- case 'if-not-location':
474
- if ( '' == $this->location )
475
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
476
-
477
- return '';
478
-
479
- case 'if-tooltip':
480
- if ( 'tooltip' == $this->type )
481
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
482
-
483
- return '';
484
-
485
- case 'if-list':
486
- if ( 'list' == $this->type )
487
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
488
-
489
- return '';
490
-
491
- case 'if-now':
492
- if ( $this->time_now >= $this->start_time && $this->time_now < $this->end_time )
493
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
494
-
495
- return '';
496
-
497
- case 'if-not-now':
498
- if ( $this->end_time < $this->time_now || $this->start_time > $this->time_now )
499
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
500
-
501
- return '';
502
-
503
- case 'if-started':
504
- if ( $this->start_time < $this->time_now )
505
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
506
-
507
- return '';
508
-
509
- case 'if-not-started':
510
- if ( $this->start_time > $this->time_now )
511
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
512
-
513
- return '';
514
-
515
- case 'if-ended':
516
- if ( $this->end_time < $this->time_now )
517
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
518
-
519
- return '';
520
-
521
- case 'if-not-ended':
522
- if ( $this->end_time > $this->time_now )
523
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
524
-
525
- return '';
526
-
527
- case 'if-first':
528
- if ( 0 == $this->num_in_day )
529
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
530
-
531
- return '';
532
-
533
- case 'if-not-first':
534
- if ( 0 != $this->num_in_day )
535
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
536
-
537
- return '';
538
-
539
- case 'if-multi-day':
540
- if ( 'MPD' == $this->day_type || 'MWD' == $this->day_type )
541
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
542
-
543
- return '';
544
-
545
- case 'if-single-day':
546
- if ( 'SPD' == $this->day_type || 'SWD' == $this->day_type )
547
- return $m[1] . $this->look_for_shortcodes( $m[5] ) . $m[6];
548
-
549
- return '';
550
- }
551
- }
552
-
553
- //Returns the difference between two times in human-readable format. Based on a patch for human_time_diff posted in the WordPress trac (http://core.trac.wordpress.org/ticket/9272) by Viper007Bond
554
- function gce_human_time_diff( $from, $to = '', $limit = 1 ) {
555
- $units = array(
556
- 31556926 => array( __( '%s year', 'gce' ), __( '%s years', 'gce' ) ),
557
- 2629744 => array( __( '%s month', 'gce' ), __( '%s months', 'gce' ) ),
558
- 604800 => array( __( '%s week', 'gce' ), __( '%s weeks', 'gce' ) ),
559
- 86400 => array( __( '%s day', 'gce' ), __( '%s days', 'gce' ) ),
560
- 3600 => array( __( '%s hour', 'gce' ), __( '%s hours', 'gce' ) ),
561
- 60 => array( __( '%s min', 'gce' ), __( '%s mins', 'gce' ) ),
562
- );
563
-
564
- if ( empty( $to ) )
565
- $to = time();
566
-
567
- $from = (int) $from;
568
- $to = (int) $to;
569
- $diff = (int) abs( $to - $from );
570
-
571
- $items = 0;
572
- $output = array();
573
-
574
- foreach ( $units as $unitsec => $unitnames ) {
575
- if ( $items >= $limit )
576
- break;
577
-
578
- if ( $diff < $unitsec )
579
- continue;
580
-
581
- $numthisunits = floor( $diff / $unitsec );
582
- $diff = $diff - ( $numthisunits * $unitsec );
583
- $items++;
584
-
585
- if ( $numthisunits > 0 )
586
- $output[] = sprintf( _n( $unitnames[0], $unitnames[1], $numthisunits, 'gce' ), $numthisunits );
587
- }
588
-
589
- $seperator = _x( ', ', 'human_time_diff', 'gce' );
590
-
591
- if ( ! empty( $output ) ) {
592
- return implode( $seperator, $output );
593
- } else {
594
- $smallest = array_pop( $units );
595
- return sprintf( $smallest[0], 1 );
596
- }
597
- }
598
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class-gce-feed.php DELETED
@@ -1,344 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Class GCE_Feed
5
- *
6
- * @package GCE
7
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
8
- * @license GPL-2.0+
9
- * @copyright 2014 Phil Derksen
10
- */
11
-
12
- class GCE_Feed {
13
-
14
- public $id,
15
- $calendar_id,
16
- $feed_url,
17
- $date_format,
18
- $time_format,
19
- $cache,
20
- $multiple_day_events,
21
- $display_url,
22
- $search_query,
23
- $expand_recurring,
24
- $title,
25
- $feed_start,
26
- $feed_end,
27
- $builder;
28
-
29
- public $events = array();
30
-
31
- // Google API Key
32
- private $api_key = 'AIzaSyAssdKVved1mPVY0UJCrx96OUOF9u17AuY';
33
-
34
- /**
35
- * Class constructor
36
- *
37
- * @since 2.0.0
38
- */
39
- public function __construct( $id ) {
40
- // Set the ID
41
- $this->id = $id;
42
-
43
- $this->calendar_id = get_post_meta( $this->id, 'gce_feed_url', true );
44
-
45
- // Set up all other data based on the ID
46
- $this->setup_attributes();
47
-
48
- // Now create the Feed
49
- $this->create_feed();
50
- }
51
-
52
- /**
53
- * Set the transient to cache the events
54
- *
55
- * @since 2.0.0
56
- */
57
- private function cache_events() {
58
- set_transient( 'gce_feed_' . $this->id, $this->events, $this->cache );
59
- }
60
-
61
- /**
62
- * Set all of the feed attributes from the post meta options
63
- *
64
- * @since 2.0.0
65
- */
66
- private function setup_attributes() {
67
- $date_format = get_post_meta( $this->id, 'gce_date_format', true );
68
- $time_format = get_post_meta( $this->id, 'gce_time_format', true );
69
-
70
- $this->feed_url = get_post_meta( $this->id, 'gce_feed_url', true );
71
- $this->date_format = ( ! empty( $date_format ) ? $date_format : get_option( 'date_format' ) );
72
- $this->time_format = ( ! empty( $time_format ) ? $time_format : get_option( 'time_format' ) );
73
- $this->cache = get_post_meta( $this->id, 'gce_cache', true );
74
- $this->multiple_day_events = get_post_meta( $this->id, 'gce_multi_day_events', true );
75
- $this->search_query = get_post_meta( $this->id, 'gce_search_query', true );
76
- $this->expand_recurring = get_post_meta( $this->id, 'gce_expand_recurring', true );
77
- $this->title = get_the_title( $this->id );
78
- $this->feed_start = $this->get_feed_start();
79
- $this->feed_end = $this->get_feed_end();
80
- }
81
-
82
- /**
83
- * Create the feed URL
84
- *
85
- * @since 2.0.0
86
- */
87
- private function create_feed() {
88
- //Break the feed URL up into its parts (scheme, host, path, query)
89
-
90
- global $gce_options;
91
-
92
- if( empty( $this->feed_url ) ) {
93
- if( current_user_can( 'manage_options' ) ) {
94
- echo '<p>' . __( 'The Google Calendar ID has not been set. Please make sure to set it correctly in the Feed settings.', 'gce' ) . '</p>';
95
- }
96
-
97
- return;
98
- }
99
-
100
- $args = array();
101
-
102
- if( ! empty( $gce_options['api_key'] ) ) {
103
- $api_key = urlencode( $gce_options['api_key'] );
104
- } else {
105
- $api_key = $this->api_key;
106
- }
107
-
108
- $query = 'https://www.googleapis.com/calendar/v3/calendars/' . $this->calendar_id . '/events';
109
-
110
- // Set API key
111
- $query .= '?key=' . $api_key;
112
-
113
- // Timezone.
114
- $timezone_option = esc_attr( get_post_meta( $this->id, '_feed_timezone_setting', true ) );
115
- $timezone = gce_get_wp_timezone();
116
- if ( 'use_site' == $timezone_option ) {
117
- $args['timeZone'] = $timezone;
118
- }
119
-
120
- // Time boundaries.
121
- if ( version_compare( PHP_VERSION, '5.3.0' ) === -1 ) {
122
-
123
- $ts = $this->feed_start;
124
- $time = new DateTime( "@$ts" );
125
- if ( 'use_site' == $timezone_option ) {
126
- $time->setTimezone( new DateTimeZone( $timezone ) );
127
- }
128
- $args['timeMin'] = urlencode( $time->format( 'c' ) );
129
-
130
- $ts = $this->feed_end;
131
- $time = new DateTime( "@$ts" );
132
- if ( 'use_site' == $timezone_option ) {
133
- $time->setTimezone( new DateTimeZone( $timezone ) );
134
- }
135
- $args['timeMax'] = urlencode( $time->format( 'c' ) );
136
-
137
- } else {
138
-
139
- $time = new DateTime();
140
-
141
- if ( 'use_site' == $timezone_option ) {
142
- $time->setTimezone( new DateTimeZone( $timezone ) );
143
- }
144
-
145
- $time->setTimestamp( $this->feed_start );
146
- $args['timeMin'] = urlencode( $time->format( 'c' ) );
147
- $time->setTimestamp( $this->feed_end );
148
- $args['timeMax'] = urlencode( $time->format( 'c' ) );
149
-
150
- }
151
-
152
- // Max no. of events.
153
- $args['maxResults'] = 2500;
154
-
155
- // Google search query terms.
156
- if ( ! empty( $this->search_query ) ) {
157
- $args['q'] = rawurlencode( $this->search_query );
158
- }
159
-
160
- // Show recurring.
161
- if( ! empty( $this->expand_recurring ) ) {
162
- $args['singleEvents'] = 'true';
163
- }
164
-
165
- $query = esc_url_raw( add_query_arg( $args, $query ) );
166
-
167
- $this->display_url = $query;
168
-
169
- if( isset( $_GET['gce_debug'] ) && $_GET['gce_debug'] == true ) {
170
- echo '<pre>' . $this->display_url . '</pre><br>';
171
- }
172
-
173
- $this->get_feed_data( $query );
174
- }
175
-
176
- /**
177
- * Make remote call to get the feed data
178
- *
179
- * @since 2.0.0
180
- */
181
- private function get_feed_data( $url ) {
182
-
183
- // First check for transient data to use
184
- if( false !== get_transient( 'gce_feed_' . $this->id ) ) {
185
- $this->events = get_transient( 'gce_feed_' . $this->id );
186
- } else {
187
- $raw_data = wp_remote_get( $url, array( 'timeout' => 10 ) );
188
- //If $raw_data is a WP_Error, something went wrong
189
- if ( ! is_wp_error( $raw_data ) ) {
190
- //Attempt to convert the returned JSON into an array
191
- $raw_data = json_decode( $raw_data['body'], true );
192
-
193
- if( ! isset( $raw_data['error'] ) ) {
194
- //If decoding was successful
195
- if ( ! empty( $raw_data ) ) {
196
- //If there are some entries (events) to process
197
- //Loop through each event, extracting the relevant information
198
- foreach ( $raw_data['items'] as $event ) {
199
- $id = ( isset( $event['id'] ) ? esc_html( $event['id'] ) : '' );
200
- $title = ( isset( $event['summary'] ) ? esc_html( $event['summary'] ) : '' );
201
- $description = ( isset( $event['description'] ) ? $event['description'] : '' );
202
- $link = ( isset( $event['htmlLink'] ) ? esc_url( $event['htmlLink'] ) : '' );
203
- $location = ( isset( $event['location'] ) ? esc_html( $event['location'] ) : '' );
204
-
205
- if( isset( $event['start']['dateTime'] ) ) {
206
- $start_time = $this->iso_to_ts( $event['start']['dateTime'] );
207
- } else if( isset( $event['start']['date'] ) ) {
208
- $start_time = $this->iso_to_ts( $event['start']['date'] );
209
- } else {
210
- $start_time = null;
211
- }
212
-
213
- if( isset( $event['end']['dateTime'] ) ) {
214
- $end_time = $this->iso_to_ts( $event['end']['dateTime'] );
215
- } else if( isset( $event['end']['date'] ) ) {
216
- $end_time = $this->iso_to_ts( $event['end']['date'] );
217
- } else {
218
- $end_time = null;
219
- }
220
-
221
- //Create a GCE_Event using the above data. Add it to the array of events
222
- $this->events[] = new GCE_Event( $this, $id, $title, $description, $location, $start_time, $end_time, $link );
223
- }
224
- } else {
225
- //json_decode failed
226
- $this->error = __( 'Some data was retrieved, but could not be parsed successfully. Please ensure your feed settings are correct.', 'gce' );
227
- }
228
- } else {
229
- $this->error = __( 'An error has occured.', 'gce' );
230
- $this->error .= '<pre>' . $raw_data['error']['message'] . '</pre>';
231
- }
232
- } else{
233
- //Generate an error message from the returned WP_Error
234
- $this->error = $raw_data->get_error_message() . __( ' Please ensure your calendar ID is correct.', 'gce' );
235
- }
236
- }
237
-
238
- if( ! empty( $this->error ) ) {
239
- if( current_user_can( 'manage_options' ) ) {
240
- echo $this->error;
241
- return;
242
- }
243
- } else {
244
- if( $this->cache > 0 && false === get_transient( 'gce_feed_' . $this->id ) ) {
245
- $this->cache_events();
246
- }
247
- }
248
- }
249
-
250
- /**
251
- * Convert an ISO date/time to a UNIX timestamp
252
- *
253
- * @since 2.0.0
254
- */
255
- private function iso_to_ts( $iso ) {
256
- sscanf( $iso, "%u-%u-%uT%u:%u:%uZ", $year, $month, $day, $hour, $minute, $second );
257
- return mktime( $hour, $minute, $second, $month, $day, $year );
258
- }
259
-
260
- /**
261
- * @return int
262
- */
263
- private function get_feed_start() {
264
-
265
- $range = get_post_meta( $this->id, 'gce_display_mode', true );
266
- $use_range = ( ( $range == 'date-range-list' || $range == 'date-range-grid' ) ? true : false );
267
-
268
- if( $use_range ) {
269
- $start = get_post_meta( $this->id, 'gce_feed_range_start', true );
270
-
271
- $start = gce_date_unix( $start );
272
-
273
- $interval = 'date-range';
274
-
275
- } else {
276
- $start = get_post_meta( $this->id, 'gce_feed_start_num', true );
277
- $interval = get_post_meta( $this->id, 'gce_feed_start', true );
278
-
279
- if( empty( $start ) && $start !== '0' ) {
280
- $start = 1;
281
- }
282
- }
283
-
284
- switch( $interval ) {
285
- case 'days':
286
- return time() - ( $start * 86400 );
287
- case 'months':
288
- return time() - ( $start * 2629743 );
289
- case 'years':
290
- return time() - ( $start * 31556926 );
291
- case 'date-range':
292
- return $start;
293
- }
294
-
295
- // fall back just in case. Falls back to 1 year ago
296
- return time() - 31556926;
297
- }
298
-
299
- /**
300
- * @return int
301
- */
302
- private function get_feed_end() {
303
-
304
- $range = get_post_meta( $this->id, 'gce_display_mode', true );
305
- $use_range = ( ( $range == 'date-range-list' || $range == 'date-range-grid' ) ? true : false );
306
-
307
- if( $use_range ) {
308
- $end = get_post_meta( $this->id, 'gce_feed_range_end', true );
309
-
310
- $end = gce_date_unix( $end );
311
-
312
- $interval = 'date-range';
313
-
314
- } else {
315
- $end = get_post_meta( $this->id, 'gce_feed_end_num', true );
316
- $interval = get_post_meta( $this->id, 'gce_feed_end', true );
317
-
318
- if( empty( $end ) && $end !== '0' ) {
319
- $end = 1;
320
- }
321
- }
322
-
323
- switch( $interval ) {
324
- case 'days':
325
- return time() + ( $end * 86400 );
326
- case 'months':
327
- return time() + ( $end * 2629743 );
328
- case 'years':
329
- return time() + ( $end * 31556926 );
330
- case 'date-range':
331
- return mktime( 23, 59, 59, date( 'n', $end ), date( 'j', $end ), date( 'Y', $end ) );
332
- }
333
-
334
- // Falls back to 1 year ahead just in case
335
- return time() + 31556926;
336
- }
337
-
338
- function get_builder() {
339
-
340
- $this->builder = get_post( $this->id )->post_content;
341
-
342
- return $this->builder;
343
- }
344
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/events/event-builder.php ADDED
@@ -0,0 +1,952 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Event Builder
4
+ *
5
+ * @package SimpleCalendar/Events
6
+ */
7
+ namespace SimpleCalendar\Events;
8
+
9
+ use Carbon\Carbon;
10
+ use SimpleCalendar\Abstracts\Calendar;
11
+
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * The Event Builder.
18
+ *
19
+ * Parses event templates from a feed post type for printing on calendar views.
20
+ *
21
+ * @since 3.0.0
22
+ */
23
+ class Event_Builder {
24
+
25
+ /**
26
+ * Event.
27
+ *
28
+ * @access public
29
+ * @var Event
30
+ */
31
+ public $event = null;
32
+
33
+ /**
34
+ * Calendar.
35
+ *
36
+ * @access public
37
+ * @var Calendar
38
+ */
39
+ public $calendar = null;
40
+
41
+ /**
42
+ * Tags.
43
+ *
44
+ * @access public
45
+ * @var array
46
+ */
47
+ public $tags = array();
48
+
49
+ /**
50
+ * Constructor.
51
+ *
52
+ * @since 3.0.0
53
+ *
54
+ * @param Event $event
55
+ * @param Calendar $calendar
56
+ */
57
+ public function __construct( Event $event, Calendar $calendar ) {
58
+ $this->event = $event;
59
+ $this->calendar = $calendar;
60
+ $this->tags = $this->get_content_tags();
61
+ }
62
+
63
+ /**
64
+ * Get content tags.
65
+ *
66
+ * @since 3.0.0
67
+ *
68
+ * @return array
69
+ */
70
+ public function get_content_tags() {
71
+ return array(
72
+
73
+ /* ============ *
74
+ * Content Tags *
75
+ * ============ */
76
+
77
+ 'title', // The event title.
78
+ 'event-title', // @deprecated An alias for 'title' tag.
79
+ 'description', // The event description.
80
+
81
+ 'when', // Date and time of the event.
82
+ 'start-time', // Start time of the event.
83
+ 'start-date', // Start date of the event.
84
+ 'start-custom', // @deprecated Start time in a user defined format (set by tag 'format' attribute).
85
+ 'start-human', // Start time in a human friendly format.
86
+ 'end-time', // End time of the event.
87
+ 'end-date', // End date of the event.
88
+ 'end-custom', // @deprecated End date-time in a user defined format (set by tag 'format' attribute).
89
+ 'end-human', // End date-time in a human friendly format.
90
+
91
+ 'duration', // How long the events lasts, in a human-readable format.
92
+ 'length', // @deprecated An alias of 'duration' tag.
93
+
94
+ 'location', // Alias of start-location.
95
+ 'start-location', // Location name where the event starts.
96
+ 'maps-link', // @deprecated An alias for 'start-location-link' tag.
97
+ 'start-location-link', // Link to Google Maps querying the event start location address.
98
+ 'end-location', // Location name where the event ends.
99
+ 'end-location-link', // Link to Google Maps querying the event end location address.
100
+
101
+ 'link', // An HTML link to the event URL.
102
+ 'url', // A string with the raw event link URL.
103
+
104
+ 'calendar', // The title of the source calendar.
105
+ 'feed-title', // @deprecated An alias of 'calendar'.
106
+
107
+ 'id', // The event unique ID.
108
+ 'uid', // An alias of ID.
109
+ 'event-id', // @deprecated An alias for 'id' tag.
110
+ 'calendar-id', // The calendar ID.
111
+ 'feed-id', // @deprecated An alias for 'calendar-id' tag.
112
+ 'cal-id', // @deprecated An alias for 'calendar-id' tag.
113
+
114
+ /* ========= *
115
+ * Meta Tags *
116
+ * ========= */
117
+
118
+ 'attachments', // List of attachments.
119
+ 'attendees', // List of attendees.
120
+ 'organizer', // Creator info.
121
+
122
+ /* ================ *
123
+ * Conditional Tags *
124
+ * ================ */
125
+
126
+ 'if-title', // If the event has a title.
127
+ 'if-description', // If the event has a description.
128
+
129
+ 'if-now', // If the event is taking place now.
130
+ 'if-not-now', // If the event is not taking place now (may have ended or just not started yet).
131
+ 'if-started', // If the event has started (and may as well as ended).
132
+ 'if-not-started', // If the event has NOT started yet (event could be any time in the future).
133
+ 'if-ended', // If the event has ended (event could be any time in the past).
134
+ 'if-not-ended', // If the event has NOT ended (may as well as not started yet).
135
+
136
+ 'if-whole-day', // If the event lasts the whole day.
137
+ 'if-all-day', // @deprecated Alias for 'if-whole-day'.
138
+ 'if-not-whole-day', // If the event does NOT last the whole day.
139
+ 'if-not-all-day', // @deprecated Alias for 'if-not-whole-day'.
140
+ 'if-end-time', // If the event has a set end time.
141
+ 'if-no-end-time', // If the event has NOT a set end time.
142
+
143
+ 'if-multi-day', // If the event spans multiple days.
144
+ 'if-single-day', // If the event does not span multiple days.
145
+
146
+ 'if-recurring', // If the event is a recurring event.
147
+ 'if-not-recurring', // If the event is NOT a recurring event.
148
+
149
+ 'if-location', // @deprecated Alias for 'if-start-location'.
150
+ 'if-start-location', // Does the event has a start location?
151
+ 'if-end-location', // Does the event has an end location?
152
+ 'if-not-location', // @deprecated Alias for 'if-not-start-location'.
153
+ 'if-not-start-location', // Does the event has NOT a start location?
154
+ 'if-not-end-location', // Does the event has NOT an end location?
155
+
156
+ );
157
+ }
158
+
159
+ /**
160
+ * Get event content.
161
+ *
162
+ * @since 3.0.0
163
+ *
164
+ * @param string $template_tags
165
+ *
166
+ * @return string
167
+ */
168
+ public function parse_event_template_tags( $template_tags = '' ) {
169
+
170
+ // Process tags.
171
+ $result = preg_replace_callback(
172
+ $this->get_regex(),
173
+ array( $this, 'process_event_content' ),
174
+ $template_tags
175
+ );
176
+
177
+ // Removes extra consecutive <br> tags.
178
+ return preg_replace( '#(<br */?>\s*)+#i', '<br />', trim( $result ) );
179
+ }
180
+
181
+ /**
182
+ * Process event content.
183
+ *
184
+ * @since 3.0.0
185
+ *
186
+ * @param string $match
187
+ *
188
+ * @return string
189
+ */
190
+ public function process_event_content( $match ) {
191
+
192
+ if ( $match[1] == '[' && $match[6] == ']' ) {
193
+ return substr( $match[0], 1, - 1 );
194
+ }
195
+
196
+ $tag = $match[2]; // Tag name without square brackets.
197
+ $before = $match[1]; // Before tag.
198
+ $partial = $match[5]; // HTML content between tags.
199
+ $after = $match[6]; // After tag.
200
+ $attr = $match[3]; // Tag attributes in quotes.
201
+
202
+ $calendar = $this->calendar;
203
+ $event = $this->event;
204
+
205
+ if ( ( $calendar instanceof Calendar ) && ( $event instanceof Event ) ) {
206
+
207
+ switch ( $tag ) {
208
+
209
+ /* ============ *
210
+ * Content Tags *
211
+ * ============ */
212
+
213
+ case 'title' :
214
+ case 'event-title' :
215
+ return $this->get_title( $event->title, $attr );
216
+
217
+ case 'description' :
218
+ return $this->get_description( $event->description, $attr );
219
+
220
+ case 'when' :
221
+ return $this->get_when( $event );
222
+
223
+ case 'end-date' :
224
+ case 'end-custom' :
225
+ case 'end-human' :
226
+ case 'end-time' :
227
+ case 'start-custom' :
228
+ case 'start-date' :
229
+ case 'start-human' :
230
+ case 'start-time' :
231
+ return $this->get_dt( $tag, $event, $attr );
232
+
233
+ case 'length' :
234
+ case 'duration' :
235
+ if ( false !== $event->end ) {
236
+ $duration = $event->start - $event->end;
237
+ $value = human_time_diff( $event->start, $event->end );
238
+ } else {
239
+ $duration = '-1';
240
+ $value = __( 'No end time', 'google-calendar-events' );
241
+ }
242
+ return ' <span class="simcal-event-duration" data-event-duration="' . $duration . '">' . $value . '</span>';
243
+
244
+ case 'location' :
245
+ case 'start-location' :
246
+ case 'end-location' :
247
+ $location = $tag == 'end-location' ? $event->end_location['address'] : $event->start_location['address'];
248
+ return ' <span class="simcal-event-address simcal-event-start-location" itemprop="location" itemscope itemtype="http://schema.org/Place">' . wp_strip_all_tags( $location ) . '</span>';
249
+
250
+ case 'start-location-link':
251
+ case 'end-location-link' :
252
+ case 'maps-link' :
253
+ $location = $tag == 'end-location-link' ? $event->end_location['address'] : $event->start_location['address'];
254
+ if ( ! empty( $location ) ) {
255
+ $url = '//maps.google.com?q=' . urlencode( $location );
256
+ return $this->make_link( $tag, $url, $calendar->get_event_html( $event, $partial ), $attr );
257
+ }
258
+ break;
259
+
260
+ case 'link' :
261
+ case 'url' :
262
+ $content = 'link' == $tag ? $calendar->get_event_html( $event, $partial ) : '';
263
+ return $this->make_link( $tag, $event->link, $content , $attr );
264
+
265
+ case 'calendar' :
266
+ case 'feed-title' :
267
+ return $event->source;
268
+
269
+ case 'id' :
270
+ case 'uid' :
271
+ case 'event-id' :
272
+ return $event->uid;
273
+
274
+ case 'calendar-id' :
275
+ case 'cal-id' :
276
+ case 'feed-id' :
277
+ return $event->calendar;
278
+
279
+ /* ========= *
280
+ * Meta Tags *
281
+ * ========= */
282
+
283
+ case 'attachments' :
284
+ $attachments = $event->get_attachments();
285
+ if ( ! empty( $attachments ) ) {
286
+ return $this->get_attachments( $attachments );
287
+ }
288
+ break;
289
+
290
+ case 'attendees' :
291
+ $attendees = $event->get_attendees();
292
+ if ( ! empty( $attendees ) ) {
293
+ return $this->get_attendees( $attendees, $attr );
294
+ }
295
+ break;
296
+
297
+ case 'organizer' :
298
+ $organizer = $event->get_organizer();
299
+ if ( ! empty( $organizer ) ) {
300
+ return $this->get_organizer( $organizer, $attr );
301
+ }
302
+ break;
303
+
304
+ /* ================ *
305
+ * Conditional Tags *
306
+ * ================ */
307
+
308
+ case 'if-title':
309
+ if ( ! empty( $event->title ) ) {
310
+ return $calendar->get_event_html( $event, $partial );
311
+ }
312
+ break;
313
+
314
+ case 'if-description':
315
+ if ( ! empty( $event->description ) ) {
316
+ return $calendar->get_event_html( $event, $partial );
317
+ }
318
+ break;
319
+
320
+ case 'if-now' :
321
+ case 'if-not-now' :
322
+
323
+ $start_dt = $event->start_dt->setTimezone( $calendar->timezone );
324
+ $start = $start_dt->getTimestamp();
325
+
326
+ if ( $event->end_dt instanceof Carbon ) {
327
+ $end = $event->end_dt->setTimezone( $calendar->timezone )->getTimestamp();
328
+ } else {
329
+ return '';
330
+ }
331
+
332
+ $now = ( $start <= $calendar->now ) && ( $end >= $calendar->now );
333
+
334
+ if ( ( 'if-now' == $tag ) && $now ) {
335
+ return $calendar->get_event_html( $event, $partial );
336
+ } elseif ( ( 'if-not-now' == $tag ) && ( false == $now ) ) {
337
+ return $calendar->get_event_html( $event, $partial );
338
+ }
339
+
340
+ break;
341
+
342
+ case 'if-started' :
343
+ case 'if-not-started' :
344
+
345
+ $start = $event->start_dt->setTimezone( $calendar->timezone )->getTimestamp();
346
+
347
+ if ( 'if-started' == $tag ) {
348
+ if ( $start < $calendar->now ) {
349
+ return $calendar->get_event_html( $event, $partial );
350
+ }
351
+ } elseif ( 'if-not-started' == $tag ) {
352
+ if ( $start > $calendar->now ) {
353
+ return $calendar->get_event_html( $event, $partial );
354
+ }
355
+ }
356
+
357
+ break;
358
+
359
+ case 'if-ended' :
360
+ case 'if-not-ended' :
361
+
362
+ if ( false !== $event->end ) {
363
+
364
+ $end = $event->end_dt->setTimezone( $calendar->timezone )->getTimestamp();
365
+
366
+ if ( 'if-ended' == $tag ) {
367
+ if ( $end < $calendar->now ) {
368
+ return $calendar->get_event_html( $event, $partial );
369
+ }
370
+ } elseif ( 'if-not-ended' == $tag ) {
371
+ if ( $end > $calendar->now ) {
372
+ return $calendar->get_event_html( $event, $partial );
373
+ }
374
+ }
375
+
376
+ }
377
+
378
+ break;
379
+
380
+ case 'if-end-time' :
381
+ if ( false !== $event->end ) {
382
+ return $calendar->get_event_html( $event, $partial );
383
+ }
384
+ break;
385
+
386
+ case 'if-no-end-time' :
387
+ if ( false === $event->end ) {
388
+ return $calendar->get_event_html( $event, $partial );
389
+ }
390
+ break;
391
+
392
+ case 'if-all-day' :
393
+ case 'if-whole-day' :
394
+ case 'if-not-all-day' :
395
+ case 'if-not-whole-day' :
396
+ $bool = strstr( $tag, 'not' ) ? false : true;
397
+ if ( $bool === $event->whole_day ) {
398
+ return $calendar->get_event_html( $event, $partial );
399
+ }
400
+ break;
401
+
402
+ case 'if-recurring' :
403
+ if ( ! empty( $event->recurrence ) ) {
404
+ return $calendar->get_event_html( $event, $partial );
405
+ }
406
+ break;
407
+
408
+ case 'if-not-recurring' :
409
+ if ( false === $event->recurrence ) {
410
+ return $calendar->get_event_html( $event, $partial );
411
+ }
412
+ break;
413
+
414
+ case 'if-multi-day' :
415
+ if ( false !== $event->multiple_days ) {
416
+ return $calendar->get_event_html( $event, $partial );
417
+ }
418
+ break;
419
+
420
+ case 'if-single-day' :
421
+ if ( false === $event->multiple_days ) {
422
+ return $calendar->get_event_html( $event, $partial );
423
+ }
424
+ break;
425
+
426
+ case 'if-location' :
427
+ case 'if-start-location' :
428
+ if ( ! empty( $event->start_location['address'] ) ) {
429
+ return $calendar->get_event_html( $event, $partial );
430
+ }
431
+ return false;
432
+
433
+ case 'if-not-location' :
434
+ case 'if-not-start-location' :
435
+ if ( empty( $event->start_location['address'] ) ) {
436
+ return $calendar->get_event_html( $event, $partial );
437
+ }
438
+ return '';
439
+
440
+ case 'if-not-end-location' :
441
+ if ( empty( $event->end_location['address'] ) ) {
442
+ return $calendar->get_event_html( $event, $partial );
443
+ }
444
+ return '';
445
+
446
+ case 'if-end-location' :
447
+ if ( ! empty( $event->end_location['address'] ) ) {
448
+ return $calendar->get_event_html( $event, $partial );
449
+ }
450
+ return '';
451
+
452
+ /* ======= *
453
+ * Default *
454
+ * ======= */
455
+
456
+ default :
457
+ return wp_kses_post( $before . $partial . $after );
458
+ }
459
+ }
460
+
461
+ return '';
462
+ }
463
+
464
+ /**
465
+ * Limit words in text string.
466
+ *
467
+ * @since 3.0.0
468
+ * @access private
469
+ *
470
+ * @param string $text
471
+ * @param int $limit
472
+ *
473
+ * @return string
474
+ */
475
+ private function limit_words( $text, $limit ) {
476
+
477
+ $text = wp_strip_all_tags( $text );
478
+ $limit = max( absint( $limit ), 0 );
479
+
480
+ if ( $limit > 0 && ( str_word_count( $text, 0 ) > $limit ) ) {
481
+ $words = str_word_count( $text, 2 );
482
+ $pos = array_keys( $words );
483
+ $text = trim( substr( $text, 0, $pos[ $limit ] ) ) . '&hellip;';
484
+ }
485
+
486
+ return $text;
487
+ }
488
+
489
+ /**
490
+ * Get event title.
491
+ *
492
+ * @since 3.0.0
493
+ * @access private
494
+ *
495
+ * @param $title
496
+ * @param $attr
497
+ *
498
+ * @return string
499
+ */
500
+ private function get_title( $title, $attr ) {
501
+
502
+ if ( empty( $title ) ) {
503
+ return '';
504
+ }
505
+
506
+ $attr = array_merge( array(
507
+ 'html' => '', // Parse HTML
508
+ 'limit' => 0, // Trim length to amount of words
509
+ ), (array) shortcode_parse_atts( $attr ) );
510
+
511
+ if ( ! empty( $attr['html'] ) ) {
512
+ $title = wp_kses_post( $title );
513
+ $tag = 'div';
514
+ } else {
515
+ $title = $this->limit_words( $title, $attr['limit'] );
516
+ $tag = 'span';
517
+ }
518
+
519
+ return '<' . $tag . ' class="simcal-event-title" itemprop="name">' . $title . '</' . $tag . '>';
520
+ }
521
+
522
+ /**
523
+ * Get event description.
524
+ *
525
+ * @since 3.0.0
526
+ * @access private
527
+ *
528
+ * @param string $description
529
+ * @param string $attr
530
+ *
531
+ * @return string
532
+ */
533
+ private function get_description( $description, $attr ) {
534
+
535
+ if ( empty( $description ) ) {
536
+ return '';
537
+ }
538
+
539
+ $attr = array_merge( array(
540
+ 'limit' => 0, // Trim length to number of words
541
+ 'html' => 'no', // Parse HTML content
542
+ 'markdown' => 'no', // Parse Markdown content
543
+ 'autolink' => 'no', // Automatically convert plaintext URIs to anchors
544
+ ), (array) shortcode_parse_atts( $attr ) );
545
+
546
+ $allow_html = 'no' != $attr['html'] ? true : false;
547
+ $allow_md = 'no' != $attr['markdown'] ? true : false;
548
+
549
+ $html = '<div class="simcal-event-description" itemprop="description">';
550
+
551
+ // Markdown and HTML don't play well together, use one or the other in the same tag.
552
+ if ( $allow_html || $allow_md ) {
553
+ if ( $allow_html ) {
554
+ $html .= wp_kses_post( $description );
555
+ } elseif ( $allow_md ) {
556
+ $markdown = new \Parsedown();
557
+ $html .= $markdown->text( wp_strip_all_tags( $description ) );
558
+ }
559
+ } else {
560
+ $html .= $this->limit_words( $description, $attr['limit'] );
561
+ }
562
+
563
+ $html .= '</div>';
564
+
565
+ if ( 'no' != $attr['autolink'] ) {
566
+ $html = ' ' . make_clickable( $html );
567
+ }
568
+
569
+ return $html;
570
+ }
571
+
572
+ /**
573
+ * Get event start and end date and time.
574
+ *
575
+ * @since 3.0.0
576
+ * @access private
577
+ *
578
+ * @param Event $event
579
+ *
580
+ * @return string
581
+ */
582
+ private function get_when( Event $event ) {
583
+
584
+ $start = $event->start_dt->setTimezone( $event->timezone );
585
+ $end = ! is_null( $event->end_dt ) ? $event->end_dt->setTimezone( $event->timezone ) : null;
586
+
587
+ $time_start = '';
588
+ $time_end = '';
589
+
590
+ if ( ! $event->whole_day ) {
591
+
592
+ $time_start = $this->calendar->datetime_separator .
593
+ ' <span class="simcal-event-start simcal-event-start-time" ' .
594
+ 'data-event-start="' . $start->getTimestamp() . '" ' .
595
+ 'data-event-format="' . $this->calendar->time_format . '" ' .
596
+ 'itemprop="startDate" content="' . $start->toIso8601String() . '">' .
597
+ date_i18n( $this->calendar->time_format, $start->getTimestamp() ) .
598
+ '</span> ';
599
+
600
+ if ( $end instanceof Carbon ) {
601
+
602
+ $time_end = ' <span class="simcal-event-end simcal-event-end-time" ' .
603
+ 'data-event-end="' . $end->getTimestamp() . '" ' .
604
+ 'data-event-format="' . $this->calendar->time_format . '" ' .
605
+ 'itemprop="endDate" content="' . $end->toIso8601String() . '">' .
606
+ date_i18n( $this->calendar->time_format, $end->getTimestamp() ) .
607
+ '</span> ';
608
+
609
+ }
610
+
611
+ }
612
+
613
+ if ( $event->multiple_days ) {
614
+
615
+ $output = ' <span class="simcal-event-start simcal-event-start-date" ' .
616
+ 'data-event-start="' . $start->getTimestamp() . '" ' .
617
+ 'data-event-format="' . $this->calendar->date_format . '" ' .
618
+ 'itemprop="startDate" content="' . $start->toIso8601String() . '">' .
619
+ date_i18n( $this->calendar->date_format, $start->getTimestamp() ) .
620
+ '</span> ' .
621
+ $time_start;
622
+
623
+ if ( $end instanceof Carbon ) {
624
+
625
+ $output .= '-' .
626
+ ' <span class="simcal-event-start simcal-event-end-date" ' .
627
+ 'data-event-start="' . $end->getTimestamp() . '" ' .
628
+ 'data-event-format="' . $this->calendar->date_format . '" ' .
629
+ 'itemprop="endDate" content="' . $end->toIso8601String() . '">' .
630
+ date_i18n( $this->calendar->date_format, $end->getTimestamp() ) .
631
+ '</span> ' .
632
+ $time_end;
633
+ }
634
+
635
+ } else {
636
+
637
+ $time_end = ! empty( $time_start ) && ! empty( $time_end ) ? ' - ' . $time_end : '';
638
+
639
+ $output = ' <span class="simcal-event-start simcal-event-start-date" ' .
640
+ 'data-event-start="' . $start->getTimestamp() . '" ' .
641
+ 'data-event-format="' . $this->calendar->date_format . '">' .
642
+ date_i18n( $this->calendar->date_format, $start->getTimestamp() ) .
643
+ '</span> ' .
644
+ $time_start .
645
+ $time_end;
646
+
647
+ }
648
+
649
+ return trim( $output );
650
+ }
651
+
652
+ /**
653
+ * Get event date or time.
654
+ *
655
+ * @since 3.0.0
656
+ * @access private
657
+ *
658
+ * @param string $tag
659
+ * @param Event $event
660
+ * @param string $attr
661
+ *
662
+ * @return string
663
+ */
664
+ private function get_dt( $tag, Event $event, $attr ) {
665
+
666
+ $bound = 0 === strpos( $tag, 'end' ) ? 'end' : 'start';
667
+ if ( ( 'end' == $bound ) && ( false === $event->end ) ) {
668
+ return '';
669
+ }
670
+
671
+ $dt = $bound . '_dt';
672
+ if ( ! $event->$dt instanceof Carbon ) {
673
+ return '';
674
+ }
675
+ $event_dt = $event->$dt->setTimezone( $event->timezone );
676
+
677
+ $attr = array_merge( array(
678
+ 'format' => '',
679
+ ), (array) shortcode_parse_atts( $attr ) );
680
+
681
+ $format = ltrim( strstr( $tag, '-' ), '-' );
682
+ $dt_format = '';
683
+ if ( ! empty( $attr['format'] ) ) {
684
+ $dt_format = esc_attr( wp_strip_all_tags( $attr['format'] ) );
685
+ } elseif ( 'date' == $format ) {
686
+ $dt_format = $this->calendar->date_format;
687
+ } elseif ( 'time' == $format ) {
688
+ $dt_format = $this->calendar->time_format;
689
+ }
690
+
691
+ if ( 'human' == $format ) {
692
+ $value = human_time_diff( $event_dt->getTimestamp(), Carbon::now( $event->timezone )->getTimestamp() );
693
+ } else {
694
+ $value = date_i18n( $dt_format, $event_dt->getTimestamp() );
695
+ }
696
+
697
+ return '<span class="simcal-event-' . $bound . ' ' . 'simcal-event-' . $bound . '-' . $format . '"' .
698
+ 'data-event-' . $bound . '="' . $event_dt->getTimestamp() . '"' .
699
+ 'data-event-format="' . $dt_format . '"' .
700
+ 'itemprop="' . $bound . 'Date" content="' . $event_dt->toIso8601String() . '">' .
701
+ $value .
702
+ '</span>';
703
+ }
704
+
705
+ /**
706
+ * Make a link.
707
+ *
708
+ * @since 3.0.0
709
+ * @access private
710
+ *
711
+ * @param string $tag
712
+ * @param string $url
713
+ * @param string $content
714
+ * @param string $attr
715
+ *
716
+ * @return string
717
+ */
718
+ private function make_link( $tag, $url, $content, $attr ) {
719
+
720
+ if ( empty( $url ) ) {
721
+ return '';
722
+ }
723
+
724
+ $text = empty( $content ) ? $url : $content;
725
+
726
+ $attr = array_merge( array(
727
+ 'autolink' => false, // Convert url to link anchor
728
+ 'newwindow' => false, // If autolink attribute is true, open link in new window
729
+ ), (array) shortcode_parse_atts( $attr ) );
730
+
731
+ $anchor = $tag != 'url' ? 'yes' : $attr['autolink'];
732
+ $target = $attr['newwindow'] !== false ? 'target="_blank"' : '';
733
+
734
+ return $anchor !== false ? ' <a href="' . esc_url( $url ) . '" ' . $target . '>' . $text . '</a>' : ' ' . $text;
735
+ }
736
+
737
+ /**
738
+ * Get event attachments.
739
+ *
740
+ * @since 3.0.0
741
+ * @access private
742
+ *
743
+ * @param array $attachments
744
+ *
745
+ * @return string
746
+ */
747
+ private function get_attachments( $attachments ) {
748
+
749
+ $html = '<ul class="simcal-attachments">' . "\n\t";
750
+
751
+ foreach ( $attachments as $attachment ) {
752
+ $html .= '<li class="simcal-attachment">';
753
+ $html .= '<a href="' . $attachment['url'] . '" target="_blank">';
754
+ $html .= ! empty( $attachment['icon'] ) ? '<img src="' . $attachment['icon'] . '" />' : '';
755
+ $html .= '<span>' . $attachment['name'] . '</span>';
756
+ $html .= '</a>';
757
+ $html .= '</li>' . "\n";
758
+ }
759
+
760
+ $html .= '</ul>' . "\n";
761
+
762
+ return $html;
763
+ }
764
+
765
+ /**
766
+ * Get attendees.
767
+ *
768
+ * @since 3.0.0
769
+ * @access private
770
+ *
771
+ * @param array $attendees
772
+ * @param string $attr
773
+ *
774
+ * @return string
775
+ */
776
+ private function get_attendees( $attendees, $attr ) {
777
+
778
+ $attr = array_merge( array(
779
+ 'photo' => 'show', // show/hide attendee photo
780
+ 'email' => 'hide', // show/hide attendee email address
781
+ 'rsvp' => 'hide', // show/hide rsvp response status
782
+ 'response' => '', // filter attendees by rsvp response (yes/no/maybe)
783
+ ), (array) shortcode_parse_atts( $attr ) );
784
+
785
+ $html = '<ul class="simcal-attendees" itemprop="attendees">' . "\n\t";
786
+
787
+ $known = 0;
788
+ $unknown = 0;
789
+
790
+ foreach ( $attendees as $attendee ) {
791
+
792
+ if ( 'yes' == $attr['response'] && 'yes' != $attendee['response'] ) {
793
+ continue;
794
+ } elseif ( 'no' == $attr['response'] && 'no' != $attendee['response'] ) {
795
+ continue;
796
+ } elseif ( 'maybe' == $attr['response'] && ! in_array( $attendee['response'], array( 'yes', 'maybe' ) ) ) {
797
+ continue;
798
+ }
799
+
800
+ if ( ! empty( $attendee['name'] ) ) {
801
+
802
+ $photo = 'hide' != $attr['photo'] ? '<img class="avatar avatar-128 photo" src="' . $attendee['photo'] . '" itemprop="image" />' : '';
803
+ $response = 'hide' != $attr['rsvp'] ? $this->get_rsvp_response( $attendee['response'] ) : '';
804
+ $guest = $photo . '<span itemprop="name">' . $attendee['name'] . $response . '</span>';
805
+
806
+ if ( ! empty( $attendee['email'] ) && ( 'show' == $attr['email'] ) ) {
807
+ $guest = sprintf( '<a href="mailto:' . $attendee['email'] . '" itemprop="email">%s</a>', $guest );
808
+ }
809
+
810
+ $html .= '<li class="simcal-attendee" itemprop="attendee" itemscope itemtype="http://schema.org/Person">' . $guest . '</li>' . "\n";
811
+
812
+ $known++;
813
+
814
+ } else {
815
+
816
+ $unknown++;
817
+
818
+ }
819
+ }
820
+
821
+ if ( $unknown > 0 ) {
822
+ if ( $known > 0 ) {
823
+ /* translators: One more person attending the event. */
824
+ $others = sprintf( _n( '1 more attendee', '%s more attendees', $unknown, 'google-calendar-events' ), $unknown );
825
+ } else {
826
+ /* translators: One or more persons attending the event whose name is unknown. */
827
+ $others = sprintf( _n( '1 anonymous attendee', '%s anonymous attendees', $unknown, 'google-calendar-events' ), $unknown );
828
+ }
829
+ $photo = $attr['photo'] !== 'hide' ? get_avatar( '', 128 ) : '';
830
+ $html .= '<li class="simcal-attendee simcal-attendee-anonymous">' . $photo . '<span>' . $others . '</span></li>' . "\n";
831
+ } elseif ( $known === 0 ) {
832
+ $html .= '<li class="simcal-attendee">' . _x( 'No one yet', 'No one yet rsvp to attend the event.', 'google-calendar-events' ) . '</li>' . "\n";
833
+ }
834
+
835
+ $html .= '</ul>' . "\n";
836
+
837
+ return $html;
838
+ }
839
+
840
+ /**
841
+ * Format attendee rsvp response.
842
+ *
843
+ * @since 3.0.0
844
+ *
845
+ * @param $response
846
+ *
847
+ * @return string
848
+ */
849
+ private function get_rsvp_response( $response ) {
850
+
851
+ if ( 'yes' == $response ) {
852
+ /* translators: Someone replied with 'yes' to a rsvp request. */
853
+ $rsvp = __( 'Attending', 'google-calendar-events' );
854
+ } elseif ( 'no' == $response ) {
855
+ /* translators: Someone replied with 'no' to a rsvp request. */
856
+ $rsvp = __( 'Not attending', 'google-calendar-events' );
857
+ } elseif ( 'maybe' == $response ) {
858
+ /* translators: Someone replied with 'maybe' to a rsvp request. */
859
+ $rsvp = __( 'Maybe attending', 'google-calendar-events' );
860
+ } else {
861
+ /* translators: Someone did not send yet a rsvp confirmation to join an event. */
862
+ $rsvp = __( 'Response pending', 'google-calendar-events' );
863
+ }
864
+
865
+ return ' <small>(' . $rsvp . ')</small>';
866
+ }
867
+
868
+ /**
869
+ * Get event organizer.
870
+ *
871
+ * @since 3.0.0
872
+ * @access private
873
+ *
874
+ * @param array $organizer
875
+ * @param string $attr
876
+ *
877
+ * @return string
878
+ */
879
+ private function get_organizer( $organizer, $attr ) {
880
+
881
+ $attr = array_merge( array(
882
+ 'photo' => 'show', // show/hide attendee photo
883
+ 'email' => 'hide', // show/hide attendee email address
884
+ ), (array) shortcode_parse_atts( $attr ) );
885
+
886
+ $photo = 'hide' != $attr['photo'] ? '<img class="avatar avatar-128 photo" src="' . $organizer['photo'] . '" itemprop="image" />' : '';
887
+ $organizer = $photo . '<span itemprop="name">' . $organizer['name'] . '</span>';
888
+
889
+ if ( ! empty( $organizer['email'] ) && ( 'show' == $attr['email'] ) ) {
890
+ $organizer = sprintf( '<a href="mailto:' . $organizer['email'] . '" itemprop="email">%s</a>', $organizer );
891
+ }
892
+
893
+ return '<div class="simcal-organizer" itemprop="organizer" itemscope itemtype="https://schema.org/Person">' . $organizer . '</div>';
894
+ }
895
+
896
+ /**
897
+ * Retrieve the event builder tag regular expression for searching.
898
+ *
899
+ * Combines the event builder tags in the regular expression in a regex class.
900
+ * The regular expression contains 6 different sub matches to help with parsing:
901
+ *
902
+ * 1 - An extra [ to allow for escaping tags with double square brackets [[]]
903
+ * 2 - The tag name
904
+ * 3 - The tag argument list
905
+ * 4 - The self closing /
906
+ * 5 - The content of a tag when it wraps some content.
907
+ * 6 - An extra ] to allow for escaping tags with double square brackets [[]]
908
+ *
909
+ * @since 3.0.0
910
+ *
911
+ * @return string The tag search regular expression result
912
+ */
913
+ private function get_regex() {
914
+
915
+ // This is largely borrowed on get_shortcode_regex() from WordPress Core.
916
+ // @see /wp-includes/shortcodes.php (with some modification)
917
+
918
+ $tagregexp = implode( '|', array_values( $this->tags ) );
919
+
920
+ return '/'
921
+ . '\\[' // Opening bracket
922
+ . '(\\[?)' // 1: Optional second opening bracket for escaping tags: [[tag]]
923
+ . "($tagregexp)" // 2: Tag name
924
+ . '(?![\\w-])' // Not followed by word character or hyphen
925
+ . '(' // 3: Unroll the loop: Inside the opening tag
926
+ . '[^\\]\\/]*' // Not a closing bracket or forward slash
927
+ . '(?:'
928
+ . '\\/(?!\\])' // A forward slash not followed by a closing bracket
929
+ . '[^\\]\\/]*' // Not a closing bracket or forward slash
930
+ . ')*?'
931
+ . ')'
932
+ . '(?:'
933
+ . '(\\/)' // 4: Self closing tag ...
934
+ . '\\]' // ... and closing bracket
935
+ . '|'
936
+ . '\\]' // Closing bracket
937
+ . '(?:'
938
+ . '(' // 5: Unroll the loop: Optionally, anything between the opening and closing tags
939
+ . '[^\\[]*+' // Not an opening bracket
940
+ . '(?:'
941
+ . '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing tag
942
+ . '[^\\[]*+' // Not an opening bracket
943
+ . ')*+'
944
+ . ')'
945
+ . '\\[\\/\\2\\]' // Closing tag
946
+ . ')?'
947
+ . ')'
948
+ . '(\\]?)' // 6: Optional second closing bracket for escaping tags: [[tag]]
949
+ . '/s';
950
+ }
951
+
952
+ }
includes/events/event.php ADDED
@@ -0,0 +1,603 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Event
4
+ *
5
+ * @package SimpleCalendar/Events
6
+ */
7
+ namespace SimpleCalendar\Events;
8
+
9
+ use Carbon\Carbon;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * The Event.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Event {
21
+
22
+ /**
23
+ * Event type.
24
+ *
25
+ * @access public
26
+ * @var string
27
+ */
28
+ public $type = '';
29
+
30
+ /**
31
+ * Event source.
32
+ *
33
+ * @access public
34
+ * @var string
35
+ */
36
+ public $source = '';
37
+
38
+ /**
39
+ * Event title.
40
+ *
41
+ * @access public
42
+ * @var string
43
+ */
44
+ public $title = '';
45
+
46
+ /**
47
+ * Event description.
48
+ *
49
+ * @access public
50
+ * @var string
51
+ */
52
+ public $description = '';
53
+
54
+ /**
55
+ * Event visibility.
56
+ *
57
+ * @access public
58
+ * @var string
59
+ */
60
+ public $visibility = '';
61
+
62
+ /**
63
+ * Event privacy.
64
+ *
65
+ * @access public
66
+ * @var bool
67
+ */
68
+ public $public = false;
69
+
70
+ /**
71
+ * Event link URL.
72
+ *
73
+ * @access public
74
+ * @var
75
+ */
76
+ public $link = '';
77
+
78
+ /**
79
+ * Event unique identifier.
80
+ *
81
+ * @access public
82
+ * @var string
83
+ */
84
+ public $uid = '';
85
+
86
+ /**
87
+ * Event parent calendar id.
88
+ *
89
+ * @access public
90
+ * @var int
91
+ */
92
+ public $calendar = 0;
93
+
94
+ /**
95
+ * Event parent calendar timezone.
96
+ *
97
+ * @access public
98
+ * @var string
99
+ */
100
+ public $timezone = 'UTC';
101
+
102
+ /**
103
+ * Event start time.
104
+ *
105
+ * @access public
106
+ * @var int
107
+ */
108
+ public $start = 0;
109
+
110
+ /**
111
+ * Event start time in GMT.
112
+ *
113
+ * @access public
114
+ * @var int
115
+ */
116
+ public $start_utc = 0;
117
+
118
+ /**
119
+ * Event start datetime object.
120
+ *
121
+ * @access public
122
+ * @var Carbon
123
+ */
124
+ public $start_dt = null;
125
+
126
+ /**
127
+ * Event start time timezone.
128
+ *
129
+ * @access public
130
+ * @var string
131
+ */
132
+ public $start_timezone = 'UTC';
133
+
134
+ /**
135
+ * Event location at event start.
136
+ *
137
+ * @access public
138
+ * @var array
139
+ */
140
+ public $start_location = false;
141
+
142
+ /**
143
+ * Event end time.
144
+ *
145
+ * @access public
146
+ * @var false|int
147
+ */
148
+ public $end = false;
149
+
150
+ /**
151
+ * Event end time in GMT.
152
+ *
153
+ * @access public
154
+ * @var false|int
155
+ */
156
+ public $end_utc = false;
157
+
158
+ /**
159
+ * Event end datetime object.
160
+ *
161
+ * @access public
162
+ * @var null|Carbon
163
+ */
164
+ public $end_dt = null;
165
+
166
+ /**
167
+ * Event end time timezone.
168
+ *
169
+ * @access public
170
+ * @var string
171
+ */
172
+ public $end_timezone = 'UTC';
173
+
174
+ /**
175
+ * Event location at event end.
176
+ *
177
+ * @access public
178
+ * @var array
179
+ */
180
+ public $end_location = false;
181
+
182
+ /**
183
+ * Event has location.
184
+ *
185
+ * @access public
186
+ * @var bool
187
+ */
188
+ public $venue = false;
189
+
190
+ /**
191
+ * Whole day event.
192
+ *
193
+ * @access public
194
+ * @var bool
195
+ */
196
+ public $whole_day = false;
197
+
198
+ /**
199
+ * Multiple days span.
200
+ *
201
+ * @access public
202
+ * @var bool|int
203
+ */
204
+ public $multiple_days = false;
205
+
206
+ /**
207
+ * Recurring event.
208
+ *
209
+ * @access public
210
+ * @var false|array
211
+ */
212
+ public $recurrence = false;
213
+
214
+ /**
215
+ * Event meta.
216
+ *
217
+ * @access public
218
+ * @var array
219
+ */
220
+ public $meta = array();
221
+
222
+ /**
223
+ * Event default template.
224
+ *
225
+ * @access public
226
+ * @var string
227
+ */
228
+ public $template = '';
229
+
230
+ /**
231
+ * Event constructor.
232
+ *
233
+ * @since 3.0.0
234
+ *
235
+ * @param array $event
236
+ */
237
+ public function __construct( array $event ) {
238
+
239
+ /* ================= *
240
+ * Event Identifiers *
241
+ * ================= */
242
+
243
+ // Event unique id.
244
+ if ( ! empty( $event['uid'] ) ) {
245
+ $this->uid = esc_attr( $event['uid'] );
246
+ }
247
+
248
+ // Event source.
249
+ if ( ! empty( $event['source'] ) ) {
250
+ $this->source = esc_attr( $event['source'] );
251
+ }
252
+
253
+ // Event parent calendar id.
254
+ if ( ! empty( $event['calendar'] ) ) {
255
+ $this->calendar = max( intval( $event['calendar'] ), 0 );
256
+ }
257
+
258
+ // Event parent calendar timezone.
259
+ if ( ! empty( $event['timezone'] ) ) {
260
+ $this->timezone = esc_attr( $event['timezone'] );
261
+ }
262
+
263
+ /* ============= *
264
+ * Event Content *
265
+ * ============= */
266
+
267
+ // Event title.
268
+ if ( ! empty( $event['title'] ) ) {
269
+ $this->title = esc_html( $event['title'] );
270
+ }
271
+
272
+ // Event description.
273
+ if ( ! empty( $event['description'] ) ) {
274
+ $this->description = esc_html( $event['description'] );
275
+ }
276
+
277
+ // Event link URL.
278
+ if ( ! empty( $event['link'] ) ) {
279
+ $this->link = esc_url_raw( $event['link'] );
280
+ }
281
+
282
+ // Event visibility.
283
+ if ( ! empty( $event['visibility'] ) ) {
284
+ $this->visibility = esc_attr( $event['visibility'] );
285
+ $this->public = $this->visibility == 'public' ? true : false;
286
+ }
287
+
288
+ /* =========== *
289
+ * Event Start *
290
+ * =========== */
291
+
292
+ if ( ! empty( $event['start'] ) ) {
293
+ $this->start = is_numeric( $event['start'] ) ? intval( $event['start'] ) : 0;
294
+ if ( ! empty( $event['start_utc'] ) ) {
295
+ $this->start_utc = is_numeric( $event['start_utc'] ) ? intval( $event['start_utc'] ) : 0;
296
+ }
297
+ if ( ! empty( $event['start_timezone'] ) ) {
298
+ $this->start_timezone = esc_attr( $event['start_timezone'] );
299
+ }
300
+ $this->start_dt = Carbon::createFromTimestamp( $this->start, $this->start_timezone );
301
+ $start_location = isset( $event['start_location'] ) ? $event['start_location'] : '';
302
+ $this->start_location = $this->esc_location( $start_location );
303
+ }
304
+
305
+ /* ========= *
306
+ * Event End *
307
+ * ========= */
308
+
309
+ if ( ! empty( $event['end'] ) ) {
310
+ $this->end = is_numeric( $event['end'] ) ? intval( $event['end'] ): false;
311
+ if ( ! empty( $event['end_timezone'] ) ) {
312
+ $this->end_timezone = esc_attr( $event['end_timezone'] );
313
+ }
314
+ if ( ! empty( $event['end_utc'] ) ) {
315
+ $this->end_utc = is_numeric( $event['end_utc'] ) ? intval( $event['end_utc'] ) : false;
316
+ $this->end_dt = Carbon::createFromTimestamp( $this->end, $this->end_timezone );
317
+ }
318
+ $end_location = isset( $event['end_location'] ) ? $event['end_location'] : '';
319
+ $this->end_location = $this->esc_location( $end_location );
320
+ }
321
+
322
+ /* ================== *
323
+ * Event Distribution *
324
+ * ================== */
325
+
326
+ // Whole day event.
327
+ if ( ! empty( $event['whole_day'] ) ) {
328
+ $this->whole_day = true === $event['whole_day'] ? true: false;
329
+ }
330
+
331
+ // Multi day event.
332
+ if ( ! empty( $event['multiple_days'] ) ) {
333
+ $this->multiple_days = max( absint( $event['multiple_days'] ), 2 );
334
+ }
335
+
336
+ // Event recurrence.
337
+ if ( isset( $event['recurrence'] ) ) {
338
+ $this->recurrence = ! empty( $event['recurrence'] ) ? $event['recurrence'] : false;
339
+ }
340
+
341
+ /* ========== *
342
+ * Event Meta *
343
+ * ========== */
344
+
345
+ // Event has venue(s).
346
+ if ( $this->start_location['venue'] || $this->end_location['venue'] ) {
347
+ $this->venue = true;
348
+ }
349
+
350
+ // Event meta.
351
+ if ( ! empty( $event['meta'] ) ) {
352
+ $this->meta = is_array( $event['meta'] ) ? $event['meta'] : array();
353
+ }
354
+
355
+ // Event template.
356
+ if ( ! empty( $event['template'] ) ) {
357
+ $this->template = wp_kses_post( $event['template'] );
358
+ }
359
+
360
+ }
361
+
362
+ /**
363
+ * Escape location.
364
+ *
365
+ * @since 3.0.0
366
+ * @access private
367
+ *
368
+ * @param string|array $var
369
+ *
370
+ * @return array
371
+ */
372
+ private function esc_location( $var = '' ) {
373
+
374
+ $location = array();
375
+
376
+ if ( is_string( $var ) ) {
377
+ $var = array(
378
+ 'name' => $var,
379
+ 'address' => $var,
380
+ );
381
+ } elseif ( is_bool( $var ) || is_null( $var ) ) {
382
+ $var = array();
383
+ } else {
384
+ $var = (array) $var;
385
+ }
386
+
387
+ $location['name'] = isset( $var['name'] ) ? esc_attr( strip_tags( $var['name'] ) ) : '';
388
+ $location['address'] = isset( $var['address'] ) ? esc_attr( strip_tags( $var['address'] ) ) : '';
389
+ $location['lat'] = isset( $var['lat'] ) ? $this->esc_coordinate( $var['lat'] ) : 0;
390
+ $location['lng'] = isset( $var['lng'] ) ? $this->esc_coordinate( $var['lng'] ) : 0;
391
+
392
+ if ( ! empty( $location['name'] ) || ! empty( $location['address'] ) ) {
393
+ $location['venue'] = true;
394
+ } else {
395
+ $location['venue'] = false;
396
+ }
397
+
398
+ return $location;
399
+ }
400
+
401
+ /**
402
+ * Escape coordinate.
403
+ *
404
+ * @since 3.0.0
405
+ * @access private
406
+ *
407
+ * @param int|float $latlng
408
+ *
409
+ * @return int|float
410
+ */
411
+ private function esc_coordinate( $latlng = 0 ) {
412
+ return is_numeric( $latlng ) ? floatval( $latlng ) : 0;
413
+ }
414
+
415
+ /**
416
+ * Set timezone.
417
+ *
418
+ * @since 3.0.0
419
+ * @access private
420
+ *
421
+ * @param string $tz Timezone.
422
+ *
423
+ * @return bool
424
+ */
425
+ public function set_timezone( $tz ) {
426
+ if ( in_array( $tz, timezone_identifiers_list() ) ) {
427
+ $this->timezone = $tz;
428
+ return true;
429
+ }
430
+ return false;
431
+ }
432
+
433
+ /**
434
+ * Starts or ends today.
435
+ *
436
+ * @since 3.0.0
437
+ *
438
+ * @return bool
439
+ */
440
+ public function is_today() {
441
+ return $this->starts_today() || $this->ends_today();
442
+ }
443
+
444
+ /**
445
+ * Starts today.
446
+ *
447
+ * @since 3.0.0
448
+ *
449
+ * @return bool
450
+ */
451
+ public function starts_today() {
452
+ return $this->start_dt->setTimezone( $this->timezone )->isToday();
453
+ }
454
+
455
+ /**
456
+ * Ends today.
457
+ *
458
+ * @since 3.0.0
459
+ *
460
+ * @return bool
461
+ */
462
+ public function ends_today() {
463
+ return ! is_null( $this->end_dt ) ? $this->end_dt->setTimezone( $this->timezone )->isToday() : true;
464
+ }
465
+
466
+ /**
467
+ * Starts tomorrow
468
+ *
469
+ * @since 3.0.0
470
+ *
471
+ * @return bool
472
+ */
473
+ public function starts_tomorrow() {
474
+ return $this->start_dt->setTimezone( $this->timezone )->isTomorrow();
475
+ }
476
+
477
+ /**
478
+ * Ends tomorrow.
479
+ *
480
+ * @since 3.0.0
481
+ *
482
+ * @return bool
483
+ */
484
+ public function ends_tomorrow() {
485
+ return ! is_null( $this->end_dt ) ? $this->end_dt->setTimezone( $this->timezone )->isTomorrow() : false;
486
+ }
487
+
488
+ /**
489
+ * Started yesterday.
490
+ *
491
+ * @since 3.0.0
492
+ *
493
+ * @return bool
494
+ */
495
+ public function started_yesterday() {
496
+ return $this->start_dt->setTimezone( $this->timezone )->isYesterday();
497
+ }
498
+
499
+ /**
500
+ * Ended yesterday.
501
+ *
502
+ * @since 3.0.0
503
+ *
504
+ * @return bool
505
+ */
506
+ public function ended_yesterday() {
507
+ return ! is_null( $this->end_dt ) ? $this->end_dt->setTimezone( $this->timezone )->isYesterday() : false;
508
+ }
509
+
510
+ /**
511
+ * Starts in the future.
512
+ *
513
+ * @since 3.0.0
514
+ *
515
+ * @return bool
516
+ */
517
+ public function starts_future() {
518
+ return $this->start_dt->setTimezone( $this->timezone )->isFuture();
519
+ }
520
+
521
+ /**
522
+ * Ends in the future.
523
+ *
524
+ * @since 3.0.0
525
+ *
526
+ * @return bool
527
+ */
528
+ public function ends_future() {
529
+ return ! is_null( $this->end_dt ) ? $this->end_dt->setTimezone( $this->timezone )->isFuture() : false;
530
+ }
531
+
532
+ /**
533
+ * Started in the past.
534
+ *
535
+ * @since 3.0.0
536
+ *
537
+ * @return bool
538
+ */
539
+ public function started_past() {
540
+ return $this->start_dt->setTimezone( $this->timezone )->isPast();
541
+ }
542
+
543
+ /**
544
+ * Ended in the past.
545
+ *
546
+ * @since 3.0.0
547
+ *
548
+ * @return bool
549
+ */
550
+ public function ended_past() {
551
+ return ! is_null( $this->end_dt ) ? $this->end_dt->setTimezone( $this->timezone )->isPast() : false;
552
+ }
553
+
554
+ /**
555
+ * Get color.
556
+ *
557
+ * @since 3.0.0
558
+ *
559
+ * @param string $default
560
+ *
561
+ * @return string
562
+ */
563
+ public function get_color( $default = '' ) {
564
+ if ( isset( $this->meta['color'] ) ) {
565
+ return ! empty( $this->meta['color'] ) ? esc_attr( $this->meta['color'] ) : $default;
566
+ }
567
+ return $default;
568
+ }
569
+
570
+ /**
571
+ * Get attachments.
572
+ *
573
+ * @since 3.0.0
574
+ *
575
+ * @return array
576
+ */
577
+ public function get_attachments() {
578
+ return isset( $this->meta['attachments'] ) ? $this->meta['attachments'] : array();
579
+ }
580
+
581
+ /**
582
+ * Get attendees.
583
+ *
584
+ * @since 3.0.0
585
+ *
586
+ * @return array
587
+ */
588
+ public function get_attendees() {
589
+ return isset( $this->meta['attendees'] ) ? $this->meta['attendees'] : array();
590
+ }
591
+
592
+ /**
593
+ * Get organizer.
594
+ *
595
+ * @since 3.0.0
596
+ *
597
+ * @return array
598
+ */
599
+ public function get_organizer() {
600
+ return isset( $this->meta['organizer'] ) ? $this->meta['organizer'] : array();
601
+ }
602
+
603
+ }
includes/events/events.php ADDED
@@ -0,0 +1,605 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Events Collection
4
+ *
5
+ * @package SimpleCalendar/Events
6
+ */
7
+ namespace SimpleCalendar\Events;
8
+
9
+ use Carbon\Carbon;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * The Events.
17
+ *
18
+ * A collection of Event objects.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Events {
23
+
24
+ /**
25
+ * Events.
26
+ *
27
+ * @access public
28
+ * @var array
29
+ */
30
+ protected $events = array();
31
+
32
+ /**
33
+ * Timezone.
34
+ *
35
+ * @access public
36
+ * @var string
37
+ */
38
+ protected $timezone = 'UTC';
39
+
40
+ /**
41
+ * Constructor.
42
+ *
43
+ * @since 3.0.0
44
+ *
45
+ * @param array $e Events.
46
+ * @param string|\DateTimeZone $tz Timezone.
47
+ */
48
+ public function __construct( $e = array(), $tz = 'UTC' ) {
49
+ $this->set_events( $e );
50
+ $this->set_timezone( $tz );
51
+ }
52
+
53
+ /**
54
+ * Get events.
55
+ *
56
+ * @since 3.0.0
57
+ *
58
+ * @param string|int $n Amount of events (optional).
59
+ *
60
+ * @return array
61
+ */
62
+ public function get_events( $n = '' ) {
63
+ if ( ! empty( $n ) && ! empty( $this->events ) ) {
64
+ $length = absint( $n );
65
+ return array_slice( $this->events, 0, $length, true );
66
+ }
67
+ return $this->events;
68
+ }
69
+
70
+ /**
71
+ * Set events.
72
+ *
73
+ * @since 3.0.0
74
+ *
75
+ * @param array $ev Events.
76
+ */
77
+ public function set_events( array $ev ) {
78
+ $this->events = $ev;
79
+ }
80
+
81
+ /**
82
+ * Set timezone.
83
+ *
84
+ * @since 3.0.0
85
+ *
86
+ * @param string|\DateTimeZone $tz Timezone.
87
+ *
88
+ * @return Events
89
+ */
90
+ public function set_timezone( $tz ) {
91
+ if ( $tz instanceof \DateTimeZone ) {
92
+ $tz = $tz->getName();
93
+ }
94
+ $this->timezone = simcal_esc_timezone( $tz, $this->timezone );
95
+ return $this;
96
+ }
97
+
98
+ /**
99
+ * Shift events.
100
+ *
101
+ * @since 3.0.0
102
+ *
103
+ * @param int $n
104
+ *
105
+ * @return Events
106
+ */
107
+ public function shift( $n ) {
108
+ if ( ! empty( $this->events ) ) {
109
+ $offset = intval( $n );
110
+ $length = count( $this->events );
111
+ $this->set_events( array_slice( $this->events, $offset, $length, true ) );
112
+ }
113
+ return $this;
114
+ }
115
+
116
+ /**
117
+ * Filter private events.
118
+ *
119
+ * @since 3.0.0
120
+ *
121
+ * @return Events
122
+ */
123
+ public function private_only() {
124
+ $this->set_events( $this->filter_property( 'public', 'hide' ) );
125
+ return $this;
126
+ }
127
+
128
+ /**
129
+ * Filter public events.
130
+ *
131
+ * @since 3.0.0
132
+ *
133
+ * @return Events
134
+ */
135
+ public function public_only() {
136
+ $this->set_events( $this->filter_property( 'public', 'show' ) );
137
+ return $this;
138
+ }
139
+
140
+ /**
141
+ * Filter recurring events in the current block.
142
+ *
143
+ * @since 3.0.0
144
+ *
145
+ * @return Events
146
+ */
147
+ public function recurring() {
148
+ $this->set_events( $this->filter_property( 'recurrence', 'show' ) );
149
+ return $this;
150
+ }
151
+
152
+ /**
153
+ * Filter non recurring events in the current block.
154
+ *
155
+ * @since 3.0.0
156
+ *
157
+ * @return Events
158
+ */
159
+ public function not_recurring() {
160
+ $this->set_events( $this->filter_property( 'recurrence', 'hide' ) );
161
+ return $this;
162
+ }
163
+
164
+ /**
165
+ * Filter whole day events in the current block.
166
+ *
167
+ * @since 3.0.0
168
+ *
169
+ * @return Events
170
+ */
171
+ public function whole_day() {
172
+ $this->set_events( $this->filter_property( 'whole_day', 'show' ) );
173
+ return $this;
174
+ }
175
+
176
+ /**
177
+ * Filter non whole day in the current block.
178
+ *
179
+ * @since 3.0.0
180
+ *
181
+ * @return Events
182
+ */
183
+ public function not_whole_day() {
184
+ $this->set_events( $this->filter_property( 'whole_day', 'hide' ) );
185
+ return $this;
186
+ }
187
+
188
+ /**
189
+ * Filter events spanning multiple days in the current block.
190
+ *
191
+ * @since 3.0.0
192
+ *
193
+ * @return Events
194
+ */
195
+ public function multi_day() {
196
+ $this->set_events( $this->filter_property( 'multiple_days', 'show' ) );
197
+ return $this;
198
+ }
199
+
200
+ /**
201
+ * Filter events that do not span multiple days in the current block.
202
+ *
203
+ * @since 3.0.0
204
+ *
205
+ * @return Events
206
+ */
207
+ public function single_day() {
208
+ $this->set_events( $this->filter_property( 'multiple_days', 'hide' ) );
209
+ return $this;
210
+ }
211
+
212
+ /**
213
+ * Filter events in the current block that have a location.
214
+ *
215
+ * @since 3.0.0
216
+ *
217
+ * @return Events
218
+ */
219
+ public function with_location() {
220
+ $this->set_events( $this->filter_property( 'venue', 'show' ) );
221
+ return $this;
222
+ }
223
+
224
+ /**
225
+ * Filter events in the current block that do not have a location.
226
+ *
227
+ * @since 3.0.0
228
+ *
229
+ * @return Events
230
+ */
231
+ public function without_location() {
232
+ $this->set_events( $this->filter_property( 'venue', 'hide' ) );
233
+ return $this;
234
+ }
235
+
236
+ /**
237
+ * Filter whole day events.
238
+ *
239
+ * @since 3.0.0
240
+ * @access private
241
+ *
242
+ * @param string $property
243
+ * @param string $toggle
244
+ *
245
+ * @return array
246
+ */
247
+ private function filter_property( $property, $toggle ) {
248
+ $filtered = array();
249
+ if ( ! empty( $this->events ) ) {
250
+ foreach ( $this->events as $ts => $events ) {
251
+ foreach ( $events as $event ) {
252
+ if ( 'hide' == $toggle ) {
253
+ if ( ! $event->$property ) {
254
+ $filtered[ $ts ][] = $event;
255
+ }
256
+ } elseif ( 'show' == $toggle ) {
257
+ if ( $event->$property ) {
258
+ $filtered[ $ts ][] = $event;
259
+ }
260
+ }
261
+ }
262
+ }
263
+ }
264
+ return $filtered;
265
+ }
266
+
267
+ /**
268
+ * Filter events in the past.
269
+ *
270
+ * @since 3.0.0
271
+ *
272
+ * @param int|string $present
273
+ *
274
+ * @return Events
275
+ */
276
+ public function future( $present = '' ) {
277
+ $last = $this->get_last();
278
+ $to = $last instanceof Event ? $last->start_utc : false;
279
+ if ( $to ) {
280
+ if ( empty( $present ) ) {
281
+ $present = Carbon::now( $this->timezone )->getTimestamp();
282
+ }
283
+ $this->set_events( $this->filter_events( intval( $present ), $to ) );
284
+ }
285
+ return $this;
286
+ }
287
+
288
+ /**
289
+ * Filter events in the future.
290
+ *
291
+ * @since 3.0.0
292
+ *
293
+ * @param int|string $present
294
+ *
295
+ * @return Events
296
+ */
297
+ public function past( $present = '' ) {
298
+ $first = $this->get_last();
299
+ $from = $first instanceof Event ? $first->start_utc : false;
300
+ if ( $from ) {
301
+ if ( empty( $present ) ) {
302
+ $present = Carbon::now( $this->timezone )->getTimestamp();
303
+ }
304
+ $this->set_events( $this->filter_events( $from, intval( $present ) ) );
305
+ }
306
+ return $this;
307
+ }
308
+
309
+ /**
310
+ * Filter events after time.
311
+ *
312
+ * @since 3.0.0
313
+ *
314
+ * @param int|string|\DateTime|Carbon $time
315
+ *
316
+ * @return Events
317
+ */
318
+ public function after( $time ) {
319
+ $dt = $this->parse( $time );
320
+ return ! is_null( $dt ) ? $this->future( $dt->getTimestamp() ) : $this;
321
+ }
322
+
323
+ /**
324
+ * Filter events before time.
325
+ *
326
+ * @since 3.0.0
327
+ *
328
+ * @param int|string|\DateTime|Carbon $time
329
+ *
330
+ * @return Events
331
+ */
332
+ public function before( $time ) {
333
+ $dt = $this->parse( $time );
334
+ return ! is_null( $dt ) ? $this->past( $dt->getTimestamp() ) : $this;
335
+ }
336
+
337
+ /**
338
+ * Filter events from a certain time onwards.
339
+ *
340
+ * @since 3.0.0
341
+ *
342
+ * @param int|string|\DateTime|Carbon $time
343
+ *
344
+ * @return Events
345
+ */
346
+ public function from( $time ) {
347
+ $last = $this->parse( $time );
348
+ if ( ! is_null( $last ) ) {
349
+ $this->set_events( $this->filter_events( $time, $last->getTimestamp() ) );
350
+ }
351
+ return $this;
352
+ }
353
+
354
+ /**
355
+ * Filter events up to to a certain time.
356
+ *
357
+ * @since 3.0.0
358
+ *
359
+ * @param int|string|\DateTime|Carbon $time
360
+ *
361
+ * @return Events
362
+ */
363
+ public function to( $time ) {
364
+ $first = $this->parse( $time );
365
+ if ( ! is_null( $first ) ) {
366
+ $this->set_events( $this->filter_events( $first->getTimestamp(), $time ) );
367
+ }
368
+ return $this;
369
+ }
370
+
371
+ /**
372
+ * Parse time.
373
+ *
374
+ * @since 3.0.0
375
+ *
376
+ * @param int|string|\DateTime|Carbon $time
377
+ *
378
+ * @return null|Carbon
379
+ */
380
+ private function parse( $time ) {
381
+ if ( is_int( $time ) ) {
382
+ return Carbon::createFromTimestamp( $time, $this->timezone );
383
+ } elseif ( is_string( $time ) && ! empty( $time ) ) {
384
+ return Carbon::parse( $time, $this->timezone );
385
+ } elseif ( $time instanceof Carbon ) {
386
+ return $time->setTimezone( $this->timezone );
387
+ } elseif ( $time instanceof \DateTime ) {
388
+ return Carbon::instance( $time )->setTimezone( $this->timezone );
389
+ }
390
+ return null;
391
+ }
392
+
393
+ /**
394
+ * Get first event of the current block.
395
+ *
396
+ * @since 3.0.0
397
+ *
398
+ * @return null|Event
399
+ */
400
+ public function get_first() {
401
+ return array_shift( $this->events );
402
+ }
403
+
404
+ /**
405
+ * Get last event of the current block.
406
+ *
407
+ * @since 3.0.0
408
+ *
409
+ * @return null|Event
410
+ */
411
+ public function get_last() {
412
+ return array_pop( $this->events );
413
+ }
414
+
415
+ /**
416
+ * Get the closest event in the future.
417
+ *
418
+ * @since 3.0.0
419
+ *
420
+ * @return null|Event
421
+ */
422
+ public function get_upcoming() {
423
+ return $this->get_closest( 'future' );
424
+ }
425
+
426
+ /**
427
+ * Get the closest event in the past.
428
+ *
429
+ * @since 3.0.0
430
+ *
431
+ * @return null|Event
432
+ */
433
+ public function get_latest() {
434
+ return $this->get_closest( 'past' );
435
+ }
436
+
437
+ /**
438
+ * Get the closest event compared to now.
439
+ *
440
+ * @since 3.0.0
441
+ * @access private
442
+ *
443
+ * @param string $dir Direction: 'future' or 'past'.
444
+ *
445
+ * @return null|Event
446
+ */
447
+ private function get_closest( $dir ) {
448
+ if ( 'future' == $dir ) {
449
+ return array_shift( $this->future()->get_events() );
450
+ } elseif ( 'past' == $dir ) {
451
+ return array_shift( $this->past()->get_events() );
452
+ }
453
+ return null;
454
+ }
455
+
456
+ /**
457
+ * Get events for the given year.
458
+ *
459
+ * @since 3.0.0
460
+ *
461
+ * @param int $year Year.
462
+ *
463
+ * @return array Multidimensional array with month number, week number and Event objects for each weekday.
464
+ */
465
+ public function get_year( $year ) {
466
+ $y = intval( $year );
467
+ $months = array();
468
+ for ( $m = 1; $m <= 12; $m++ ) {
469
+ $months[ strval( $m ) ] = $this->get_month( $y, $m );
470
+ }
471
+ return $months;
472
+ }
473
+
474
+ /**
475
+ * Get events for the given month in the given year.
476
+ *
477
+ * @since 3.0.0
478
+ *
479
+ * @param int $year Year.
480
+ * @param int $month Month number.
481
+ *
482
+ * @return array Multidimensional array with week number, day of the week and array of Event objects for each week day.
483
+ */
484
+ public function get_month( $year, $month ) {
485
+ $y = intval( $year );
486
+ $m = min( max( 1, absint( $month ) ), 12 );
487
+ $days = Carbon::createFromDate( $y, $m, 2, $this->timezone )->startOfMonth()->daysInMonth;
488
+ $weeks = array();
489
+ for ( $d = 1; $d < $days; $d++ ) {
490
+ $current = Carbon::createFromDate( $y, $m, $d );
491
+ $week = $current->weekOfYear;
492
+ $day = $current->dayOfWeek;
493
+ $weeks[ strval( $week ) ][ strval( $day ) ] = $this->get_day( $y, $m, $d );
494
+ }
495
+ return $weeks;
496
+ }
497
+
498
+ /**
499
+ * Get events for the given week in the given year.
500
+ *
501
+ * @since 3.0.0
502
+ *
503
+ * @param int $year Year.
504
+ * @param int $week Week number.
505
+ *
506
+ * @return array Associative array with day of the week for key and array of Event objects for value.
507
+ */
508
+ public function get_week( $year, $week ) {
509
+ $y = intval( $year );
510
+ $w = absint( $week );
511
+ $m = date( 'n', strtotime( strval( $y ) . '-W' . strval( $w ) ) );
512
+ $month_dt = Carbon::createFromDate( $y, $m, 2, $this->timezone );
513
+ $days = array();
514
+ for ( $d = 1; $d < $month_dt->daysInMonth; $d++ ) {
515
+ $current = Carbon::createFromDate( $y, $m, $d );
516
+ if ( $w == $current->weekOfYear ) {
517
+ $days[ strval( $current->dayOfWeek ) ] = $this->get_day( $y, $m, $d );
518
+ }
519
+ }
520
+ return $days;
521
+ }
522
+
523
+ /**
524
+ * Get events for the given day of the given month in the given year.
525
+ *
526
+ * @since 3.0.0
527
+ *
528
+ * @param int $year Year.
529
+ * @param int $month Month number.
530
+ * @param int $day Day of the month number.
531
+ *
532
+ * @return array Event objects for the day.
533
+ */
534
+ public function get_day( $year, $month, $day ) {
535
+ $y = intval( $year );
536
+ $m = min( max( 1, absint( $month ) ), 12 );
537
+ $d = min( absint( $day ), 31 );
538
+ $from = Carbon::createFromDate( $y, $m, $d, $this->timezone )->startOfDay()->getTimestamp();
539
+ $to = Carbon::createFromDate( $y, $m, $d, $this->timezone )->endOfDay()->getTimestamp();
540
+ return $this->filter_events( $from, $to );
541
+ }
542
+
543
+ /**
544
+ * Get events for today.
545
+ *
546
+ * @since 3.0.0
547
+ *
548
+ * @return array Event objects for today.
549
+ */
550
+ public function get_today() {
551
+ $start = Carbon::today( $this->timezone )->startOfDay()->getTimestamp();
552
+ $end = Carbon::today( $this->timezone )->endOfDay()->getTimestamp();
553
+ return $this->filter_events( $start, $end );
554
+ }
555
+
556
+ /**
557
+ * Get events for tomorrow.
558
+ *
559
+ * @since 3.0.0
560
+ *
561
+ * @return array Event objects for tomorrow.
562
+ */
563
+ public function get_tomorrow() {
564
+ $start = Carbon::tomorrow( $this->timezone )->startOfDay()->getTimestamp();
565
+ $end = Carbon::tomorrow( $this->timezone )->endOfDay()->getTimestamp();
566
+ return $this->filter_events( $start, $end );
567
+ }
568
+
569
+ /**
570
+ * Get events for yesterday.
571
+ *
572
+ * @since 3.0.0
573
+ *
574
+ * @return array Event objects for yesterday.
575
+ */
576
+ public function get_yesterday() {
577
+ $start = Carbon::yesterday( $this->timezone )->startOfDay()->getTimestamp();
578
+ $end = Carbon::yesterday( $this->timezone )->endOfDay()->getTimestamp();
579
+ return $this->filter_events( $start, $end );
580
+ }
581
+
582
+ /**
583
+ * Filter events by timestamps.
584
+ *
585
+ * @since 3.0.0
586
+ * @access private
587
+ *
588
+ * @param int $from Lower bound timestamp.
589
+ * @param int $to Upper bound timestamp.
590
+ *
591
+ * @return array Filtered array of Event objects.
592
+ */
593
+ private function filter_events( $from, $to ) {
594
+ $timestamps = array_keys( $this->events );
595
+ $lower_bound = array_filter( $timestamps, function( $ts ) use( $from ) {
596
+ return intval( $ts ) > intval( $from );
597
+ } );
598
+ $higher_bound = array_filter( $lower_bound, function( $ts ) use( $to ) {
599
+ return intval( $ts ) > intval( $to );
600
+ } );
601
+ $filtered = array_combine( $higher_bound, $higher_bound );
602
+ return array_intersect_key( $this->events, $filtered );
603
+ }
604
+
605
+ }
includes/feeds/admin/google-admin.php ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Google Calendar - Admin
4
+ *
5
+ * @package SimpleCalendar/Feeds
6
+ */
7
+ namespace SimpleCalendar\Feeds\Admin;
8
+
9
+ use SimpleCalendar\Admin\Metaboxes\Settings;
10
+ use SimpleCalendar\Admin\Notice;
11
+ use SimpleCalendar\Feeds\Google;
12
+
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * Google Calendar feed admin.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Google_Admin {
23
+
24
+ /**
25
+ * Google calendar feed object.
26
+ *
27
+ * @access private
28
+ * @var Google
29
+ */
30
+ private $feed = null;
31
+
32
+ /**
33
+ * Google Api Key.
34
+ *
35
+ * @access private
36
+ * @var string
37
+ */
38
+ private $google_api_key = '';
39
+
40
+ /**
41
+ * Google Calendar id.
42
+ *
43
+ * @access private
44
+ * @var string
45
+ */
46
+ private $google_calendar_id = '';
47
+
48
+ /**
49
+ * Hook in tabs.
50
+ *
51
+ * @since 3.0.0
52
+ *
53
+ * @param Google $feed
54
+ * @param string $google_api_key
55
+ * @param string $google_calendar_id
56
+ */
57
+ public function __construct( Google $feed, $google_api_key, $google_calendar_id ) {
58
+
59
+ $this->feed = $feed;
60
+ $this->google_api_key = $google_api_key;
61
+ $this->google_calendar_id = $google_calendar_id;
62
+
63
+ $screen = simcal_is_admin_screen();
64
+
65
+ if ( 'calendar' == $screen ) {
66
+ $this->test_api_key_connection( $this->google_calendar_id );
67
+ add_filter( 'simcal_settings_meta_tabs_li', array( $this, 'add_settings_meta_tab_li' ), 10, 1 );
68
+ add_action( 'simcal_settings_meta_panels', array( $this, 'add_settings_meta_panel' ), 10, 1 );
69
+ }
70
+
71
+ add_action( 'simcal_process_settings_meta', array( $this, 'process_meta' ), 10, 1 );
72
+ }
73
+
74
+ /**
75
+ * Feed settings page fields.
76
+ *
77
+ * @since 3.0.0
78
+ *
79
+ * @return array
80
+ */
81
+ public function settings_fields() {
82
+ return array(
83
+ 'name' => $this->feed->name,
84
+ 'description' => __( "To read events from your public Google Calendars you'll need create a Google API key and save it here.", 'google-calendar-events' ) .
85
+ '<br/><br/>' .
86
+ '<em style="font-size: 14px;">' .
87
+ sprintf( __( '<strong>Note:</strong> A Google API key is not needed for calendars configured to use the <strong><a href="%s" target="_blank">Google Calendar Pro add-on</a></strong>.', 'google-calendar-events' ),
88
+ simcal_ga_campaign_url( simcal_get_url( 'gcal-pro' ), 'core-plugin', 'settings-link' )
89
+ ) .
90
+ '</em>',
91
+ 'fields' => array(
92
+ 'api_key' => array(
93
+ 'type' => 'standard',
94
+ 'subtype' => 'text',
95
+ 'class' => array( 'simcal-wide-text regular-text', 'ltr' ),
96
+ 'title' => __( 'Google API Key', 'google-calendar-events' ),
97
+ 'validation' => array( $this, 'check_google_api_key' ),
98
+ ),
99
+ ),
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Check if there's a Google API Key or a legacy key is being used.
105
+ *
106
+ * This method only checks if the api key setting is not empty.
107
+ * It is not currently possible to check or validate an API Key without performing a full request.
108
+ * On the settings page there are no known calendars to use for this so we can only check if there is a string.
109
+ *
110
+ * @since 3.0.0
111
+ *
112
+ * @param string $api_key Google API key.
113
+ *
114
+ * @return true|string
115
+ */
116
+ public function check_google_api_key( $api_key = '' ) {
117
+
118
+ $message = '';
119
+ $has_errors = false;
120
+
121
+ if ( empty( $api_key ) ){
122
+ $api_key = $this->google_api_key;
123
+ if ( empty( $api_key ) ) {
124
+ $settings = get_option( 'simple-calendar_settings_feeds' );
125
+ $api_key = isset( $settings['google']['api_key'] ) ? esc_attr( $settings['google']['api_key'] ) : '';
126
+ }
127
+ }
128
+
129
+ $message = '<p class="description">' .
130
+ sprintf( __( '<a href="%s" target="_blank">Step-by-step instructions</a> ', 'google-calendar-events' ),
131
+ simcal_ga_campaign_url( simcal_get_url( 'docs' ) . '/google-api-key/', 'core-plugin', 'settings-link' )
132
+ ) .
133
+ '<br/>' .
134
+ sprintf( __( '<a href="%s" target="_blank">Google Developers Console</a> ', 'google-calendar-events' ),
135
+ simcal_get_url( 'gdev-console' )
136
+ ) .
137
+ '</p>';
138
+
139
+ return $message;
140
+ }
141
+
142
+ /**
143
+ * Add a tab to the settings meta box.
144
+ *
145
+ * @since 3.0.0
146
+ *
147
+ * @param array $tabs
148
+ *
149
+ * @return array
150
+ */
151
+ public function add_settings_meta_tab_li( $tabs ) {
152
+ return array_merge( $tabs, array(
153
+ 'google' => array(
154
+ 'label' => $this->feed->name,
155
+ 'target' => 'google-settings-panel',
156
+ 'class' => array( 'simcal-feed-type', 'simcal-feed-type-google' ),
157
+ 'icon' => 'simcal-icon-google',
158
+ ),
159
+ ) );
160
+ }
161
+
162
+ /**
163
+ * Add a panel to the settings meta box.
164
+ *
165
+ * @since 3.0.0
166
+ *
167
+ * @param int $post_id
168
+ */
169
+ public function add_settings_meta_panel( $post_id ) {
170
+
171
+ $inputs = array(
172
+ $this->feed->type => array(
173
+ '_google_calendar_id' => array(
174
+ 'type' => 'standard',
175
+ 'subtype' => 'text',
176
+ 'name' => '_google_calendar_id',
177
+ 'id' => '_google_calendar_id',
178
+ 'title' => __( 'Calendar ID', 'google-calendar-events' ),
179
+ 'tooltip' => __( 'Visit your Google Calendar account, copy your public calendar ID, then paste it here.', 'google-calendar-events' ),
180
+ 'placeholder' => __( 'Enter a valid Google Calendar ID from a public calendar', 'google-calendar-events' ),
181
+ 'escaping' => array( $this->feed, 'esc_google_calendar_id' ),
182
+ 'validation' => array( $this, 'test_api_key_connection' ),
183
+ ),
184
+ '_google_events_search_query' => array(
185
+ 'type' => 'standard',
186
+ 'subtype' => 'text',
187
+ 'name' => '_google_events_search_query',
188
+ 'id' => '_google_events_search_query',
189
+ 'title' => __( 'Search query', 'google-calendar-events' ),
190
+ 'tooltip' => __( 'Type in keywords if you only want display events that match these terms. You can use basic boolean search operators too.', 'google-calendar-events' ),
191
+ 'placeholder' => __( 'Filter events to display by search terms...', 'google-calendar-events' ),
192
+ ),
193
+ '_google_events_recurring' => array(
194
+ 'type' => 'select',
195
+ 'name' => '_google_events_recurring',
196
+ 'id' => '_google_events_recurring',
197
+ 'title' => __( 'Recurring events', 'google-calendar-events' ),
198
+ 'tooltip' => __( 'Events that are programmed to repeat themselves periodically.', 'google-calendar-events' ),
199
+ 'options' => array(
200
+ 'show' => __( 'Show all', 'google-calendar-events' ),
201
+ 'first-only' => __( 'Only show first occurrence', 'google-calendar-events' ),
202
+ ),
203
+ ),
204
+ '_google_events_max_results' => array(
205
+ 'type' => 'standard',
206
+ 'subtype' => 'number',
207
+ 'name' => '_google_events_max_results',
208
+ 'id' => '_google_events_max_results',
209
+ 'title' => __( 'Maximum Events', 'google-calendar-events' ),
210
+ 'tooltip' => __( 'Google Calendar only allows to query for a maximum amount of 2500 events from a calendar each time.', 'google-calendar-events' ),
211
+ 'class' => array(
212
+ 'simcal-field-small',
213
+ ),
214
+ 'default' => '2500',
215
+ 'attributes' => array(
216
+ 'min' => '0',
217
+ 'max' => '2500',
218
+ ),
219
+ ),
220
+ ),
221
+ );
222
+
223
+ ?>
224
+ <div id="google-settings-panel" class="simcal-panel">
225
+ <table>
226
+ <thead>
227
+ <tr><th colspan="2"><?php _e( 'Google Calendar settings', 'google-calendar-events' ); ?></th></tr>
228
+ </thead>
229
+ <?php Settings::print_panel_fields( $inputs, $post_id ); ?>
230
+ </table>
231
+ </div>
232
+ <?php
233
+
234
+ }
235
+
236
+ /**
237
+ * Test a connection to Google Calendar API.
238
+ *
239
+ * @since 3.0.0
240
+ *
241
+ * @param string $google_calendar_id
242
+ *
243
+ * @return true|string
244
+ */
245
+ public function test_api_key_connection( $google_calendar_id ) {
246
+
247
+ global $post;
248
+
249
+ $post_id = isset( $post->ID ) ? $post->ID : 0;
250
+ $feed = null;
251
+ if ( $feed_type = wp_get_object_terms( $post_id, 'calendar_feed' ) ) {
252
+ $feed = sanitize_title( current( $feed_type )->name );
253
+ }
254
+
255
+ $message = '';
256
+ $error = '';
257
+ $has_errors = false;
258
+
259
+ $message .= '<p class="description">' .
260
+ sprintf(
261
+ __( 'Step 1: Set the Google Calendar you want to use as <strong>"public."</strong> <a href="%1s" target="_blank">Detailed instructions</a>', 'google-calendar-events' ) . '<br />' .
262
+ __( 'Step 2: Copy and paste your Google Calendar ID here. <a href="%2s" target="_blank">Detailed instructions</a>', 'google-calendar-events' ),
263
+ simcal_ga_campaign_url( simcal_get_url( 'docs' ) . '/make-google-calendar-public/', 'core-plugin', 'settings-link' ),
264
+ simcal_ga_campaign_url( simcal_get_url( 'docs' ) . '/find-google-calendar-id/', 'core-plugin', 'settings-link' )
265
+ ) . '</p>';
266
+
267
+ if ( $post_id > 0 && ! is_null( $feed ) && ! empty( $this->feed->type ) ) {
268
+
269
+ $no_key_notice = new Notice( array(
270
+ 'id' => array( 'calendar_' . $post_id => 'google-no-api-key' ),
271
+ 'type' => 'error',
272
+ 'screen' => 'calendar',
273
+ 'post' => $post_id,
274
+ 'dismissable' => false,
275
+ 'content' => '<p>' .
276
+ '<i class="simcal-icon-warning"></i> ' .
277
+ sprintf(
278
+ __( 'Your Google Calendar events will not show up until you <a href="%s">create and save a Google API key</a>.', 'google-calendar-events' ),
279
+ admin_url( 'edit.php?post_type=calendar&page=simple-calendar_settings&tab=feeds' )
280
+ ) .
281
+ '</p>',
282
+ )
283
+ );
284
+
285
+ if ( empty( $this->google_api_key ) && ( $feed == $this->feed->type ) ) {
286
+
287
+ $has_errors = true;
288
+ $no_key_notice->add();
289
+
290
+ } else {
291
+
292
+ $no_key_notice->remove();
293
+
294
+ try {
295
+ $this->feed->make_request( $google_calendar_id );
296
+ } catch ( \Exception $e ) {
297
+ $error = $e->getMessage();
298
+ $message = ! empty( $error ) ? '<blockquote>' . $error . '</blockquote>' : '';
299
+ }
300
+
301
+ $error_notice = new Notice( array(
302
+ 'id' => array( 'calendar_' . $post_id => 'google-error-response' ),
303
+ 'type' => 'error',
304
+ 'screen' => 'calendar',
305
+ 'post' => $post_id,
306
+ 'dismissable' => false,
307
+ 'content' => '<p>' .
308
+ '<i class="simcal-icon-warning"></i> ' .
309
+ __( 'While trying to retrieve events, Google returned an error:', 'google-calendar-events' ) .
310
+ '<br>' . $message . '<br>' .
311
+ __( 'Please ensure that both your Google Calendar ID and API Key are valid and that the Google Calendar you want to display is public.', 'google-calendar-events' ) .
312
+ '</p>',
313
+ )
314
+ );
315
+
316
+ if ( ! empty( $error ) && ( $feed == $this->feed->type ) ) {
317
+ $error_notice->add();
318
+ $has_errors = true;
319
+ } else {
320
+ $error_notice->remove();
321
+ $has_errors = false;
322
+ }
323
+
324
+ }
325
+
326
+ }
327
+
328
+ return $message;
329
+ }
330
+
331
+ /**
332
+ * Process meta fields.
333
+ *
334
+ * @since 3.0.0
335
+ *
336
+ * @param int $post_id
337
+ */
338
+ public function process_meta( $post_id ) {
339
+
340
+ $calendar_id = isset( $_POST['_google_calendar_id'] ) ? base64_encode( trim( $_POST['_google_calendar_id'] ) ): '';
341
+ update_post_meta( $post_id, '_google_calendar_id', $calendar_id );
342
+
343
+ $search_query = isset( $_POST['_google_events_search_query'] ) ? sanitize_text_field( $_POST['_google_events_search_query'] ) : '';
344
+ update_post_meta( $post_id, '_google_events_search_query', $search_query );
345
+
346
+ $recurring = isset( $_POST['_google_events_recurring'] ) ? sanitize_key( $_POST['_google_events_recurring'] ) : 'show';
347
+ update_post_meta( $post_id, '_google_events_recurring', $recurring );
348
+
349
+ $max_results = isset( $_POST['_google_events_max_results'] ) ? absint( $_POST['_google_events_max_results'] ) : '2500';
350
+ update_post_meta( $post_id, '_google_events_max_results', $max_results );
351
+
352
+ $this->test_api_key_connection( $calendar_id );
353
+ }
354
+
355
+ }
includes/feeds/admin/grouped-calendars-admin.php ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Grouped Calendars Feed - Admin
4
+ *
5
+ * @package SimpleCalendar/Feeds
6
+ */
7
+ namespace SimpleCalendar\Feeds\Admin;
8
+
9
+ use SimpleCalendar\Feeds\Grouped_Calendars;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Grouped calendars feed admin.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Grouped_Calendars_Admin {
21
+
22
+ /**
23
+ * Grouped Calendars feed object.
24
+ *
25
+ * @access private
26
+ * @var Grouped_Calendars
27
+ */
28
+ private $feed = null;
29
+
30
+ /**
31
+ * Hook in tabs.
32
+ *
33
+ * @since 3.0.0
34
+ *
35
+ * @param Grouped_Calendars $feed
36
+ */
37
+ public function __construct( Grouped_Calendars $feed ) {
38
+
39
+ $this->feed = $feed;
40
+
41
+ if ( 'calendar' == simcal_is_admin_screen() ) {
42
+ add_filter( 'simcal_settings_meta_tabs_li', array( $this, 'add_settings_meta_tab_li' ), 10, 1 );
43
+ add_action( 'simcal_settings_meta_panels', array( $this, 'add_settings_meta_panel' ), 10, 1 );
44
+ }
45
+ add_action( 'simcal_process_settings_meta', array( $this, 'process_meta' ), 10, 1 );
46
+ }
47
+
48
+ /**
49
+ * Add a tab to the settings meta box.
50
+ *
51
+ * @since 3.0.0
52
+ *
53
+ * @param array $tabs
54
+ *
55
+ * @return array
56
+ */
57
+ public function add_settings_meta_tab_li( $tabs ) {
58
+ return array_merge( $tabs, array(
59
+ 'grouped-calendars' => array(
60
+ 'label' => $this->feed->name,
61
+ 'target' => 'grouped-calendars-settings-panel',
62
+ 'class' => array(
63
+ 'simcal-feed-type',
64
+ 'simcal-feed-type-grouped-calendars',
65
+ ),
66
+ 'icon' => 'simcal-icon-docs',
67
+ ),
68
+ ));
69
+ }
70
+
71
+ /**
72
+ * Add a panel to the settings meta box.
73
+ *
74
+ * @since 3.0.0
75
+ *
76
+ * @param int $post_id
77
+ */
78
+ public function add_settings_meta_panel( $post_id ) {
79
+
80
+ ?>
81
+ <div id="grouped-calendars-settings-panel" class="simcal-panel">
82
+ <table>
83
+ <thead>
84
+ <tr><th colspan="2"><?php _e( 'Grouped Calendars settings', 'google-calendar-events' ); ?></th></tr>
85
+ </thead>
86
+ <tbody class="simcal-panel-section">
87
+ <tr class="simcal-panel-field">
88
+ <th><label for="_grouped_calendars_source"><?php _e( 'Get calendars from', 'google-calendar-events' ); ?></label></th>
89
+ <td>
90
+ <?php
91
+
92
+ $source = esc_attr( get_post_meta( $post_id, '_grouped_calendars_source', true ) );
93
+ $source = empty( $source ) ? 'ids' : $source;
94
+
95
+ ?>
96
+ <select name="_grouped_calendars_source"
97
+ id="_grouped_calendars_source"
98
+ class="simcal-field simcal-field-select simcal-field-inline simcal-field-show-other"
99
+ data-show-field-on-choice="true">
100
+ <option value="ids" data-show-field="_grouped_calendars_ids" <?php selected( 'ids', $source, true ); ?>><?php _e( 'Manual selection', 'google-calendar-events' ); ?></option>
101
+ <option value="category" data-show-field="_grouped_calendars_category" <?php selected( 'category', $source, true ); ?>><?php _e( 'Category', 'google-calendar-events' ); ?></option>
102
+ </select>
103
+ <i class="simcal-icon-help simcal-help-tip" data-tip="<?php _e( 'Choose from which calendar feeds you want to get events from. Choose them individually or select all those belonging to calendar feed categories.', 'google-calendar-events' ); ?>"></i>
104
+ <br><br>
105
+ <?php
106
+
107
+ $cals = simcal_get_calendars( $post_id );
108
+ $meta = get_post_meta( $post_id, '_grouped_calendars_ids', true );
109
+ $ids = $meta && is_array( $meta ) ? implode( ',', array_map( 'absint', $meta ) ) : absint( $meta );
110
+
111
+ simcal_print_field( array(
112
+ 'type' => 'select',
113
+ 'multiselect' => 'multiselect',
114
+ 'name' => '_grouped_calendars_ids',
115
+ 'id' => '_grouped_calendars_ids',
116
+ 'value' => $ids !== 0 ? $ids : '',
117
+ 'options' => $cals,
118
+ 'enhanced' => 'enhanced',
119
+ 'style' => 'ids' == $source ? '' : array( 'display' => 'none' ),
120
+ 'attributes' => array(
121
+ 'data-noresults' => __( 'No results found.', 'google-calendar-events' ),
122
+ ),
123
+ ));
124
+
125
+ $meta = get_post_meta( $post_id, '_grouped_calendars_category', true );
126
+ $category = $meta && is_array( $meta ) ? implode( ',', array_map( 'absint', $meta ) ): '';
127
+
128
+ $terms = get_terms( 'calendar_category' );
129
+
130
+ if ( ! empty( $terms ) ) {
131
+
132
+ $categories = array();
133
+ foreach ( $terms as $term ) {
134
+ $categories[ $term->term_id ] = $term->name;
135
+ }
136
+
137
+ simcal_print_field( array(
138
+ 'type' => 'select',
139
+ 'multiselect' => 'multiselect',
140
+ 'name' => '_grouped_calendars_category',
141
+ 'id' => '_grouped_calendars_category',
142
+ 'value' => $category,
143
+ 'options' => $categories,
144
+ 'enhanced' => 'enhanced',
145
+ 'style' => 'category' == $source ? '' : array( 'display' => 'none' ),
146
+ 'attributes' => array(
147
+ 'data-noresults' => __( 'No results found.', 'google-calendar-events' ),
148
+ ),
149
+ ) );
150
+
151
+ } else {
152
+
153
+ $style = 'category' == $source ? '' : 'display: none;';
154
+ $style .= ' width: 100%; max-width: 500px';
155
+ echo '<input type="text" disabled="disabled" name="_grouped_calendars_category" id="_grouped_calendars_category" style="' . $style . '" placeholder="' . __( 'There are no calendar categories yet.', 'google-calendar-events' ) . '" />';
156
+
157
+ }
158
+
159
+ ?>
160
+ </td>
161
+ </tr>
162
+ </tbody>
163
+ </table>
164
+ </div>
165
+ <?php
166
+
167
+ }
168
+
169
+ /**
170
+ * Process meta fields.
171
+ *
172
+ * @since 3.0.0
173
+ *
174
+ * @param int $post_id
175
+ */
176
+ public function process_meta( $post_id ) {
177
+
178
+ $source = isset( $_POST['_grouped_calendars_source'] ) ? sanitize_key( $_POST['_grouped_calendars_source'] ) : 'ids';
179
+ update_post_meta( $post_id, '_grouped_calendars_source', $source );
180
+
181
+ $ids = isset( $_POST['_grouped_calendars_ids'] ) ? array_map( 'absint', $_POST['_grouped_calendars_ids'] ) : '';
182
+ update_post_meta( $post_id, '_grouped_calendars_ids', $ids );
183
+
184
+ $category = isset( $_POST['_grouped_calendars_category'] ) ? array_map( 'absint', $_POST['_grouped_calendars_category'] ) : '';
185
+ update_post_meta( $post_id, '_grouped_calendars_category', $category );
186
+
187
+ }
188
+
189
+ }
includes/feeds/google.php ADDED
@@ -0,0 +1,426 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Google Calendar Feed
4
+ *
5
+ * @package SimpleCalendar/Feeds
6
+ */
7
+ namespace SimpleCalendar\Feeds;
8
+
9
+ use Carbon\Carbon;
10
+ use Carbon\CarbonInterval;
11
+ use SimpleCalendar\Abstracts\Calendar;
12
+ use SimpleCalendar\Abstracts\Feed;
13
+ use SimpleCalendar\Feeds\Admin\Google_Admin as Admin;
14
+
15
+ if ( ! defined( 'ABSPATH' ) ) {
16
+ exit;
17
+ }
18
+
19
+ /**
20
+ * Google Calendar feed.
21
+ *
22
+ * A feed using a simple Google API key to pull events from public calendars.
23
+ *
24
+ * @since 3.0.0
25
+ */
26
+ class Google extends Feed {
27
+
28
+
29
+ /**
30
+ * Google API Client.
31
+ *
32
+ * @access private
33
+ * @var \Google_Client
34
+ */
35
+ protected $google_client = null;
36
+
37
+ /**
38
+ * Client scopes.
39
+ *
40
+ * @access private
41
+ * @var array
42
+ */
43
+ protected $google_client_scopes = array();
44
+
45
+ /**
46
+ * Google Calendar API key.
47
+ *
48
+ * @access protected
49
+ * @var string
50
+ */
51
+ protected $google_api_key = '';
52
+
53
+ /**
54
+ * Google Calendar ID.
55
+ *
56
+ * @access protected
57
+ * @var string
58
+ */
59
+ protected $google_calendar_id = '';
60
+
61
+ /**
62
+ * Google recurring events query setting.
63
+ *
64
+ * @access protected
65
+ * @var string
66
+ */
67
+ protected $google_events_recurring = '';
68
+
69
+ /**
70
+ * Google search query setting.
71
+ *
72
+ * @access protected
73
+ * @var string
74
+ */
75
+ protected $google_search_query = '';
76
+
77
+ /**
78
+ * Google max results query setting.
79
+ *
80
+ * @access protected
81
+ * @var int
82
+ */
83
+ protected $google_max_results = 2500;
84
+
85
+ /**
86
+ * Set properties.
87
+ *
88
+ * @since 3.0.0
89
+ *
90
+ * @param string|Calendar $calendar
91
+ * @param bool $load_admin
92
+ */
93
+ public function __construct( $calendar = '', $load_admin = true ) {
94
+
95
+ parent::__construct( $calendar );
96
+
97
+ $this->type = 'google';
98
+ $this->name = __( 'Google Calendar', 'google-calendar-events' );
99
+
100
+ // Google client config.
101
+ $settings = get_option( 'simple-calendar_settings_feeds' );
102
+ $this->google_api_key = isset( $settings['google']['api_key'] ) ? esc_attr( $settings['google']['api_key'] ) : '';
103
+ $this->google_client_scopes = array( \Google_Service_Calendar::CALENDAR_READONLY );
104
+ $this->google_client = $this->get_client();
105
+
106
+ if ( $this->post_id > 0 ) {
107
+
108
+ // Google query args.
109
+ $this->google_calendar_id = $this->esc_google_calendar_id( get_post_meta( $this->post_id, '_google_calendar_id', true ) );
110
+ $this->google_events_recurring = esc_attr( get_post_meta( $this->post_id, '_google_events_recurring', true ) );
111
+ $this->google_search_query = esc_attr( get_post_meta( $this->post_id, '_google_events_search_query', true ) );
112
+ $this->google_max_results = max( absint( get_post_meta( $this->post_id, '_google_events_max_results', true ) ), 1 );
113
+
114
+ if ( ! is_admin() || defined( 'DOING_AJAX' ) ) {
115
+ $this->events = ! empty( $this->google_api_key ) ? $this->get_events() : array();
116
+ }
117
+ }
118
+
119
+ if ( is_admin() && $load_admin ) {
120
+ $admin = new Admin( $this, $this->google_api_key, $this->google_calendar_id );
121
+ $this->settings = $admin->settings_fields();
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Decode a calendar id.
127
+ *
128
+ * @since 3.0.0
129
+ *
130
+ * @param string $id Base64 encoded id.
131
+ *
132
+ * @return string
133
+ */
134
+ public function esc_google_calendar_id( $id ) {
135
+ return base64_decode( $id );
136
+ }
137
+
138
+ /**
139
+ * Get events feed.
140
+ *
141
+ * Normalizes Google data into a standard array object to list events.
142
+ *
143
+ * @since 3.0.0
144
+ *
145
+ * @return string|array
146
+ */
147
+ public function get_events() {
148
+
149
+ $calendar = get_transient( '_simple-calendar_feed_id_' . strval( $this->post_id ) . '_' . $this->type );
150
+
151
+ if ( empty( $calendar ) && ! empty( $this->google_calendar_id ) ) {
152
+
153
+ $error = '';
154
+
155
+ try {
156
+ $response = $this->make_request( $this->google_calendar_id );
157
+ } catch ( \Exception $e ) {
158
+ $error .= $e->getMessage();
159
+ }
160
+
161
+ if ( empty( $error ) && isset( $response['events'] ) && isset( $response['timezone'] ) ) {
162
+
163
+ $calendar = array_merge( $response, array( 'events' => array() ) );
164
+
165
+ // If no timezone has been set, use calendar feed.
166
+ if ( 'use_calendar' == $this->timezone_setting ) {
167
+ $this->timezone = $calendar['timezone'];
168
+ }
169
+
170
+ $source = isset( $response['title'] ) ? sanitize_text_field( $response['title'] ) : '';
171
+
172
+ if ( ! empty( $response['events'] ) && is_array( $response['events'] ) ) {
173
+ foreach ( $response['events'] as $event ) {
174
+ if ( $event instanceof \Google_Service_Calendar_Event ) {
175
+
176
+ // Visibility.
177
+ $visibility = $event->getVisibility();
178
+ // Public calendars may have private events which can't be properly accessed by simple api key method.
179
+ if ( $this->type == 'google' && ( $visibility == 'private' || $visibility == 'confidential' ) ) {
180
+ continue;
181
+ }
182
+
183
+ // Event title & description.
184
+ $title = strip_tags( $event->getSummary() );
185
+ $title = sanitize_text_field( iconv( mb_detect_encoding( $title, mb_detect_order(), true ), 'UTF-8', $title ) );
186
+ $description = wp_kses_post( iconv( mb_detect_encoding( $event->getDescription(), mb_detect_order(), true ), 'UTF-8', $event->getDescription() ) );
187
+
188
+ $whole_day = false;
189
+
190
+ // Event start properties.
191
+ $start_timezone = ! $event->getStart()->timeZone ? $calendar['timezone'] : $event->getStart()->timeZone;
192
+ if ( is_null( $event->getStart()->dateTime ) ) {
193
+ // Whole day event.
194
+ $date = Carbon::parse( $event->getStart()->date );
195
+ $google_start = Carbon::createFromDate( $date->year, $date->month, $date->day, $start_timezone )->startOfDay()->addSeconds( 59 );
196
+ $google_start_utc = Carbon::createFromDate( $date->year, $date->month, $date->day, 'UTC' )->startOfDay()->addSeconds( 59 );
197
+ $whole_day = true;
198
+ } else {
199
+ $date = Carbon::parse( $event->getStart()->dateTime );
200
+ $google_start = Carbon::create( $date->year, $date->month, $date->day, $date->hour, $date->minute, $date->second, $start_timezone );
201
+ $google_start_utc = Carbon::create( $date->year, $date->month, $date->day, $date->hour, $date->minute, $date->second, 'UTC' );
202
+ }
203
+ // Start.
204
+ $start = $google_start->getTimestamp();
205
+ // Start UTC.
206
+ $start_utc = $google_start_utc->getTimestamp();
207
+
208
+ $end = $end_utc = $end_timezone = '';
209
+ $span = 0;
210
+ if ( false == $event->getEndTimeUnspecified() ) {
211
+
212
+ // Event end properties.
213
+ $end_timezone = ! $event->getEnd()->timeZone ? $calendar['timezone'] : $event->getEnd()->timeZone;
214
+ if ( is_null( $event->getEnd()->dateTime ) ) {
215
+ // Whole day event.
216
+ $date = Carbon::parse( $event->getEnd()->date );
217
+ $google_end = Carbon::createFromDate( $date->year, $date->month, $date->day, $end_timezone )->endOfDay()->subSeconds( 59 );
218
+ $google_end_utc = Carbon::createFromDate( $date->year, $date->month, $date->day, 'UTC' )->endOfDay()->subSeconds( 59 );
219
+ } else {
220
+ $date = Carbon::parse( $event->getEnd()->dateTime );
221
+ $google_end = Carbon::create( $date->year, $date->month, $date->day, $date->hour, $date->minute, $date->second, $end_timezone );
222
+ $google_end_utc = Carbon::create( $date->year, $date->month, $date->day, $date->hour, $date->minute, $date->second, 'UTC' );
223
+ }
224
+ // End.
225
+ $end = $google_end->getTimestamp();
226
+ // End UTC.
227
+ $end_utc = $google_end_utc->getTimestamp();
228
+
229
+ // Count multiple days.
230
+ $span = $google_start->setTimezone( $calendar['timezone'] )->diffInDays( $google_end->setTimezone( $calendar['timezone'] ) );
231
+ }
232
+
233
+ // Multiple days.
234
+ $multiple_days = $span > 1 ? $span : false;
235
+
236
+ // Google cannot have two different locations for start and end time.
237
+ $start_location = $end_location = $event->getLocation();
238
+
239
+ // Recurring event.
240
+ $recurrence = $event->getRecurrence();
241
+ $recurring_id = $event->getRecurringEventId();
242
+ if ( ! $recurrence && $recurring_id ) {
243
+ $recurrence = true;
244
+ }
245
+
246
+ // Event link.
247
+ if ( 'use_calendar' == $this->timezone_setting ) {
248
+ $link = add_query_arg( array( 'ctz' => $this->timezone ), $event->getHtmlLink() );
249
+ } else {
250
+ $link = $event->getHtmlLink();
251
+ }
252
+
253
+ // Build the event.
254
+ $calendar['events'][ intval( $start ) ][] = array(
255
+ 'type' => 'google-calendar',
256
+ 'source' => $source,
257
+ 'title' => $title,
258
+ 'description' => $description,
259
+ 'link' => $link,
260
+ 'visibility' => $visibility,
261
+ 'uid' => $event->getICalUID(),
262
+ 'calendar' => $this->post_id,
263
+ 'timezone' => $this->timezone,
264
+ 'start' => $start,
265
+ 'start_utc' => $start_utc,
266
+ 'start_timezone' => $start_timezone,
267
+ 'start_location' => $start_location,
268
+ 'end' => $end,
269
+ 'end_utc' => $end_utc,
270
+ 'end_timezone' => $end_timezone,
271
+ 'end_location' => $end_location,
272
+ 'whole_day' => $whole_day,
273
+ 'multiple_days' => $multiple_days,
274
+ 'recurrence' => $recurrence,
275
+ 'template' => $this->events_template,
276
+ );
277
+
278
+ }
279
+ }
280
+
281
+ if ( ! empty( $calendar['events'] ) ) {
282
+
283
+ ksort( $calendar['events'], SORT_NUMERIC );
284
+
285
+ set_transient(
286
+ '_simple-calendar_feed_id_' . strval( $this->post_id ) . '_' . $this->type,
287
+ $calendar,
288
+ max( absint( $this->cache ), 60 )
289
+ );
290
+ }
291
+ }
292
+
293
+ } else {
294
+
295
+ $message = __( 'While trying to retrieve events, Google returned an error:', 'google-calendar-events' );
296
+ $message .= '<br><br>' . $error . '<br><br>';
297
+ $message .= __( 'Please ensure that both your Google Calendar ID and API Key are valid and that the Google Calendar you want to display is public.', 'google-calendar-events' ) . '<br><br>';
298
+ $message .= __( 'Only you can see this notice.', 'google-calendar-events' );
299
+
300
+ return $message;
301
+ }
302
+
303
+ }
304
+
305
+ // If no timezone has been set, use calendar feed.
306
+ if ( 'use_calendar' == $this->timezone_setting && isset( $calendar['timezone'] ) ) {
307
+ $this->timezone = $calendar['timezone'];
308
+ }
309
+
310
+ return isset( $calendar['events'] ) ? $calendar['events'] : array();
311
+ }
312
+
313
+ /**
314
+ * Query Google Calendar.
315
+ *
316
+ * @since 3.0.0
317
+ *
318
+ * @param string $id A valid Google Calendar ID.
319
+ * @param int $time_min Lower bound timestamp.
320
+ * @param int $time_max Upper bound timestamp.
321
+ *
322
+ * @return array
323
+ *
324
+ * @throws \Exception On request failure will throw an exception from Google.
325
+ */
326
+ public function make_request( $id = '', $time_min = 0, $time_max = 0 ) {
327
+
328
+ $calendar = array();
329
+ $google = $this->get_service();
330
+
331
+ if ( ! is_null( $google ) && ! empty( $id ) ) {
332
+
333
+ // Build the request args.
334
+ $args = array();
335
+
336
+ // Expand recurring events.
337
+ if ( $this->google_events_recurring == 'show' ) {
338
+ $args['singleEvents'] = true;
339
+ }
340
+
341
+ // Query events using search terms.
342
+ if ( ! empty( $this->google_search_query ) ) {
343
+ $args['q'] = rawurlencode( $this->google_search_query );
344
+ }
345
+
346
+ // Max results to query.
347
+ $args['maxResults'] = strval( min( absint( $this->google_max_results ), 2500 ) );
348
+
349
+ // Specify a timezone.
350
+ $timezone = '';
351
+ if ( 'use_calendar' != get_post_meta( $this->post_id, '_feed_timezone_setting', true ) ) {
352
+ $args['timeZone'] = $timezone = $this->timezone;
353
+ }
354
+
355
+ // Lower bound (inclusive) for an event's end time to filter by.
356
+ $earliest_event = intval( $this->time_min );
357
+ if ( $earliest_event > 0 ) {
358
+ $timeMin = Carbon::now();
359
+ if ( ! empty( $timezone ) ) {
360
+ $timeMin->setTimezone( $timezone );
361
+ }
362
+ $timeMin->setTimestamp( $earliest_event );
363
+ $args['timeMin'] = $timeMin->toRfc3339String();
364
+ }
365
+
366
+ // Upper bound (exclusive) for an event's start time to filter by.
367
+ $latest_event = intval( $this->time_max );
368
+ if ( $latest_event > 0 ) {
369
+ $timeMax = Carbon::now();
370
+ if ( ! empty( $timezone ) ) {
371
+ $timeMax->setTimezone( $timezone );
372
+ }
373
+ $timeMax->setTimestamp( $latest_event );
374
+ $args['timeMax'] = $timeMax->toRfc3339String();
375
+ }
376
+
377
+ // Query events in calendar.
378
+ $response = $google->events->listEvents( $id, $args );
379
+
380
+ if ( $response instanceof \Google_Service_Calendar_Events ) {
381
+ $calendar = array(
382
+ 'id' => $id,
383
+ 'title' => $response->getSummary(),
384
+ 'description' => $response->getDescription(),
385
+ 'timezone' => $response->getTimeZone(),
386
+ 'url' => esc_url( '//www.google.com/calendar/embed?src=' . $id ),
387
+ 'events' => $response->getItems(),
388
+ );
389
+ }
390
+ }
391
+
392
+ return $calendar;
393
+ }
394
+
395
+ /**
396
+ * Google API Client.
397
+ *
398
+ * @since 3.0.0
399
+ * @access private
400
+ *
401
+ * @return \Google_Client
402
+ */
403
+ private function get_client() {
404
+
405
+ $client = new \Google_Client();
406
+ $client->setApplicationName( 'Simple Calendar' );
407
+ $client->setScopes( $this->google_client_scopes );
408
+ $client->setDeveloperKey( $this->google_api_key );
409
+ $client->setAccessType( 'online' );
410
+
411
+ return $client;
412
+ }
413
+
414
+ /**
415
+ * Google Calendar Service.
416
+ *
417
+ * @since 3.0.0
418
+ * @access protected
419
+ *
420
+ * @return null|\Google_Service_Calendar
421
+ */
422
+ protected function get_service() {
423
+ return $this->google_client instanceof \Google_Client ? new \Google_Service_Calendar( $this->google_client ) : null;
424
+ }
425
+
426
+ }
includes/feeds/grouped-calendars.php ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Grouped Calendars Feed
4
+ *
5
+ * @package SimpleCalendar/Feeds
6
+ */
7
+ namespace SimpleCalendar\Feeds;
8
+
9
+ use SimpleCalendar\Abstracts\Calendar;
10
+ use SimpleCalendar\Abstracts\Feed;
11
+ use SimpleCalendar\Feeds\Admin\Grouped_Calendars_Admin;
12
+
13
+ if ( ! defined( 'ABSPATH' ) ) {
14
+ exit;
15
+ }
16
+
17
+ /**
18
+ * Grouped calendars feed.
19
+ *
20
+ * Feed made of multiple calendar feeds combined together.
21
+ *
22
+ * @since 3.0.0
23
+ */
24
+ class Grouped_Calendars extends Feed {
25
+
26
+ /**
27
+ * Feed ids to get events from.
28
+ *
29
+ * @access public
30
+ * @var array
31
+ */
32
+ public $calendars_ids = array();
33
+
34
+ /**
35
+ * Set properties.
36
+ *
37
+ * @since 3.0.0
38
+ *
39
+ * @param string|Calendar $calendar
40
+ */
41
+ public function __construct( $calendar = '' ) {
42
+
43
+ parent::__construct( $calendar );
44
+
45
+ $this->type = 'grouped-calendars';
46
+ $this->name = __( 'Grouped Calendars', 'google-calendar-events' );
47
+
48
+ if ( $this->post_id > 0 ) {
49
+ $this->set_source();
50
+ if ( ! is_admin() || defined( 'DOING_AJAX' ) ) {
51
+ $this->events = $this->get_events();
52
+ }
53
+ }
54
+
55
+ if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
56
+ new Grouped_Calendars_Admin( $this );
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Set source.
62
+ *
63
+ * @since 3.0.0
64
+ *
65
+ * @param array $ids Array of calendar ids.
66
+ */
67
+ public function set_source( $ids = array() ) {
68
+
69
+ $source = get_post_meta( $this->post_id, '_grouped_calendars_source', true );
70
+
71
+ if ( 'ids' == $source ) {
72
+
73
+ if ( empty( $ids ) ) {
74
+ $ids = get_post_meta( $this->post_id, '_grouped_calendars_ids', true );
75
+ }
76
+
77
+ $this->calendars_ids = ! empty( $ids ) && is_array( $ids ) ? array_map( 'absint', $ids ) : array();
78
+
79
+ } elseif ( 'category' == $source ) {
80
+
81
+ $categories = get_post_meta( $this->post_id, '_grouped_calendars_category', true );
82
+
83
+ if ( $categories && is_array( $categories ) ) {
84
+
85
+ $tax_query = array(
86
+ 'taxonomy' => 'events_feed_category',
87
+ 'field' => 'term_id',
88
+ 'terms' => array_map( 'absint', $categories ),
89
+ );
90
+
91
+ $calendars = get_posts( array(
92
+ 'post_type' => 'calendar',
93
+ 'tax_query' => $tax_query,
94
+ 'nopaging' => true,
95
+ 'fields' => 'ids',
96
+ ) );
97
+
98
+ $this->calendars_ids = ! empty( $calendars ) && is_array( $calendars ) ? $calendars : array();
99
+ }
100
+
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Get events from multiple calendars.
106
+ *
107
+ * @since 3.0.0
108
+ *
109
+ * @return array
110
+ */
111
+ public function get_events() {
112
+
113
+ $ids = $this->calendars_ids;
114
+ $events = get_transient( '_simple-calendar_feed_id_' . strval( $this->post_id ) . '_' . $this->type );
115
+
116
+ if ( empty( $events ) && ! empty( $ids ) && is_array( $ids ) ) {
117
+
118
+ $events = array();
119
+
120
+ foreach ( $ids as $cal_id ) {
121
+
122
+ $calendar = simcal_get_calendar( intval( $cal_id ) );
123
+
124
+ if ( $calendar instanceof Calendar ) {
125
+ $events = is_array( $calendar->events ) ? $events + $calendar->events : $events;
126
+ }
127
+
128
+ }
129
+
130
+ if ( ! empty( $events ) ) {
131
+
132
+ // Trim events to set the earliest one as specified in feed settings.
133
+ $earliest_event = intval( $this->time_min );
134
+ if ( $earliest_event > 0 ) {
135
+ $events = $this->array_filter_key( $events, array( $this, 'filter_events_before' ) );
136
+ }
137
+
138
+ // Trim events to set the latest one as specified in feed settings.
139
+ $latest_event = intval( $this->time_max );
140
+ if ( $latest_event > 0 ) {
141
+ $events = $this->array_filter_key( $events, array( $this, 'filter_events_after' ) );
142
+ }
143
+
144
+ set_transient(
145
+ '_simple-calendar_feed_id_' . strval( $this->post_id ) . '_' . $this->type,
146
+ $events,
147
+ absint( $this->cache )
148
+ );
149
+ }
150
+
151
+ }
152
+
153
+ return $events;
154
+ }
155
+
156
+ /**
157
+ * Array filter key.
158
+ *
159
+ * `array_filter` does not allow to parse an associative array keys before PHP 5.6.
160
+ *
161
+ * @since 3.0.0
162
+ * @access private
163
+ *
164
+ * @param array $array
165
+ * @param array|string $callback
166
+ *
167
+ * @return array
168
+ */
169
+ private function array_filter_key( array $array, $callback ) {
170
+ $matched_keys = array_filter( array_keys( $array ), $callback );
171
+ return array_intersect_key( $array, array_flip( $matched_keys ) );
172
+ }
173
+
174
+ /**
175
+ * Array filter callback.
176
+ *
177
+ * @since 3.0.0
178
+ * @access private
179
+ *
180
+ * @param int $event Timestamp.
181
+ *
182
+ * @return bool
183
+ */
184
+ private function filter_events_before( $event ) {
185
+ if ( $this->time_min !== 0 ) {
186
+ return intval( $event ) > intval( $this->time_min );
187
+ }
188
+ return true;
189
+ }
190
+
191
+ /**
192
+ * Array filter callback.
193
+ *
194
+ * @since 3.0.0
195
+ * @access private
196
+ *
197
+ * @param int $event Timestamp.
198
+ *
199
+ * @return bool
200
+ */
201
+ private function filter_events_after( $event ) {
202
+ if ( $this->time_max !== 0 ) {
203
+ return intval( $event ) < intval( $this->time_max );
204
+ }
205
+ return true;
206
+ }
207
+
208
+ }
includes/functions/admin.php ADDED
@@ -0,0 +1,450 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin functions
4
+ *
5
+ * Functions for the admin back end components only.
6
+ *
7
+ * @package SimpleCalendar/Admin/Functions
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit;
12
+ }
13
+
14
+ /**
15
+ * Get settings pages and tabs.
16
+ *
17
+ * @since 3.0.0
18
+ *
19
+ * @return array
20
+ */
21
+ function simcal_get_admin_pages() {
22
+ $objects = \SimpleCalendar\plugin()->objects;
23
+ return $objects instanceof \SimpleCalendar\Objects ? $objects->get_admin_pages() : array();
24
+ }
25
+
26
+ /**
27
+ * Get a settings page tab.
28
+ *
29
+ * @since 3.0.0
30
+ *
31
+ * @param string $page
32
+ *
33
+ * @return null|\SimpleCalendar\Abstracts\Admin_Page
34
+ */
35
+ function simcal_get_admin_page( $page ) {
36
+ $objects = \SimpleCalendar\plugin()->objects;
37
+ return $objects instanceof \SimpleCalendar\Objects ? $objects->get_admin_page( $page ) : null;
38
+ }
39
+
40
+ /**
41
+ * Get a field.
42
+ *
43
+ * @since 3.0.0
44
+ *
45
+ * @param array $args
46
+ * @param string $name
47
+ *
48
+ * @return null|\SimpleCalendar\Abstracts\Field
49
+ */
50
+ function simcal_get_field( $args, $name = '' ) {
51
+ $objects = \SimpleCalendar\plugin()->objects;
52
+ return $objects instanceof \SimpleCalendar\Objects ? $objects->get_field( $args, $name ) : null;
53
+ }
54
+
55
+ /**
56
+ * Print a field.
57
+ *
58
+ * @since 3.0.0
59
+ *
60
+ * @param array $args
61
+ * @param string $name
62
+ *
63
+ * @return void
64
+ */
65
+ function simcal_print_field( $args, $name = '' ) {
66
+
67
+ $field = simcal_get_field( $args, $name );
68
+
69
+ if ( $field instanceof \SimpleCalendar\Abstracts\Field ) {
70
+ $field->html();
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Clear feed transients cache.
76
+ *
77
+ * @since 3.0.0
78
+ *
79
+ * @param string|int|array|\WP_Post $id
80
+ *
81
+ * @return bool
82
+ */
83
+ function simcal_delete_feed_transients( $id = '' ) {
84
+
85
+ if ( is_numeric( $id ) ) {
86
+ $id = intval( $id ) > 0 ? absint( $id ) : simcal_get_calendars();
87
+ } elseif ( $id instanceof WP_Post ) {
88
+ $id = $id->ID;
89
+ } elseif ( is_array( $id ) ) {
90
+ $id = array_map( 'absint', $id );
91
+ } else {
92
+ $id = simcal_get_calendars( '', true );
93
+ }
94
+
95
+ $feed_types = simcal_get_feed_types();
96
+
97
+ if ( is_array( $id ) ) {
98
+
99
+ $posts = get_posts( array(
100
+ 'post_type' => 'calendar',
101
+ 'fields' => 'ids',
102
+ 'post__in' => $id,
103
+ 'nopaging' => true,
104
+ ) );
105
+
106
+ foreach ( $posts as $post ) {
107
+ $calendar = simcal_get_calendar( $post );
108
+ if ( $calendar instanceof \SimpleCalendar\Abstracts\Calendar ) {
109
+ foreach ( $feed_types as $feed_type ) {
110
+ delete_transient( '_simple-calendar_feed_id_' . strval( $calendar->id ) . '_' . $feed_type );
111
+ }
112
+ }
113
+ }
114
+
115
+ } else {
116
+
117
+ $post = get_post( $id );
118
+ $calendar = simcal_get_calendar( $post );
119
+ if ( $calendar instanceof \SimpleCalendar\Abstracts\Calendar ) {
120
+ foreach ( $feed_types as $feed_type ) {
121
+ delete_transient( '_simple-calendar_feed_id_' . strval( $calendar->id ) . '_' . $feed_type );
122
+ }
123
+ }
124
+ }
125
+
126
+ return delete_transient( '_simple-calendar_feed_ids' );
127
+ }
128
+
129
+ /**
130
+ * Sanitize a variable of unknown type.
131
+ *
132
+ * Recursive helper function to sanitize a variable from input,
133
+ * which could also be a multidimensional array of variable depth.
134
+ *
135
+ * @since 3.0.0
136
+ *
137
+ * @param mixed $var Variable to sanitize.
138
+ * @param string $func Function to use for sanitizing text strings (default 'sanitize_text_field')
139
+ *
140
+ * @return array|string Sanitized variable
141
+ */
142
+ function simcal_sanitize_input( $var, $func = 'sanitize_text_field' ) {
143
+
144
+ if ( is_null( $var ) ) {
145
+ return '';
146
+ }
147
+
148
+ if ( is_bool( $var ) ) {
149
+ if ( $var === true ) {
150
+ return 'yes';
151
+ } else {
152
+ return 'no';
153
+ }
154
+ }
155
+
156
+ if ( is_string( $var ) || is_numeric( $var ) ) {
157
+ $func = is_string( $func ) && function_exists( $func ) ? $func : 'sanitize_text_field';
158
+ return call_user_func( $func, trim( strval( $var ) ) );
159
+ }
160
+
161
+ if ( is_object( $var ) ) {
162
+ $var = (array) $var;
163
+ }
164
+
165
+ if ( is_array( $var ) ) {
166
+ $array = array();
167
+ foreach ( $var as $k => $v ) {
168
+ $array[ $k ] = simcal_sanitize_input( $v );
169
+ }
170
+ return $array;
171
+ }
172
+
173
+ return '';
174
+ }
175
+
176
+ /**
177
+ * Check if a screen is a plugin admin view.
178
+ * Returns the screen id if true, false (bool) if not.
179
+ *
180
+ * @since 3.0.0
181
+ *
182
+ * @return string|bool
183
+ */
184
+ function simcal_is_admin_screen() {
185
+
186
+ $view = function_exists( 'get_current_screen' ) ? get_current_screen() : false;
187
+
188
+ if ( $view instanceof WP_Screen ) {
189
+
190
+ // Screens used by this plugin.
191
+ $screens = array(
192
+ 'customize',
193
+ 'calendar',
194
+ 'calendar_page_simple-calendar_add_ons',
195
+ 'calendar_page_simple-calendar_settings',
196
+ 'calendar_page_simple-calendar_tools',
197
+ 'edit-calendar',
198
+ 'edit-calendar_category',
199
+ 'dashboard_page_simple-calendar_about',
200
+ 'dashboard_page_simple-calendar_credits',
201
+ 'dashboard_page_simple-calendar_translators',
202
+ );
203
+ if ( in_array( $view->id, $screens ) ) {
204
+ return $view->id;
205
+ }
206
+ }
207
+
208
+ return false;
209
+ }
210
+
211
+ /**
212
+ * Update add-on.
213
+ *
214
+ * @since 3.0.0
215
+ *
216
+ * @param string $_api_url The URL pointing to the custom API end
217
+ * @param string $_plugin_file Path to the plugin file.
218
+ * @param array $_api_data Optional data to send with API calls.
219
+ *
220
+ * @return \SimpleCalendar\Admin\Updater
221
+ */
222
+ function simcal_addon_updater( $_api_url, $_plugin_file, $_api_data = null ) {
223
+ return new \SimpleCalendar\Admin\Updater( $_api_url, $_plugin_file, $_api_data );
224
+ }
225
+
226
+ /**
227
+ * Get add-on license key.
228
+ *
229
+ * @since 3.0.0
230
+ *
231
+ * @param string $addon Unique add-on id.
232
+ *
233
+ * @return null|string
234
+ */
235
+ function simcal_get_license_key( $addon ) {
236
+ $licenses = get_option( 'simple-calendar_settings_licenses', array() );
237
+ if ( isset( $licenses['keys'][ $addon ] ) ) {
238
+ return empty( $licenses['keys'][ $addon ] ) ? null : $licenses['keys'][ $addon ];
239
+ }
240
+ return null;
241
+ }
242
+
243
+ /**
244
+ * Get add-on license status.
245
+ *
246
+ * Without passing arguments returns all the statuses array.
247
+ *
248
+ * @since 3.0.0
249
+ *
250
+ * @param null|string $addon Unique add-on slug.
251
+ *
252
+ * @return array|string
253
+ */
254
+ function simcal_get_license_status( $addon = null ) {
255
+ $licenses = get_option( 'simple-calendar_licenses_status', array() );
256
+ return isset( $licenses[ $addon ] ) ? $licenses[ $addon ] : $licenses;
257
+ }
258
+
259
+ /**
260
+ * Get admin notices.
261
+ *
262
+ * @since 3.0.0
263
+ *
264
+ * @return array
265
+ */
266
+ function simcal_get_admin_notices() {
267
+ $notices = new \SimpleCalendar\Admin\Notices();
268
+ return $notices->get_notices();
269
+ }
270
+
271
+ /**
272
+ * Delete admin notices.
273
+ *
274
+ * @since 3.0.0
275
+ */
276
+ function simcal_delete_admin_notices() {
277
+ delete_option( 'simple-calendar_admin_notices' );
278
+ }
279
+
280
+ /**
281
+ * Print a shortcode tip.
282
+ *
283
+ * @since 3.0.0
284
+ *
285
+ * @param int $post_id
286
+ *
287
+ * @return void
288
+ */
289
+ function simcal_print_shortcode_tip( $post_id ) {
290
+
291
+ $browser = new \Browser();
292
+ if ( $browser::PLATFORM_APPLE == $browser->getPlatform() ) {
293
+ $cmd = '&#8984;&#43;C';
294
+ } else {
295
+ $cmd = 'Ctrl&#43;C';
296
+ }
297
+
298
+ $shortcut = sprintf( __( 'Press %s to copy.', 'google-calendar-events' ), $cmd );
299
+ $shortcode = sprintf( '[calendar id="%s"]', $post_id );
300
+
301
+ echo "<input readonly='readonly' " .
302
+ "class='simcal-shortcode simcal-calendar-shortcode simcal-shortcode-tip' " .
303
+ "title='" . $shortcut . "' " .
304
+ "onclick='this.select();' value='" . $shortcode . "' />";
305
+ }
306
+
307
+ /**
308
+ * Google Analytics campaign URL.
309
+ *
310
+ * @since 3.0.0
311
+ *
312
+ * @param string $base_url Plain URL to navigate to
313
+ * @param string $campaign GA "campaign" tracking value
314
+ * @param string $content GA "content" tracking value
315
+ * @param bool $raw Use esc_url_raw instead (default = false)
316
+ *
317
+ * @return string $url Full Google Analytics campaign URL
318
+ */
319
+ function simcal_ga_campaign_url( $base_url, $campaign, $content, $raw = false ) {
320
+
321
+ $url = add_query_arg( array(
322
+ 'utm_source' => 'inside-plugin',
323
+ 'utm_medium' => 'link',
324
+ 'utm_campaign' => $campaign, // i.e. 'core-plugin', 'gcal-pro'
325
+ 'utm_content' => $content // i.e. 'sidebar-link', 'settings-link'
326
+ ), $base_url );
327
+
328
+ if ( $raw ) {
329
+ return esc_url_raw( $url );
330
+ }
331
+
332
+ return esc_url( $url );
333
+ }
334
+
335
+ /**
336
+ * Newsletter signup form.
337
+ *
338
+ * @since 3.0.0
339
+ *
340
+ * @return void
341
+ */
342
+ function simcal_newsletter_signup() {
343
+
344
+ if ( $screen = simcal_is_admin_screen() ) {
345
+
346
+ global $current_user;
347
+ get_currentuserinfo();
348
+
349
+ $name = $current_user->user_firstname ? $current_user->user_firstname : '';
350
+
351
+ ?>
352
+ <div id="simcal-drip" class="<?php echo $screen; ?>">
353
+ <div class="signup">
354
+ <p>
355
+ <?php _e( "Enter your name and email and we'll send you a coupon code for 20% off our Google Calendar Pro add-on.", 'google-calendar-events' ); ?>
356
+ </p>
357
+
358
+ <p>
359
+ <label for="simcal-drip-field-email"><?php _e( 'Your Email', 'google-calendar-events' ); ?></label><br />
360
+ <input type="email"
361
+ id="simcal-drip-field-email"
362
+ name="fields[email]"
363
+ value="<?php echo $current_user->user_email; ?>" />
364
+ </p>
365
+
366
+ <p>
367
+ <label for="simcal-drip-field-first_name"><?php _e( 'First Name', 'google-calendar-events' ); ?></label><br />
368
+ <input type="text"
369
+ id="simcal-drip-field-first_name"
370
+ name="fields[first_name]"
371
+ value="<?php echo $name; ?>" />
372
+ </p>
373
+ <p class="textright">
374
+ <a href="#"
375
+ id="simcal-drip-signup"
376
+ class="button button-primary"><?php _e( 'Send me the coupon', 'google-calendar-events' ); ?></a>
377
+ </p>
378
+ <div class="textright">
379
+ <a href="<?php echo simcal_ga_campaign_url( simcal_get_url( 'gcal-pro' ), 'core-plugin', 'sidebar-link' ); ?>"
380
+ target="_blank"><?php _e( 'Just take me to GCal Pro', 'google-calendar-events' ); ?></a>
381
+ </div>
382
+ </div>
383
+ <div class="thank-you" style="display: none;">
384
+ <?php _e( 'Thank you!', 'google-calendar-events' ); ?>
385
+ </div>
386
+ <div class="clear">
387
+ </div>
388
+ </div>
389
+ <?php
390
+
391
+ }
392
+
393
+ }
394
+
395
+ if ( ! function_exists( 'mb_detect_encoding' ) ) {
396
+
397
+ /**
398
+ * Fallback function for `mb_detect_encoding()`,
399
+ * php_mbstring module in the php.ini could be missing.
400
+ *
401
+ * @since 3.0.0
402
+ *
403
+ * @param string $string
404
+ * @param null $enc
405
+ * @param null $ret
406
+ *
407
+ * @return bool
408
+ */
409
+ function mb_detect_encoding( $string, $enc = null, $ret = null ) {
410
+
411
+ static $enclist = array(
412
+ 'UTF-8',
413
+ 'ASCII',
414
+ 'ISO-8859-1',
415
+ 'ISO-8859-2',
416
+ 'ISO-8859-3',
417
+ 'ISO-8859-4',
418
+ 'ISO-8859-5',
419
+ 'ISO-8859-6',
420
+ 'ISO-8859-7',
421
+ 'ISO-8859-8',
422
+ 'ISO-8859-9',
423
+ 'ISO-8859-10',
424
+ 'ISO-8859-13',
425
+ 'ISO-8859-14',
426
+ 'ISO-8859-15',
427
+ 'ISO-8859-16',
428
+ 'Windows-1251',
429
+ 'Windows-1252',
430
+ 'Windows-1254',
431
+ );
432
+
433
+ $result = false;
434
+
435
+ foreach ( $enclist as $item ) {
436
+ $sample = iconv( $item, $item, $string );
437
+ if ( md5( $sample ) == md5( $string ) ) {
438
+ if ( $ret === null ) {
439
+ $result = $item;
440
+ } else {
441
+ $result = true;
442
+ }
443
+ break;
444
+ }
445
+ }
446
+
447
+ return $result;
448
+ }
449
+
450
+ }
includes/functions/deprecated.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Deprecated functions
4
+ *
5
+ * Functions that are kept for backward support but should not be used anymore.
6
+ *
7
+ * @package SimpleCalendar/Deprecated
8
+ * @deprecated
9
+ */
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Print a calendar.
17
+ * @deprecated Use simcal_print_calendar()
18
+ */
19
+ function gce_print_calendar( $feed_ids, $display, $args ) {
20
+
21
+ $id = 0;
22
+
23
+ if ( is_numeric( $feed_ids ) ) {
24
+ $id = intval( $feed_ids );
25
+ } elseif ( is_array( $feed_ids ) ) {
26
+ $id = isset( $feed_ids[0] ) ? intval( $feed_ids[0] ) : '';
27
+ }
28
+
29
+ if ( $id > 0 ) {
30
+ simcal_print_calendar( $id );
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Convert date from mm/dd/yyyy to unix timestamp.
36
+ * @deprecated Assumes a US-only time format.
37
+ */
38
+ function gce_date_unix( $date = '' ) {
39
+ if ( empty( $date ) ) {
40
+ $current_time = current_time( 'timestamp' );
41
+ $timestamp = mktime( 0, 0, 0, date( 'm', $current_time ), date( 'd', $current_time ), date( 'Y', $current_time ) );
42
+ } else {
43
+ $date = explode( '/', $date );
44
+ $month = $date[0];
45
+ $day = $date[1];
46
+ $year = $date[2];
47
+ $timestamp = mktime( 0, 0, 0, $month, $day, $year );
48
+ }
49
+ return $timestamp;
50
+ }
includes/functions/shared.php ADDED
@@ -0,0 +1,409 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Shared functions
4
+ *
5
+ * Functions shared by both back end and front end components.
6
+ *
7
+ * @package SimpleCalendar/Functions
8
+ */
9
+
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ exit;
12
+ }
13
+
14
+ /**
15
+ * Check if there is a calendar.
16
+ *
17
+ * @since 3.0.0
18
+ *
19
+ * @return bool
20
+ */
21
+ function is_simple_calendar() {
22
+
23
+ if ( is_singular() ) {
24
+
25
+ global $post, $post_type;
26
+
27
+ if ( 'calendar' == $post_type ) {
28
+ return true;
29
+ } else {
30
+ if ( false !== get_post_meta( $post->ID, '_simcal_attach_calendar_id', true ) ) {
31
+ return true;
32
+ }
33
+ if ( has_shortcode( $post->post_content, 'calendar' ) ) {
34
+ return true;
35
+ }
36
+ }
37
+ }
38
+
39
+ return false;
40
+ }
41
+
42
+ /**
43
+ * Get plugin URL.
44
+ *
45
+ * @param string $url
46
+ *
47
+ * @return string
48
+ */
49
+ function simcal_get_url( $url ) {
50
+ return \SimpleCalendar\plugin()->get_url( $url );
51
+ }
52
+
53
+ /**
54
+ * Get events feed types.
55
+ *
56
+ * @since 3.0.0
57
+ *
58
+ * @return array
59
+ */
60
+ function simcal_get_feed_types() {
61
+ $objects = \SimpleCalendar\plugin()->objects;
62
+ return $objects instanceof \SimpleCalendar\Objects ? $objects->get_feed_types() : array();
63
+ }
64
+
65
+ /**
66
+ * Get an events feed.
67
+ *
68
+ * @since 3.0.0
69
+ *
70
+ * @param string|int|object $object
71
+ *
72
+ * @return null|\SimpleCalendar\Abstracts\Feed
73
+ */
74
+ function simcal_get_feed( $object ) {
75
+ $objects = \SimpleCalendar\plugin()->objects;
76
+ return $objects instanceof \SimpleCalendar\Objects ? $objects->get_feed( $object ) : null;
77
+ }
78
+
79
+ /**
80
+ * Get calendar types.
81
+ *
82
+ * @since 3.0.0
83
+ *
84
+ * @return array
85
+ */
86
+ function simcal_get_calendar_types() {
87
+ $objects = \SimpleCalendar\plugin()->objects;
88
+ return $objects instanceof \SimpleCalendar\Objects ? $objects->get_calendar_types() : array();
89
+ }
90
+
91
+ /**
92
+ * Get a calendar.
93
+ *
94
+ * @since 3.0.0
95
+ *
96
+ * @param string|int|object|WP_Post $object
97
+ *
98
+ * @return null|\SimpleCalendar\Abstracts\Calendar
99
+ */
100
+ function simcal_get_calendar( $object ) {
101
+ $objects = \SimpleCalendar\plugin()->objects;
102
+ return $objects instanceof \SimpleCalendar\Objects ? $objects->get_calendar( $object ) : null;
103
+ }
104
+
105
+ /**
106
+ * Get a calendar view.
107
+ *
108
+ * @since 3.0.0
109
+ *
110
+ * @param int $id
111
+ * @param string $name
112
+ *
113
+ * @return mixed
114
+ */
115
+ function simcal_get_calendar_view( $id = 0, $name = '' ) {
116
+ $objects = \SimpleCalendar\plugin()->objects;
117
+ return $objects instanceof \SimpleCalendar\Objects ? $objects->get_calendar_view( $id, $name ) : false;
118
+ }
119
+
120
+ /**
121
+ * Print a calendar.
122
+ *
123
+ * @since 3.0.0
124
+ *
125
+ * @param int|object|WP_Post $object
126
+ *
127
+ * @return void
128
+ */
129
+ function simcal_print_calendar( $object ) {
130
+
131
+ $calendar = simcal_get_calendar( $object );
132
+
133
+ if ( $calendar instanceof \SimpleCalendar\Abstracts\Calendar ) {
134
+ $calendar->html();
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Common scripts variables.
140
+ *
141
+ * Variables to print in scripts localization
142
+ *
143
+ * @since 3.0.0
144
+ *
145
+ * @return array
146
+ */
147
+ function simcal_common_scripts_variables() {
148
+
149
+ $vars = array(
150
+ 'ajax_url' => \SimpleCalendar\plugin()->ajax_url(),
151
+ 'nonce' => wp_create_nonce( 'simcal' ),
152
+ 'locale' => \SimpleCalendar\plugin()->locale,
153
+ 'text_dir' => is_rtl() ? 'rtl' : 'ltr',
154
+ 'months' => array(
155
+ 'full' => simcal_get_calendar_names_i18n( 'month', 'full' ),
156
+ 'short' => simcal_get_calendar_names_i18n( 'month', 'short' ),
157
+ ),
158
+ 'days' => array(
159
+ 'full' => simcal_get_calendar_names_i18n( 'day', 'full' ),
160
+ 'short' => simcal_get_calendar_names_i18n( 'day', 'short' ),
161
+ ),
162
+ 'meridiem' => simcal_get_calendar_names_i18n( 'meridiem' ),
163
+ );
164
+
165
+ return array_merge( $vars, apply_filters( 'simcal_common_scripts_variables', array() ) );
166
+ }
167
+
168
+ /**
169
+ * Get feed IDs and names.
170
+ *
171
+ * @since 3.0.0
172
+ *
173
+ * @param string|int|array $exclude Id or array of ids to drop from results.
174
+ * @param bool $cached Use cached query.
175
+ *
176
+ * @return array Associative array with ids as keys and feed titles as values.
177
+ */
178
+ function simcal_get_calendars( $exclude = '', $cached = true ) {
179
+
180
+ $calendars = get_transient( '_simple-calendar_feed_ids' );
181
+
182
+ if ( ! $calendars || $cached === false ) {
183
+
184
+ $posts = get_posts( array(
185
+ 'post_type' => 'calendar',
186
+ 'nopaging' => true,
187
+ ) );
188
+
189
+ $calendars = array();
190
+ foreach ( $posts as $post ) {
191
+ $calendars[ $post->ID ] = $post->post_title;
192
+ }
193
+ asort( $calendars );
194
+
195
+ set_transient( '_simple-calendar_feed_ids', $calendars, 604800 );
196
+ }
197
+
198
+ if ( ! empty( $exclude ) ) {
199
+ if ( is_numeric( $exclude ) ) {
200
+ unset( $calendars[ intval( $exclude ) ] );
201
+ } elseif ( is_array( $exclude ) ) {
202
+ array_diff_key( $calendars, array_map( 'intval', array_keys( $exclude ) ) );
203
+ }
204
+ }
205
+
206
+ return $calendars;
207
+ }
208
+
209
+ /**
210
+ * Get localized list of months or day names.
211
+ *
212
+ * Each day or month matches the array index (0-11 for months or 0-6 for days).
213
+ *
214
+ * @since 3.0.0
215
+ *
216
+ * @param string $group Either 'month', 'day' or 'meridiem' names to localize.
217
+ * @param string $style Return names in 'short' or 'full' form (default full long form).
218
+ *
219
+ * @return array
220
+ */
221
+ function simcal_get_calendar_names_i18n( $group, $style = 'full' ) {
222
+
223
+ $names = array();
224
+
225
+ if ( in_array( $group, array( 'month', 'day', 'meridiem' ) ) ) {
226
+
227
+ $format = '';
228
+ $length = 0;
229
+
230
+ $date = Carbon\Carbon::now( 'UTC' );
231
+
232
+ if ( 'month' == $group ) {
233
+ $date->month( 0 )->startOfMonth();
234
+ $format = 'short' == $style ? 'M' : 'F';
235
+ $length = 11;
236
+ } elseif ( 'day' == $group ) {
237
+ $date->next( 6 );
238
+ $format = 'short' == $style ? 'D' : 'l';
239
+ $length = 6;
240
+ } elseif ( 'meridiem' == $group ) {
241
+ $date->startOfDay();
242
+ $am = $date->addHour( 1 )->getTimestamp();
243
+ $pm = $date->addHours( 13 )->getTimestamp();
244
+ return array(
245
+ 'AM' => date_i18n( 'A', $am ),
246
+ 'am' => date_i18n( 'a', $am ),
247
+ 'PM' => date_i18n( 'A', $pm ),
248
+ 'pm' => date_i18n( 'a', $pm ),
249
+ );
250
+ }
251
+
252
+ $i = 0;
253
+ while ( $i <= $length ) {
254
+ if ( 'month' == $group ) {
255
+ $date->addMonths( 1 );
256
+ } else {
257
+ $date->addDays( 1 );
258
+ }
259
+ $names[ strval( $i ) ] = date_i18n( $format, $date->getTimestamp() );
260
+ $i++;
261
+ }
262
+
263
+ }
264
+
265
+ return $names;
266
+ }
267
+
268
+ /**
269
+ * Default event template.
270
+ *
271
+ * @since 3.0.0
272
+ *
273
+ * @return string
274
+ */
275
+ function simcal_default_event_template() {
276
+
277
+ $content = '<strong>' . '[title]' . '</strong>';
278
+ $content .= '<p>';
279
+ $content .= '[when]' . "\n";
280
+ $content .= '[location]';
281
+ $content .= '</p>';
282
+ $content .= '<div>' . '[description]' . '</div>';
283
+ $content .= '<p>' . '[link newwindow="yes"]' . __( 'See more details', 'google-calendar-events' ) . '[/link]' . '</p>';
284
+
285
+ return apply_filters( 'simcal_default_event_template', $content );
286
+ }
287
+
288
+ /**
289
+ * Get day, month, year order in a datetime format string.
290
+ *
291
+ * Returns an array with d, m, y for keys and a order number.
292
+ * If either d, m, y is not found in date format, order value is false.
293
+ *
294
+ * @since 3.0.0
295
+ *
296
+ * @param string $date_format
297
+ *
298
+ * @return array
299
+ */
300
+ function simcal_get_date_format_order( $date_format ) {
301
+
302
+ $pos = array(
303
+ 'd' => strpos( $date_format, strpbrk( $date_format, 'Dj' ) ),
304
+ 'm' => strpos( $date_format, strpbrk( $date_format, 'FMmn' ) ),
305
+ 'y' => strpos( $date_format, strpbrk( $date_format, 'Yy' ) ),
306
+ );
307
+
308
+ // @TODO When one date piece is not found, perhaps fallback to ISO standard position.
309
+
310
+ $order = array();
311
+ foreach ( $pos as $k => $v ) {
312
+ $order[ $k ] = $v;
313
+ }
314
+ ksort( $order );
315
+
316
+ return $order;
317
+ }
318
+
319
+ /**
320
+ * Get WordPress timezone setting.
321
+ *
322
+ * Always returns a valid timezone string even when the setting is a GMT offset.
323
+ *
324
+ * @since 3.0.0
325
+ *
326
+ * @return null|string
327
+ */
328
+ function simcal_get_wp_timezone() {
329
+
330
+ $timezone = get_option( 'timezone_string' );
331
+
332
+ if ( empty( $timezone ) ) {
333
+ $gmt = get_option( 'gmt_offset' );
334
+ $timezone = simcal_get_timezone_from_gmt_offset( $gmt );
335
+ }
336
+
337
+ return $timezone;
338
+ }
339
+
340
+ /**
341
+ * Get a timezone from a GMT offset.
342
+ *
343
+ * Converts a numeric offset into a valid timezone string.
344
+ *
345
+ * @since 3.0.0
346
+ *
347
+ * @param string|float $offset
348
+ *
349
+ * @return null|string
350
+ */
351
+ function simcal_get_timezone_from_gmt_offset( $offset ) {
352
+
353
+ if ( is_numeric( $offset ) ) {
354
+
355
+ if ( 0 === intval( $offset ) ) {
356
+ return 'UTC';
357
+ } else {
358
+ $offset = floatval( $offset ) * 3600;
359
+ }
360
+
361
+ $timezone = timezone_name_from_abbr( null, $offset, true );
362
+ // This is buggy and might return false:
363
+ // @see http://php.net/manual/en/function.timezone-name-from-abbr.php#86928
364
+ // Therefore:
365
+ if ( false == $timezone ) {
366
+
367
+ $list = timezone_abbreviations_list();
368
+ foreach ( $list as $abbr ) {
369
+ foreach ( $abbr as $city ) {
370
+ if ( $offset == $city['offset'] ) {
371
+ return $city['timezone_id'];
372
+ }
373
+ }
374
+ }
375
+
376
+ }
377
+
378
+ return $timezone;
379
+ }
380
+
381
+ return null;
382
+ }
383
+
384
+ /**
385
+ * Convert a timezone string to a numeric offset.
386
+ *
387
+ * @since 3.0.0
388
+ *
389
+ * @param string $timezone
390
+ *
391
+ * @return int Unix time offset
392
+ */
393
+ function simcal_get_timezone_offset( $timezone ) {
394
+ return \Carbon\Carbon::now( $timezone )->offset;
395
+ }
396
+
397
+ /**
398
+ * Escape timezone string.
399
+ *
400
+ * @since 3.0.0
401
+ *
402
+ * @param string $tz
403
+ * @param mixed $default
404
+ *
405
+ * @return mixed|string
406
+ */
407
+ function simcal_esc_timezone( $tz, $default = 'UTC' ) {
408
+ return in_array( $tz, timezone_identifiers_list() ) ? $tz : $default;
409
+ }
includes/gce-feed-cpt.php DELETED
@@ -1,473 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Custom Post Type functions
5
- *
6
- * @package GCE
7
- * @author Phil Derksen <pderksen@gmail.com>, Nick Young <mycorpweb@gmail.com>
8
- * @license GPL-2.0+
9
- * @copyright 2014 Phil Derksen
10
- */
11
-
12
-
13
- /**
14
- * Register Google Calendar Events custom post type
15
- *
16
- * @since 2.0.0
17
- */
18
- function gce_setup_cpt() {
19
-
20
- $labels = array(
21
- 'name' => __( 'Google Calendar Feeds', 'gce' ),
22
- 'singular_name' => __( 'Feed', 'gce' ),
23
- 'menu_name' => __( 'GCal Events', 'gce' ),
24
- 'name_admin_bar' => __( 'Feed', 'gce' ),
25
- 'add_new' => __( 'Add New', 'gce' ),
26
- 'add_new_item' => __( 'Add New Feed', 'gce' ),
27
- 'new_item' => __( 'New Feed', 'gce' ),
28
- 'edit_item' => __( 'Edit Feed', 'gce' ),
29
- 'view_item' => __( 'View Feed', 'gce' ),
30
- 'all_items' => __( 'All GCal Feeds', 'gce' ),
31
- 'search_items' => __( 'Search GCal Feeds', 'gce' ),
32
- 'not_found' => __( 'No feeds found.', 'gce' ),
33
- 'not_found_in_trash' => __( 'No feeds found in Trash.', 'gce' )
34
- );
35
-
36
- $args = array(
37
- 'labels' => $labels,
38
- 'public' => true, // This removes the 'view' and 'preview' links from what I can tell
39
- 'publicly_queryable' => true,
40
- 'show_ui' => true,
41
- 'show_in_menu' => true,
42
- 'query_var' => true,
43
- 'capability_type' => 'post',
44
- 'has_archive' => false,
45
- 'hierarchical' => false,
46
- 'menu_icon' => plugins_url( '/assets/gcal-icon-16x16.png', GCE_MAIN_FILE ),
47
- 'supports' => array( 'title', 'editor' )
48
- );
49
-
50
- register_post_type( 'gce_feed', $args );
51
-
52
- // MUST ONLY RUN ONCE!
53
- if( false === get_option( 'gce_cpt_setup' ) ) {
54
- flush_rewrite_rules();
55
- update_option( 'gce_cpt_setup', 1 );
56
- }
57
-
58
- }
59
- add_action( 'init', 'gce_setup_cpt' );
60
-
61
- /**
62
- * Messages for Feed actions
63
- *
64
- * @since 2.0.0
65
- */
66
- function gce_feed_messages( $messages ) {
67
- global $post, $post_ID;
68
-
69
- $url1 = '<a href="' . esc_url( get_permalink( $post_ID ) ) . '">';
70
- $url2 = __( 'feed', 'gce' );
71
- $url3 = '</a>';
72
- $s1 = __( 'Feed', 'gce' );
73
-
74
- $messages['gce_feed'] = array(
75
- 1 => sprintf( __( '%4$s updated. %1$sView %2$s%3$s', 'gce' ), $url1, $url2, $url3, $s1 ),
76
- 4 => sprintf( __( '%4$s updated. %1$sView %2$s%3$s', 'gce' ), $url1, $url2, $url3, $s1 ),
77
- 6 => sprintf( __( '%4$s published. %1$sView %2$s%3$s', 'gce' ), $url1, $url2, $url3, $s1 ),
78
- 7 => sprintf( __( '%4$s saved. %1$sView %2$s%3$s', 'gce' ), $url1, $url2, $url3, $s1 ),
79
- 8 => sprintf( __( '%4$s submitted. %1$sView %2$s%3$s', 'gce' ), $url1, $url2, $url3, $s1 ),
80
- 10 => sprintf( __( '%4$s draft updated. %1$sView %2$s%3$s', 'gce' ), $url1, $url2, $url3, $s1 )
81
- );
82
-
83
- return $messages;
84
- }
85
- add_filter( 'post_updated_messages', 'gce_feed_messages' );
86
-
87
-
88
- /**
89
- * Add post meta to tie in with the Google Calendar Events custom post type.
90
- * Also render sidebar meta boxes.
91
- *
92
- * @since 2.0.0
93
- */
94
- function gce_cpt_meta() {
95
- add_meta_box( 'gce_feed_meta', __( 'Feed Settings', 'gce' ), 'gce_display_meta', 'gce_feed', 'advanced', 'core' );
96
-
97
- add_meta_box( 'gce_feed_sidebar_help', __( 'Resources', 'gce' ), 'gce_feed_sidebar_help', 'gce_feed', 'side', 'core' );
98
- add_meta_box( 'gce_display_options_meta', __( 'Display Options', 'gce' ), 'gce_display_options_meta', 'gce_feed', 'side', 'core' );
99
- }
100
- add_action( 'add_meta_boxes', 'gce_cpt_meta' );
101
-
102
- /**
103
- * Include view to display post meta.
104
- *
105
- * @since 2.0.0
106
- */
107
- function gce_display_meta() {
108
- include_once( GCE_DIR . '/views/admin/gce-feed-meta-display.php' );
109
- }
110
-
111
- /**
112
- * Include view to display help in sidebar.
113
- *
114
- * @since 2.0.0
115
- */
116
- function gce_feed_sidebar_help() {
117
- include_once( GCE_DIR . '/views/admin/gce-feed-sidebar-help.php' );
118
- }
119
-
120
- /**
121
- * Include view to display options in sidebar.
122
- *
123
- * @since 2.0.0
124
- */
125
- function gce_display_options_meta() {
126
- include_once( GCE_DIR . '/views/admin/display-options-meta.php' );
127
- }
128
-
129
- /**
130
- * Function to save post meta for the feed CPT
131
- *
132
- * @since 2.0.0
133
- *
134
- * @param int $post_id
135
- * @param WP_Post $post
136
- *
137
- * @return int
138
- */
139
- function gce_save_meta( $post_id, $post ) {
140
-
141
- if ( 'gce_feed' != $post->post_type ) {
142
- return $post_id;
143
- }
144
-
145
- if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
146
- return $post_id;
147
- }
148
-
149
- if( isset( $_REQUEST['bulk_edit'] ) ) {
150
- return $post_id;
151
- }
152
-
153
- // An array to hold all of our post meta ids so we can run them through a loop
154
- $post_meta_fields = array(
155
- 'gce_feed_url',
156
- 'gce_date_format',
157
- 'gce_time_format',
158
- 'gce_cache',
159
- 'gce_multi_day_events',
160
- 'gce_display_mode',
161
- 'gce_custom_from',
162
- 'gce_custom_until',
163
- 'gce_search_query',
164
- 'gce_expand_recurring',
165
- 'gce_show_tooltips',
166
- 'gce_paging',
167
- 'gce_events_per_page',
168
- 'gce_per_page_num',
169
- 'gce_per_page_from',
170
- 'gce_per_page_to',
171
- 'gce_list_start_offset_num',
172
- 'gce_list_start_offset_direction',
173
- 'gce_feed_start',
174
- 'gce_feed_start_num',
175
- 'gce_feed_start_custom',
176
- 'gce_feed_end',
177
- 'gce_feed_end_num',
178
- 'gce_feed_end_custom',
179
- 'gce_feed_use_range',
180
- 'gce_feed_range_start',
181
- 'gce_feed_range_end',
182
- '_feed_timezone_setting',
183
- // Display options
184
- 'gce_display_start',
185
- 'gce_display_start_text',
186
- 'gce_display_end',
187
- 'gce_display_end_text',
188
- 'gce_display_separator',
189
- 'gce_display_location',
190
- 'gce_display_location_text',
191
- 'gce_display_description',
192
- 'gce_display_description_text',
193
- 'gce_display_description_max',
194
- 'gce_display_link',
195
- 'gce_display_link_tab',
196
- 'gce_display_link_text',
197
- 'gce_display_simple'
198
- );
199
-
200
- $post_meta_fields = apply_filters( 'gce_feed_meta', $post_meta_fields );
201
-
202
- if ( current_user_can( 'edit_post', $post_id ) ) {
203
- // Loop through our array and make sure it is posted and not empty in order to update it, otherwise we delete it
204
- foreach ( $post_meta_fields as $pmf ) {
205
- if ( isset( $_POST[$pmf] ) && ( ! empty( $_POST[$pmf] ) || $_POST[$pmf] == 0 ) ) {
206
- if( $pmf == 'gce_feed_url' ) {
207
-
208
- $id = $_POST[$pmf];
209
-
210
- // convert from URL if user enters a URL link (like the old versions required)
211
- if ( strpos( $id, 'https://www.google.com/calendar/feeds/' ) !== false ) {
212
- $id = str_replace( 'https://www.google.com/calendar/feeds/', '', $id );
213
- $id = str_replace( '/public/basic', '', $id );
214
- $id = str_replace( '%40', '@', $id );
215
- }
216
-
217
- // decode first before re-encoding it
218
- $id = urldecode( $id );
219
- $id = trim( $id );
220
-
221
- $at = strpos( $id, '@' );
222
-
223
- if ( $at !== false ) {
224
- $id = substr_replace( $id, urlencode( substr( $id, 0, $at ) ), 0, $at );
225
- }
226
-
227
- update_post_meta( $post_id, $pmf, $id );
228
- } elseif( $pmf == 'gce_time_format' || $pmf == 'gce_date_format' || '_feed_timezone_setting' ) {
229
- update_post_meta( $post_id, $pmf, sanitize_text_field( $_POST[ $pmf ] ) );
230
- } else {
231
- update_post_meta( $post_id, $pmf, stripslashes( $_POST[ $pmf ] ) );
232
- }
233
- } else {
234
- delete_post_meta( $post_id, $pmf );
235
- }
236
- }
237
- }
238
-
239
- return $post_id;
240
- }
241
- add_action( 'save_post', 'gce_save_meta', 10, 2 );
242
-
243
-
244
- /**
245
- * Delete feed ids transient if a feed post type is deleted.
246
- *
247
- * @param int $id The id of the deleted post.
248
- */
249
- function gce_delete_post( $id ) {
250
- $feeds = get_transient( 'gce_feed_ids' );
251
- if ( $feeds && is_array( $feeds ) ) {
252
- if ( in_array( $id, array_keys( $feeds ) ) ) {
253
- delete_transient( 'gce_feed_ids' );
254
- }
255
- }
256
- }
257
- add_action( 'delete_post', 'gce_delete_post', 10, 1 );
258
-
259
-
260
- /**
261
- * Add column headers to our "All Feeds" CPT page
262
- *
263
- * @since 2.0.0
264
- */
265
- function gce_add_column_headers( $defaults ) {
266
-
267
- $new_columns = array(
268
- 'cb' => $defaults['cb'],
269
- 'feed-id' => __( 'Feed ID', 'gce' ),
270
- 'feed-sc' => __( 'Feed Shortcode', 'gce' ),
271
- 'display-type' => __( 'Display Type', 'gce' )
272
- );
273
-
274
- return array_merge( $defaults, $new_columns );
275
- }
276
- add_filter( 'manage_gce_feed_posts_columns', 'gce_add_column_headers' );
277
-
278
-
279
- /**
280
- * Fill out the columns
281
- *
282
- * @since 2.0.0
283
- */
284
- function gce_column_content( $column_name, $post_ID ) {
285
-
286
- switch ( $column_name ) {
287
-
288
- case 'feed-id':
289
- echo $post_ID;
290
- break;
291
- case 'feed-sc':
292
- ?>
293
- <input
294
- name="gce_shortcode"
295
- class="gce-shortcode"
296
- readonly="readonly"
297
- value='[gcal id="<?php echo $post_ID; ?>"]'
298
- onclick="this.select();"
299
- />
300
- <?php
301
- break;
302
- case 'display-type':
303
- $display = get_post_meta( $post_ID, 'gce_display_mode', true );
304
-
305
- if ( $display == 'grid' ) {
306
- echo __( 'Grid', 'gce' );
307
- } elseif ( $display == 'list' ) {
308
- echo __( 'List', 'gce' );
309
- } elseif ( $display == 'list-grouped' ) {
310
- echo __( 'Grouped List', 'gce' );
311
- } elseif ( $display == 'date-range' ) {
312
- echo __( 'Custom Date Range', 'gce' );
313
- }
314
-
315
- break;
316
- }
317
- }
318
- add_action( 'manage_gce_feed_posts_custom_column', 'gce_column_content', 10, 2 );
319
-
320
-
321
- /**
322
- * Add the "Clear Cache" action to the CPT action links
323
- *
324
- * @since 2.0.0
325
- */
326
- function gce_cpt_actions( $actions, $post ) {
327
- if( $post->post_type == 'gce_feed' ) {
328
- $actions['clear_cache'] = '<a href="' . esc_url( add_query_arg( array( 'clear_cache' => $post->ID ) ) ) . '">' . __( 'Clear Cache', 'gce' ) . '</a>';
329
- }
330
- return $actions;
331
- }
332
- add_filter( 'post_row_actions', 'gce_cpt_actions', 10, 2 );
333
-
334
- /**
335
- * Function to clear cache if on the post listing page.
336
- *
337
- * @since 2.0.0
338
- */
339
- function gce_clear_cache_link() {
340
- if( isset( $_REQUEST['clear_cache'] ) ) {
341
- $post_id = absint( $_REQUEST['clear_cache'] );
342
-
343
- gce_clear_cache( $post_id );
344
-
345
- settings_errors( 'gce-notices' );
346
- }
347
- }
348
- add_action( 'admin_init', 'gce_clear_cache_link' );
349
-
350
- /**
351
- * Clear cache on post save.
352
- *
353
- * @param int $post_id
354
- */
355
- function gce_clear_cache_on_save( $post_id ) {
356
- // Transient with calendar feed data.
357
- delete_transient( 'gce_feed_' . $post_id );
358
- // Transient with an associative array list of feed ids and their titles.
359
- delete_transient( 'gce_feed_ids' );
360
- }
361
- add_action( 'save_post_gce_feed', 'gce_clear_cache_on_save' );
362
-
363
- /**
364
- * Adds a 'clear cache' option to bulk actions.
365
- *
366
- * It's done through jQuery since one can't write into bulk actions yet.
367
- * @link https://core.trac.wordpress.org/ticket/16031
368
- * @link https://www.skyverge.com/blog/add-custom-bulk-action/
369
- */
370
- function gce_clear_cache_bulk_action_option() {
371
-
372
- global $post_type;
373
-
374
- if ( $post_type == 'gce_feed' ) {
375
-
376
- ?>
377
- <script type="text/javascript">
378
- jQuery(document).ready(function () {
379
- jQuery('<option>')
380
- .val('clear_cache')
381
- .text('<?php _e( 'Clear Cache', 'gce' ); ?>')
382
- .appendTo("select[name='action']");
383
- });
384
- </script>
385
- <?php
386
-
387
- }
388
-
389
- }
390
- add_action( 'admin_footer-edit.php', 'gce_clear_cache_bulk_action_option' );
391
-
392
- /**
393
- * Clear cache bulk action.
394
- *
395
- * @see gce_clear_cache_bulk_action_option()
396
- */
397
- function gce_clear_cache_bulk_action() {
398
-
399
- global $typenow;
400
- $post_type = $typenow;
401
-
402
- if ( 'gce_feed' == $post_type ) {
403
-
404
- $send_back = remove_query_arg( array( 'cleared' ), wp_get_referer() );
405
- if ( ! $send_back ) {
406
- $send_back = admin_url( 'edit.php?post_type=' . $post_type );
407
- }
408
-
409
- // Get the bulk action.
410
- $wp_list_table = _get_list_table( 'WP_Posts_List_Table' );
411
- $action = $wp_list_table->current_action();
412
- if ( $action == 'clear_cache' ) {
413
-
414
- // Security check (the referer is right).
415
- check_admin_referer( 'bulk-posts' );
416
-
417
- // This is based on wp-admin/edit.php.
418
- $send_back = remove_query_arg(
419
- array( 'cleared', 'untrashed', 'deleted', 'ids' ),
420
- $send_back
421
- );
422
-
423
- // Proceed if there are post ids selected.
424
- $post_ids = isset( $_REQUEST['post'] ) ? array_map( 'intval', $_REQUEST['post'] ) : '';
425
- if ( $post_ids ) {
426
-
427
- // Add page num to query arg.
428
- $page_num = $wp_list_table->get_pagenum();
429
- $send_back = add_query_arg( 'paged', $page_num, $send_back );
430
-
431
- switch ( $action ) {
432
- case 'clear_cache' :
433
- $cleared = 0;
434
- foreach ( $post_ids as $post_id ) {
435
- gce_clear_cache( $post_id );
436
- $cleared ++;
437
- }
438
- $send_back = add_query_arg( array(
439
- 'cleared' => $cleared,
440
- 'ids' => join( ',', $post_ids )
441
- ),
442
- $send_back
443
- );
444
- break;
445
- default:
446
- return;
447
- break;
448
- }
449
-
450
- $send_back = remove_query_arg(
451
- array( 'action', 'tags_input', 'post_author', 'comment_status', 'ping_status', '_status', 'post', 'bulk_edit', 'post_view' ),
452
- $send_back
453
- );
454
-
455
- wp_redirect( $send_back );
456
- exit();
457
- }
458
- }
459
- }
460
- }
461
- add_action( 'load-edit.php', 'gce_clear_cache_bulk_action' );
462
-
463
- /**
464
- * Display an admin notice when the cache is cleared.
465
- */
466
- function gce_clear_cache_bulk_action_notice() {
467
- global $post_type, $pagenow;
468
- if ( $pagenow == 'edit.php' && $post_type == 'gce_feed' && isset( $_REQUEST['cleared'] ) && (int) $_REQUEST['cleared'] ) {
469
- $message = sprintf( _n( 'Feed cache cleared.', 'Cleared cache for %s feeds.', $_REQUEST['cleared'], 'gce' ), number_format_i18n( $_REQUEST['cleared'] ) );
470
- echo '<div class="updated notice is-dismissible"><p>' . $message . '</p></div>';
471
- }
472
- }
473
- add_action( 'admin_notices', 'gce_clear_cache_bulk_action_notice' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/installation.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Installation
4
+ *
5
+ * @package SimpleCalendar
6
+ */
7
+ namespace SimpleCalendar;
8
+
9
+ use SimpleCalendar\Admin\Pages;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Installation.
17
+ *
18
+ * Static class that deals with plugin activation and deactivation events.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Installation {
23
+
24
+ /**
25
+ * What happens when the plugin is activated.
26
+ *
27
+ * @since 3.0.0
28
+ */
29
+ public static function activate() {
30
+
31
+ include_once 'functions/shared.php';
32
+ include_once 'functions/admin.php';
33
+
34
+ self::create_terms();
35
+ self::create_options();
36
+
37
+ self::update( SIMPLE_CALENDAR_VERSION );
38
+
39
+ do_action( 'simcal_activated' );
40
+ }
41
+
42
+ /**
43
+ * What happens when the plugin is deactivated.
44
+ *
45
+ * @since 3.0.0
46
+ */
47
+ public static function deactivate() {
48
+
49
+ flush_rewrite_rules();
50
+
51
+ do_action( 'simcal_deactivated' );
52
+ }
53
+
54
+ /**
55
+ * Create default terms.
56
+ *
57
+ * @since 3.0.0
58
+ */
59
+ public static function create_terms() {
60
+
61
+ $taxonomies = array(
62
+ 'calendar_feed' => array(
63
+ 'google',
64
+ 'grouped-calendar',
65
+ ),
66
+ 'calendar_type' => array(
67
+ 'default-calendar'
68
+ )
69
+ );
70
+
71
+ foreach ( $taxonomies as $taxonomy => $terms ) {
72
+ foreach ( $terms as $term ) {
73
+ if ( ! get_term_by( 'slug', sanitize_title( $term ), $taxonomy ) ) {
74
+ wp_insert_term( $term, $taxonomy );
75
+ }
76
+ }
77
+ }
78
+
79
+ }
80
+
81
+ /**
82
+ * Sets the default options.
83
+ *
84
+ * @since 3.0.0
85
+ */
86
+ public static function create_options() {
87
+
88
+ $default = '';
89
+ $page = 'settings';
90
+ $settings_pages = new Pages( $page );
91
+ $plugin_settings = $settings_pages->get_settings();
92
+
93
+ if ( $plugin_settings && is_array( $plugin_settings ) ) {
94
+
95
+ foreach ( $plugin_settings as $id => $settings ) {
96
+
97
+ $group = 'simple-calendar_' . $page . '_' . $id;
98
+
99
+ if ( isset( $settings['sections'] ) ) {
100
+
101
+ if ( $settings['sections'] && is_array( $settings['sections'] ) ) {
102
+
103
+ foreach ( $settings['sections'] as $section_id => $section ) {
104
+
105
+ if ( isset( $section['fields'] ) ) {
106
+
107
+ if ( $section['fields'] && is_array( $section['fields'] ) ) {
108
+
109
+ foreach ( $section['fields'] as $key => $field ) {
110
+
111
+ if ( isset ( $field['type'] ) ) {
112
+ // Maybe an associative array.
113
+ if ( is_int( $key ) ) {
114
+ $default[ $section_id ] = self::get_field_default_value( $field );
115
+ } else {
116
+ $default[ $section_id ][ $key ] = self::get_field_default_value( $field );
117
+ }
118
+ }
119
+
120
+ } // Loop fields.
121
+
122
+ } // Are fields non empty?
123
+
124
+ } // Are there fields?
125
+
126
+ } // Loop fields sections.
127
+
128
+ } // Are sections non empty?
129
+
130
+ } // Are there sections?
131
+
132
+ add_option( $group, $default, '', true );
133
+
134
+ // Reset before looping next settings page.
135
+ $default = '';
136
+ }
137
+
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Get field default value.
143
+ *
144
+ * Helper function to set the default value of a field.
145
+ *
146
+ * @since 3.0.0
147
+ *
148
+ * @param $field
149
+ *
150
+ * @return mixed
151
+ */
152
+ private static function get_field_default_value( $field ) {
153
+
154
+ $saved_value = isset( $field['value'] ) ? $field['value'] : '';
155
+ $default_value = isset( $field['default'] ) ? $field['default'] : '';
156
+
157
+ return ! empty( $saved_value ) ? $saved_value : $default_value;
158
+ }
159
+
160
+ /**
161
+ * Run upgrade scripts.
162
+ *
163
+ * @since 3.0.0
164
+ *
165
+ * @param string $version
166
+ */
167
+ public static function update( $version ) {
168
+ $update = new Update( $version );
169
+ $update->run_updates();
170
+ }
171
+
172
+ }
includes/main.php ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Main Class
4
+ *
5
+ * @package SimpleCalendar
6
+ */
7
+ namespace SimpleCalendar;
8
+
9
+ use SimpleCalendar\Admin\License_Manager;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Simple Calendar plugin.
17
+ */
18
+ final class Plugin {
19
+
20
+ /**
21
+ * Plugin name.
22
+ *
23
+ * @access public
24
+ * @var string
25
+ */
26
+ public static $name = 'Simple Calendar';
27
+
28
+ /**
29
+ * Plugin version.
30
+ *
31
+ * @access public
32
+ * @var string
33
+ */
34
+ public static $version = SIMPLE_CALENDAR_VERSION;
35
+
36
+ /**
37
+ * Plugin homepage.
38
+ *
39
+ * @access public
40
+ * @var string
41
+ */
42
+ protected static $homepage = 'https://simplecalendar.io';
43
+
44
+ /**
45
+ * Locale.
46
+ *
47
+ * @access public
48
+ * @var string
49
+ */
50
+ public $locale = 'en_US';
51
+
52
+ /**
53
+ * Objects factory.
54
+ *
55
+ * @access public
56
+ * @var Objects
57
+ */
58
+ public $objects = null;
59
+
60
+ /**
61
+ * The single instance of this class.
62
+ *
63
+ * @access protected
64
+ * @var Plugin
65
+ */
66
+ protected static $_instance = null;
67
+
68
+ /**
69
+ * Get the plugin instance.
70
+ *
71
+ * @return Plugin
72
+ */
73
+ public static function get_instance() {
74
+ if ( is_null( self::$_instance ) ) {
75
+ self::$_instance = new self();
76
+ }
77
+ return self::$_instance;
78
+ }
79
+
80
+ /**
81
+ * Cloning is forbidden.
82
+ */
83
+ public function __clone() {
84
+ _doing_it_wrong( __FUNCTION__, 'Cloning the main instance of this plugin is forbidden.', '1.0.0' );
85
+ }
86
+
87
+ /**
88
+ * Unserializing instances of this class is forbidden.
89
+ */
90
+ public function __wakeup() {
91
+ _doing_it_wrong( __FUNCTION__, 'Unserializing instances of this plugin is forbidden.', '1.0.0' );
92
+ }
93
+
94
+ /**
95
+ * Plugin constructor.
96
+ *
97
+ * @final
98
+ */
99
+ public function __construct() {
100
+
101
+ // Load plugin.
102
+ require_once 'autoload.php';
103
+ $this->locale = apply_filters( 'plugin_locale', get_locale(), 'google-calendar-events' );
104
+ $this->load();
105
+
106
+ // Installation hooks.
107
+ register_activation_hook( SIMPLE_CALENDAR_MAIN_FILE, array( 'SimpleCalendar\Installation', 'activate' ) );
108
+ register_deactivation_hook( SIMPLE_CALENDAR_MAIN_FILE, array( 'SimpleCalendar\Installation', 'deactivate' ) );
109
+
110
+ // Init hooks.
111
+ add_action( 'init', array( $this, 'init' ), 5 );
112
+ add_action( 'admin_init', array( $this, 'register_settings' ), 5 );
113
+
114
+ // Upon plugin loaded action hook.
115
+ do_action( 'simcal_loaded' );
116
+ }
117
+
118
+ /**
119
+ * Load plugin.
120
+ *
121
+ * @since 3.0.0
122
+ */
123
+ public function load() {
124
+
125
+ // Functions shared in both back end and front end.
126
+ include_once 'functions/shared.php';
127
+
128
+ // Init custom post types and taxonomies.
129
+ new Post_Types();
130
+
131
+ // Load back end.
132
+ if ( is_admin() ) {
133
+ $this->load_admin();
134
+ } else {
135
+ // Load front end scripts and styles.
136
+ new Assets();
137
+ }
138
+
139
+ // Front facing ajax callbacks.
140
+ new Ajax();
141
+
142
+ // Add Shortcodes.
143
+ new Shortcodes();
144
+
145
+ // Add Widgets.
146
+ new Widgets();
147
+
148
+ // Deprecated functions for backwards compatibility.
149
+ include_once 'functions/deprecated.php';
150
+ }
151
+
152
+ /**
153
+ * Load plugin admin.
154
+ *
155
+ * @since 3.0.0
156
+ */
157
+ public function load_admin() {
158
+
159
+ // Back end only functions.
160
+ include_once 'functions/admin.php';
161
+
162
+ // Display admin notices.
163
+ new Admin\Notices();
164
+
165
+ // Load back end scripts and styles.
166
+ new Admin\Assets();
167
+
168
+ // Custom content handling.
169
+ new Admin\Post_Types();
170
+
171
+ // Init menus and settings.
172
+ new Admin\Menus();
173
+
174
+ if ( defined( 'DOING_AJAX' ) ) {
175
+ // Admin ajax callbacks.
176
+ new Admin\Ajax();
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Init plugin when WordPress initializes.
182
+ *
183
+ * @since 3.0.0
184
+ */
185
+ public function init() {
186
+
187
+ // Before init action hook.
188
+ do_action( 'before_simcal_init' );
189
+
190
+ // Set up localization.
191
+ load_plugin_textdomain( 'google-calendar-events', false, dirname( plugin_basename( SIMPLE_CALENDAR_MAIN_FILE ) ) . '/languages/' );
192
+
193
+ // Init objects factory.
194
+ $this->objects = new Objects();
195
+
196
+ // Upon init action hook.
197
+ do_action( 'simcal_init' );
198
+ }
199
+
200
+ /**
201
+ * Register plugin settings.
202
+ *
203
+ * @since 3.0.0
204
+ */
205
+ public function register_settings() {
206
+ if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
207
+ $settings = new Admin\Pages();
208
+ $settings->register_settings( $settings->get_settings() );
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Get Ajax URL.
214
+ *
215
+ * @since 3.0.0
216
+ *
217
+ * @return string
218
+ */
219
+ public function ajax_url() {
220
+ return admin_url( 'admin-ajax.php', 'relative' );
221
+ }
222
+
223
+ /**
224
+ * Get URL.
225
+ *
226
+ * @since 3.0.0
227
+ *
228
+ * @param string $case Requested url.
229
+ *
230
+ * @return string
231
+ */
232
+ public function get_url( $case ) {
233
+ switch ( $case ) {
234
+ case 'codex' :
235
+ case 'apidocs' :
236
+ return 'http://codex.simplecalendar.io';
237
+ case 'add-ons' :
238
+ return self::$homepage . '/addons/';
239
+ case 'gcal-pro' :
240
+ return self::$homepage . '/addons/google-calendar-pro/';
241
+ case 'docs' :
242
+ return 'http://docs.simplecalendar.io';
243
+ case 'github' :
244
+ return 'https://github.com/moonstonemedia/Simple-Calendar';
245
+ case 'support' :
246
+ return 'https://wordpress.org/support/plugin/google-calendar-events';
247
+ case 'gdev-console':
248
+ return 'https://console.developers.google.com';
249
+ default :
250
+ return self::$homepage;
251
+ }
252
+ }
253
+
254
+ }
255
+
256
+ /**
257
+ * Simple Calendar.
258
+ *
259
+ * @return Plugin
260
+ */
261
+ function plugin() {
262
+ return Plugin::get_instance();
263
+ }
264
+
265
+ plugin();
includes/misc-functions.php DELETED
@@ -1,377 +0,0 @@
1
- <?php
2
-
3
- /*
4
- * Function to display the calendar to the screen
5
- *
6
- * @since 2.0.0
7
- */
8
- function gce_print_calendar( $feed_ids, $display = 'grid', $args = array(), $widget = false, $uid = null ) {
9
-
10
- // Set static unique ID for setting id attributes
11
- if( $uid == null ) {
12
- STATIC $uid = 1;
13
- }
14
-
15
- $defaults = array(
16
- 'title_text' => '',
17
- 'sort' => 'asc',
18
- 'grouped' => 0,
19
- 'month' => null,
20
- 'year' => null,
21
- 'widget' => 0,
22
- 'paging_interval' => null,
23
- 'max_events' => null,
24
- 'start_offset' => null,
25
- 'paging_type' => null,
26
- 'paging' => null,
27
- 'max_num' => null,
28
- 'range_start' => null,
29
- 'show_tooltips' => null
30
- );
31
-
32
- $args = array_merge( $defaults, $args );
33
-
34
- extract( $args );
35
-
36
- $ids = explode( '-', str_replace( ' ', '', $feed_ids ) );
37
-
38
- //Create new display object, passing array of feed id(s)
39
- $d = new GCE_Display( $ids, $title_text, $sort );
40
- $markup = '';
41
- $start = current_time( 'timestamp' );
42
-
43
- if( $widget ) {
44
- foreach( $ids as $f ) {
45
- $paging = get_post_meta( $f, 'gce_paging_widget', true );
46
- $old_paging[] = get_post_meta( $f, 'gce_paging', true );
47
-
48
- if( $paging ) {
49
- update_post_meta( $f, 'gce_paging', true );
50
- }
51
-
52
- $paging_interval = get_post_meta( $f, 'gce_widget_paging_interval', true );
53
- }
54
- }
55
-
56
- // If paging is not set then we need to set it now
57
- foreach( $ids as $id ) {
58
- if( $paging === null ) {
59
- $paging = get_post_meta( $id, 'gce_paging', true );
60
- }
61
-
62
- if( empty( $show_tooltips ) && $show_tooltips != 0 ) {
63
- $tooltips = get_post_meta( $id, 'gce_show_tooltips', true );
64
- } else {
65
- $tooltips = $show_tooltips;
66
- }
67
-
68
- if( ! empty( $tooltips ) && ( $tooltips === true || $tooltips == 'true' || $tooltips == '1' || $tooltips == 1 ) ) {
69
- $show_tooltips = 'true';
70
- } else {
71
- $show_tooltips = 'false';
72
- }
73
-
74
- if ( 'date-range-grid' === $display ) {
75
- $start = get_post_meta( $id, 'gce_feed_range_start', true );
76
-
77
- $start = gce_date_unix( $start );
78
-
79
- $year = date( 'Y', $start );
80
- $month = date( 'n', $start );
81
- }
82
- }
83
-
84
- if( 'grid' == $display ) {
85
-
86
- global $localize;
87
-
88
- $target = 'gce-' . $uid;
89
-
90
- $localize[$target] = array(
91
- 'target_element' => $target,
92
- 'feed_ids' => $feed_ids,
93
- 'title_text' => $title_text,
94
- 'type' => ( $widget == 1 ? 'widget' : 'page' ),
95
- 'show_tooltips' => ( $show_tooltips == 'true' || $show_tooltips == '1' ? 'true' : 'false' )
96
- );
97
-
98
- $data_attr = sprintf( 'data-feed="%s"', htmlspecialchars( json_encode( $localize ), ENT_QUOTES, 'UTF-8' ) );
99
-
100
- if( $widget == 1 ) {
101
- $markup .= '<div class="gce-widget-grid gce-widget-' . esc_attr( $feed_ids ) . '" id="gce-' . $uid . '" ' . $data_attr .'>';
102
- } else {
103
- $markup .= '<div class="gce-page-grid gce-page-grid-' . esc_attr( $feed_ids ) . '" id="gce-' . $uid . '" ' . $data_attr .'>';
104
- }
105
-
106
- $markup .= $d->get_grid( $year, $month, $widget, $paging );
107
- $markup .= '</div>';
108
-
109
-
110
- } else if( 'list' == $display || 'list-grouped' == $display ) {
111
-
112
- if( $widget ) {
113
- $markup = '<div class="gce-widget-list gce-widget-list-' . esc_attr( $feed_ids ) . '" id="gce-' . $uid . '">' . $d->get_list( $grouped, ( $start + $start_offset ), $paging, $paging_interval, $start_offset, $max_events, $paging_type, $max_num ) . '</div>';
114
- } else {
115
- $markup = '<div class="gce-page-list gce-page-list-' . esc_attr( $feed_ids ) . '" id="gce-' . $uid . '">' . $d->get_list( $grouped, ( $start + $start_offset ), $paging, $paging_interval, $start_offset, $max_events, $paging_type ) . '</div>';
116
- }
117
- } else if( 'date-range-list' == $display ) {
118
-
119
- $paging_interval = 'date-range';
120
-
121
- if( $widget ) {
122
- $markup = '<div class="gce-widget-list gce-widget-list-' . esc_attr( $feed_ids ) . '" id="gce-' . $uid . '">' . $d->get_list( $grouped, $range_start, false, $paging_interval, $start_offset, INF, $paging_type, $max_num ) . '</div>';
123
- } else {
124
- $markup = '<div class="gce-page-list gce-page-list-' . esc_attr( $feed_ids ) . '" id="gce-' . $uid . '">' . $d->get_list( $grouped, $range_start, false, $paging_interval, $start_offset, INF, $paging_type, INF ) . '</div>';
125
- }
126
- } elseif ( 'date-range-grid' == $display ) {
127
-
128
- global $localize;
129
-
130
- $target = 'gce-' . $uid;
131
-
132
- $localize[$target] = array(
133
- 'target_element' => $target,
134
- 'feed_ids' => $feed_ids,
135
- 'title_text' => $title_text,
136
- 'type' => ( $widget == 1 ? 'widget' : 'page' ),
137
- 'show_tooltips' => ( $show_tooltips == 'true' || $show_tooltips == '1' ? 'true' : 'false' )
138
- );
139
-
140
- $data_attr = sprintf( 'data-feed="%s"', htmlspecialchars( json_encode( $localize ), ENT_QUOTES, 'UTF-8' ) );
141
-
142
- if( $widget ) {
143
- $markup = '<div class="gce-widget-grid gce-widget-grid-' . esc_attr( $feed_ids ) . '" id="gce-' . $uid . '"' . $data_attr . '>' . $d->get_grid( $year, $month, $widget, $paging ) . '</div>';
144
- } else {
145
- $markup = '<div class="gce-page-grid gce-page-grid-' . esc_attr( $feed_ids ) . '" id="gce-' . $uid . '"' . $data_attr . '>' . $d->get_grid( $year, $month, $widget, $paging ) . '</div>';
146
- }
147
- }
148
-
149
- // Reset post meta
150
- if( $widget ) {
151
- $i = 0;
152
- foreach( $ids as $f ) {
153
- update_post_meta( $f, 'gce_paging', $old_paging[$i] );
154
-
155
- $i++;
156
- }
157
- }
158
-
159
- $uid++;
160
-
161
- return $markup;
162
- }
163
-
164
- /**
165
- * AJAX function for grid pagination
166
- *
167
- * @since 2.0.0
168
- */
169
- function gce_ajax() {
170
-
171
- $uid = esc_html( $_POST['gce_uid'] );
172
- $ids = esc_html( $_POST['gce_feed_ids'] );
173
- $title = esc_html( $_POST['gce_title_text'] );
174
- $month = esc_html( $_POST['gce_month'] );
175
- $year = esc_html( $_POST['gce_year'] );
176
- $paging = esc_html( $_POST['gce_paging'] );
177
- $type = esc_html( $_POST['gce_type'] );
178
-
179
- // Split ID and pass as UID to function
180
- $uid = explode( '-', $uid );
181
-
182
- $title = ( 'null' == $title ) ? null : $title;
183
-
184
- $args = array(
185
- 'title_text' => $title,
186
- 'month' => $month,
187
- 'year' => $year,
188
- 'paging' => $paging
189
- );
190
-
191
- if ( 'page' == $type ) {
192
- echo gce_print_calendar( $ids, 'grid', $args, 0, $uid[1] );
193
- } elseif ( 'widget' == $type ) {
194
- $args['widget'] = 1;
195
- echo gce_print_calendar( $ids, 'grid', $args, 1, $uid[1] );
196
- }
197
-
198
- die();
199
- }
200
- add_action( 'wp_ajax_nopriv_gce_ajax', 'gce_ajax' );
201
- add_action( 'wp_ajax_gce_ajax', 'gce_ajax' );
202
-
203
-
204
- /**
205
- * AJAX function for grid pagination
206
- *
207
- * @since 2.0.0
208
- */
209
- function gce_ajax_list() {
210
-
211
- $grouped = esc_html( $_POST['gce_grouped'] );
212
- $start = esc_html( $_POST['gce_start'] );
213
- $ids = esc_html( $_POST['gce_feed_ids'] );
214
- $title_text = esc_html( $_POST['gce_title_text'] );
215
- $sort = esc_html( $_POST['gce_sort'] );
216
- $paging = esc_html( $_POST['gce_paging'] );
217
- $paging_interval = esc_html( $_POST['gce_paging_interval'] );
218
- $paging_direction = esc_html( $_POST['gce_paging_direction'] );
219
- $start_offset = esc_html( $_POST['gce_start_offset'] );
220
- $paging_type = esc_html( $_POST['gce_paging_type'] );
221
-
222
- if( $paging_direction == 'back' ) {
223
- if( $paging_type == 'month' ) {
224
-
225
- $this_month = mktime( 0, 0, 0, date( 'm', $start ) - 1, 1, date( 'Y', $start ) );
226
- $prev_month = mktime( 0, 0, 0, date( 'm', $start ) - 2, 1, date( 'Y', $start ) );
227
- $prev_interval_days = date( 't', $prev_month );
228
- $month_days = date( 't', $this_month );
229
-
230
- $int = $month_days + $prev_interval_days;
231
- $int = $int * 86400;
232
-
233
- $start = $start - ( $int );
234
-
235
- $changed_month_days = date( 't', $start );
236
- $paging_interval = $changed_month_days * 86400;
237
- } else {
238
- $start = $start - ( $paging_interval * 2 );
239
- }
240
-
241
- } else {
242
- if( $paging_type == 'month' ) {
243
- $days_in_month = date( 't', $start );
244
- $paging_interval = 86400 * $days_in_month;
245
- }
246
- }
247
-
248
- $d = new GCE_Display( explode( '-', $ids ), $title_text, $sort );
249
-
250
- echo $d->get_list( $grouped, $start, $paging, $paging_interval, $start_offset );
251
-
252
- die();
253
- }
254
- add_action( 'wp_ajax_nopriv_gce_ajax_list', 'gce_ajax_list' );
255
- add_action( 'wp_ajax_gce_ajax_list', 'gce_ajax_list' );
256
-
257
-
258
- function gce_feed_content( $content ) {
259
- global $post;
260
-
261
- if( $post->post_type == 'gce_feed' ) {
262
- $content = '[gcal id="' . esc_attr( $post->ID ) . '"]';
263
- }
264
-
265
- return $content;
266
- }
267
- add_filter( 'the_content', 'gce_feed_content' );
268
-
269
- /**
270
- * Google Analytics campaign URL.
271
- *
272
- * @since 2.0.0
273
- *
274
- * @param string $base_url Plain URL to navigate to
275
- * @param string $source GA "source" tracking value
276
- * @param string $medium GA "medium" tracking value
277
- * @param string $campaign GA "campaign" tracking value
278
- * @return string $url Full Google Analytics campaign URL
279
- */
280
- function gce_ga_campaign_url( $base_url, $source, $medium, $campaign ) {
281
- // $medium examples: 'sidebar_link', 'banner_image'
282
-
283
- $url = esc_url( add_query_arg( array(
284
- 'utm_source' => $source,
285
- 'utm_medium' => $medium,
286
- 'utm_campaign' => $campaign
287
- ), $base_url ) );
288
-
289
- return esc_url( $url );
290
- }
291
-
292
- /**
293
- * Function to convert date format mm/dd/YYYY to unix timestamp
294
- */
295
- function gce_date_unix( $date ) {
296
-
297
- if ( empty( $date ) ) {
298
-
299
- $current_time = current_time( 'timestamp' );
300
-
301
- $timestamp = mktime( 0, 0, 0, date( 'm', $current_time ), date( 'd', $current_time ), date( 'Y', $current_time ) );
302
- } else {
303
-
304
- $date = explode( '/', $date );
305
-
306
- $month = $date[0];
307
- $day = $date[1];
308
- $year = $date[2];
309
-
310
- $timestamp = mktime( 0, 0, 0, $month, $day, $year );
311
- }
312
-
313
- return $timestamp;
314
- }
315
-
316
-
317
- /**
318
- * Get WordPress timezone setting.
319
- *
320
- * Always returns a valid timezone string even when the setting is a GMT offset.
321
- *
322
- * @return null|string
323
- */
324
- function gce_get_wp_timezone() {
325
-
326
- $timezone = get_option( 'timezone_string' );
327
-
328
- if ( empty( $timezone ) ) {
329
-
330
- $gmt = get_option( 'gmt_offset' );
331
-
332
- $timezone = gce_get_timezone_from_gmt_offset( $gmt );
333
- }
334
-
335
- return $timezone;
336
- }
337
-
338
- /**
339
- * Get a timezone from a GMT offset.
340
- *
341
- * Converts a numeric offset into a valid timezone string.
342
- *
343
- * @param string|float $offset
344
- *
345
- * @return null|string
346
- */
347
- function gce_get_timezone_from_gmt_offset( $offset ) {
348
-
349
- if ( is_numeric( $offset ) ) {
350
-
351
- if ( 0 == intval( $offset ) ) {
352
- return 'UTC';
353
- } else {
354
- $offset = floatval( $offset ) * 3600;
355
- }
356
-
357
- $timezone = timezone_name_from_abbr( null, $offset, true );
358
- // This is buggy and might return false:
359
- // @see http://php.net/manual/en/function.timezone-name-from-abbr.php#86928
360
- // Therefore:
361
- if ( false == $timezone ) {
362
- $list = timezone_abbreviations_list();
363
- foreach ( $list as $abbr ) {
364
- foreach ( $abbr as $city ) {
365
- if ( $offset == $city['offset'] ) {
366
- return $city['timezone_id'];
367
- }
368
- }
369
- }
370
-
371
- }
372
-
373
- return $timezone;
374
- }
375
-
376
- return null;
377
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/objects.php ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Objects Factory
4
+ *
5
+ * @package SimpleCalendar
6
+ */
7
+ namespace SimpleCalendar;
8
+
9
+ use SimpleCalendar\Abstracts as Object;
10
+
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Objects factory.
17
+ *
18
+ * Helper class to get the right type of object used across the plugin.
19
+ *
20
+ * @since 3.0.0
21
+ */
22
+ class Objects {
23
+
24
+ /**
25
+ * Constructor.
26
+ *
27
+ * Add default objects.
28
+ *
29
+ * @since 3.0.0
30
+ */
31
+ public function __construct() {
32
+
33
+ // Add default feed type.
34
+ add_filter( 'simcal_get_feed_types', function( $feed_types ) {
35
+ return array_merge( $feed_types, array(
36
+ 'google',
37
+ 'grouped-calendars',
38
+ ) );
39
+ }, 10, 1 );
40
+
41
+ // Add default calendar type.
42
+ add_filter( 'simcal_get_calendar_types', function( $calendar_types ) {
43
+ return array_merge( $calendar_types, array(
44
+ 'default-calendar' => array(
45
+ 'grid',
46
+ 'list',
47
+ ),
48
+ ) );
49
+ }, 10, 1 );
50
+
51
+ // Add default admin objects.
52
+ if ( $is_admin = is_admin() ) {
53
+ add_filter( 'simcal_get_admin_pages', function( $admin_pages ) {
54
+ return array_merge( $admin_pages, array(
55
+ 'add-ons' => array(
56
+ 'add-ons',
57
+ ),
58
+ 'settings' => array(
59
+ 'feeds',
60
+ 'calendars',
61
+ 'advanced',
62
+ ),
63
+ 'tools' => array(
64
+ 'system-status',
65
+ ),
66
+ ) );
67
+ }, 10, 1 );
68
+ }
69
+
70
+ do_action( 'simcal_load_objects', $is_admin );
71
+ }
72
+
73
+ /**
74
+ * Get feed types.
75
+ *
76
+ * @since 3.0.0
77
+ *
78
+ * @return array
79
+ */
80
+ public function get_feed_types() {
81
+ $array = apply_filters( 'simcal_get_feed_types', array() );
82
+ ksort( $array );
83
+ return $array;
84
+ }
85
+
86
+ /**
87
+ * Get calendar types.
88
+ *
89
+ * @since 3.0.0
90
+ *
91
+ * @return array
92
+ */
93
+ public function get_calendar_types() {
94
+ $array = apply_filters( 'simcal_get_calendar_types', array() );
95
+ ksort( $array );
96
+ return $array;
97
+ }
98
+
99
+ /**
100
+ * Get admin pages.
101
+ *
102
+ * @since 3.0.0
103
+ *
104
+ * @return array
105
+ */
106
+ public function get_admin_pages() {
107
+ return apply_filters( 'simcal_get_admin_pages', array() );
108
+ }
109
+
110
+ /**
111
+ * Get a calendar.
112
+ *
113
+ * Returns the right type of calendar.
114
+ *
115
+ * @since 3.0.0
116
+ *
117
+ * @param int|string|object|\WP_Post|Object\Calendar $object
118
+ *
119
+ * @return null|Object\Calendar
120
+ */
121
+ public function get_calendar( $object ) {
122
+
123
+ if ( is_string( $object ) ) {
124
+ return ! empty( $object ) ? $this->get_object( $object, 'calendar', '' ) : null;
125
+ }
126
+
127
+ if ( is_object( $object ) ) {
128
+ if ( $object instanceof Object\Calendar ) {
129
+ return $this->get_object( $object->type, 'feed', $object );
130
+ } elseif ( $object instanceof \WP_Post ) {
131
+ if ( $type = wp_get_object_terms( $object->ID, 'calendar_type' ) ) {
132
+ $name = sanitize_title( current( $type )->name );
133
+ return $this->get_object( $name, 'calendar', $object );
134
+ }
135
+ } elseif ( isset( $object->type ) && isset( $object->id ) ) {
136
+ return $this->get_object( $object->type, 'calendar', $object->id );
137
+ }
138
+ }
139
+
140
+ if ( is_int( $object ) ) {
141
+ $post = get_post( $object );
142
+ if ( $post && ( $type = wp_get_object_terms( $post->ID, 'calendar_type' ) ) ) {
143
+ $name = sanitize_title( current( $type )->name );
144
+ return $this->get_object( $name, 'calendar', $post );
145
+ }
146
+ }
147
+
148
+ return null;
149
+ }
150
+
151
+ /**
152
+ * Get a calendar view.
153
+ *
154
+ * @since 3.0.0
155
+ *
156
+ * @param int $id Feed post id.
157
+ * @param string $name (optional) Name of calendar view.
158
+ *
159
+ * @return null|Object\Calendar_View
160
+ */
161
+ public function get_calendar_view( $id = 0, $name = '' ) {
162
+
163
+ if ( ! $name && $id > 0 ) {
164
+
165
+ $calendar_view = get_post_meta( $id, '_calendar_view', true );
166
+
167
+ if ( $terms = wp_get_object_terms( $id, 'calendar_type' ) ) {
168
+ $calendar_type = sanitize_title( current( $terms )->name );
169
+ $name = isset( $calendar_view[ $calendar_type ] ) ? $calendar_type . '-' . $calendar_view[ $calendar_type ] : '';
170
+ }
171
+
172
+ }
173
+
174
+ return $name ? $this->get_object( $name, 'calendar-view', '' ) : null;
175
+ }
176
+
177
+ /**
178
+ * Get a feed.
179
+ *
180
+ * Returns the right type of feed.
181
+ *
182
+ * @since 3.0.0
183
+ *
184
+ * @param int|string|object|\WP_Post|Object\Calendar $object
185
+ *
186
+ * @return null|Object\Feed
187
+ */
188
+ public function get_feed( $object ) {
189
+
190
+ if ( is_string( $object ) ) {
191
+ return ! empty( $object ) ? $this->get_object( $object, 'feed', '' ) : null;
192
+ }
193
+
194
+ if ( is_object( $object ) ) {
195
+ if ( $object instanceof Object\Calendar ) {
196
+ $feed_name = '';
197
+ if ( empty( $object->feed ) ) {
198
+ if ( $feed_type = wp_get_object_terms( $object->id, 'feed_type' ) ) {
199
+ $feed_name = sanitize_title( current( $feed_type )->name );
200
+ }
201
+ } else {
202
+ $feed_name = $object->feed;
203
+ }
204
+ return $this->get_object( $feed_name, 'feed', $object );
205
+ } elseif ( $object instanceof \WP_Post ) {
206
+ $calendar = $this->get_calendar( $object );
207
+ return $this->get_object( $calendar->feed, 'feed', $calendar );
208
+ } elseif ( isset( $object->feed ) && isset( $object->id ) ) {
209
+ return $this->get_object( $object->feed, 'feed', $object );
210
+ }
211
+ }
212
+
213
+ if ( is_int( $object ) ) {
214
+ $calendar = $this->get_calendar( $object );
215
+ return isset( $calendar->feed ) ? $this->get_object( $calendar->feed, 'feed', $calendar ) : null;
216
+ }
217
+
218
+ return null;
219
+ }
220
+
221
+ /**
222
+ * Get a field.
223
+ *
224
+ * @since 3.0.0
225
+ *
226
+ * @param array $args Field args.
227
+ * @param string $name Field type.
228
+ *
229
+ * @return null|Object\Field
230
+ */
231
+ public function get_field( $args, $name = '' ) {
232
+
233
+ if ( empty( $name ) ) {
234
+ $name = isset( $args['type'] ) ? $args['type'] : false;
235
+ }
236
+
237
+ return $name ? $this->get_object( $name, 'field', $args ) : null;
238
+ }
239
+
240
+ /**
241
+ * Get a settings page.
242
+ *
243
+ * @since 3.0.0
244
+ *
245
+ * @param string $name
246
+ *
247
+ * @return null|Object\Admin_Page
248
+ */
249
+ public function get_admin_page( $name ) {
250
+ return $name ? $this->get_object( $name, 'admin-page' ) : null;
251
+ }
252
+
253
+ /**
254
+ * Get a plugin object.
255
+ *
256
+ * @since 3.0.0
257
+ * @access private
258
+ *
259
+ * @param string $name Object name.
260
+ * @param string $type Object type.
261
+ * @param mixed $args (optional) arguments for the class constructor.
262
+ *
263
+ * @return null|Object
264
+ */
265
+ private function get_object( $name, $type, $args = '' ) {
266
+
267
+ $types = array(
268
+ 'admin-page',
269
+ 'calendar',
270
+ 'calendar-view',
271
+ 'feed',
272
+ 'field',
273
+ );
274
+
275
+ if ( in_array( $type, $types ) ) {
276
+
277
+ $class_name = $this->make_class_name( $name, $type );
278
+ $parent = '\\' . __NAMESPACE__ . '\Abstracts\\' . implode( '_', array_map( 'ucfirst', explode( '-', $type ) ) );
279
+ $class = class_exists( $class_name ) ? new $class_name( $args ) : false;
280
+
281
+ return $class instanceof $parent ? $class : null;
282
+ }
283
+
284
+ return null;
285
+ }
286
+
287
+ /**
288
+ * Make class name from slug.
289
+ *
290
+ * Standardizes object naming and class names: <object-name> becomes <Class_Name>.
291
+ * The plugin autoloader uses a similar pattern.
292
+ *
293
+ * @since 3.0.0
294
+ * @access private
295
+ *
296
+ * @param string $name Object name.
297
+ * @param string $type Object type.
298
+ *
299
+ * @return string The class name complete with its full namespace.
300
+ */
301
+ private function make_class_name( $name, $type ) {
302
+
303
+ if ( 'calendar' == $type ) {
304
+ $namespace = '\\' . __NAMESPACE__ . '\Calendars\\';
305
+ } elseif ( 'calendar-view' == $type ) {
306
+ $namespace = '\\' . __NAMESPACE__ . '\Calendars\Views\\';
307
+ } elseif ( 'feed' == $type ) {
308
+ $namespace = '\\' . __NAMESPACE__ . '\Feeds\\';
309
+ } elseif ( 'field' == $type ) {
310
+ $namespace = '\\' . __NAMESPACE__ . '\Admin\Fields\\';
311
+ } elseif ( 'admin-page' == $type ) {
312
+ $namespace = '\\' . __NAMESPACE__ . '\Admin\Pages\\';
313
+ } else {
314
+ return '';
315
+ }
316
+
317
+ $class_name = implode( '_', array_map( 'ucfirst', explode( '-', $name ) ) );
318
+
319
+ return $namespace . $class_name;
320
+ }
321
+
322
+ }
includes/php-calendar.php DELETED
@@ -1,110 +0,0 @@
1
- <?php
2
- # PHP Calendar (version 2.3), written by Keith Devens
3
- # http://keithdevens.com/software/php_calendar
4
- # see example at http://keithdevens.com/weblog
5
- # License: http://keithdevens.com/software/license
6
-
7
- /*
8
- Changes made to original PHP Calendar script by me (Ross Hanney):
9
-
10
- - Renamed CSS classes to fit with my plugin
11
- - Slight modification of lines 63-71 to use Unix timestamp rather than day number
12
- - Renamed function to prevent conflicts
13
- - Replaced strftime with date_i18n
14
- - Use of $wp_locale to get weekday initials
15
- - Replaced htmlentities() with esc_attr() and esc_html()
16
- - Other small markup changes
17
- - Replaced gmmktime() with mktime()
18
- */
19
-
20
- function gce_generate_calendar( $year, $month, $days = array(), $day_name_length = 3, $month_href = NULL, $first_day = 0, $pn = array(), $widget = false ) {
21
- global $wp_locale;
22
-
23
- $paging = false;
24
-
25
- if( ! empty( $pn ) ) {
26
- $paging = true;
27
- }
28
-
29
- $first_of_month = mktime( 0, 0, 0, $month, 1, $year );
30
- #remember that mktime will automatically correct if invalid dates are entered
31
- # for instance, mktime(0,0,0,12,32,1997) will be the date for Jan 1, 1998
32
- # this provides a built in "rounding" feature to generate_calendar()
33
-
34
- $day_names = array(); #generate all the day names according to the current locale
35
- for ( $n = 0, $t = ( 3 + $first_day ) * 86400; $n < 7; $n++, $t += 86400 ) { #January 4, 1970 was a Sunday
36
- $day_names[$n]['full'] = date_i18n( 'l', $t, true );
37
- $day_names[$n]['initial'] = $wp_locale->get_weekday_initial( date_i18n( 'l', $t, true ) );
38
- }
39
-
40
- list( $month, $year, $month_name, $weekday ) = explode( ',', date_i18n( 'm, Y, F, w', $first_of_month ) );
41
- $weekday = ( $weekday + 7 - $first_day ) % 7; #adjust for $first_day
42
- $title = esc_html( $month_name ) . '&nbsp;' . $year; #note that some locales don't capitalize month and day names
43
-
44
- #Begin calendar. Uses a real <caption>. See http://diveintomark.org/archives/2002/07/03
45
- list( $p, $pl ) = each( $pn );
46
- list( $n, $nl ) = each( $pn ); #previous and next links, if applicable
47
-
48
- // Previous filter
49
- $p = apply_filters( 'gce_prev_text', $p );
50
-
51
- // Next filter
52
- $n = apply_filters( 'gce_next_text', $n );
53
-
54
- if( $widget ) {
55
- $p = '<div class="gce-prev">' . ( ( $pl ) ? ( '<a href="#" class="gce-change-month" title="' . esc_attr__( 'Previous', 'gce' ) . '" name="' . esc_attr( $pl ) . '" data-gce-grid-paging="' . esc_attr( $paging ) . '">' . $p . '</a>' ) : $p ) . '</div>';
56
- } else {
57
- $p = '<div class="gce-prev">' . ( ( $pl ) ? ( '<a href="#" class="gce-change-month" title="' . esc_attr__( 'Previous', 'gce' ) . '" name="' . esc_attr( $pl ) . '" data-gce-grid-paging="' . esc_attr( $paging ) . '">' . $p . '</a>' ) : $p ) . '</div>';
58
- }
59
-
60
- if( $widget ) {
61
- $n = '<div class="gce-next">' . ( ( $nl ) ? ( '<a href="#" class="gce-change-month" title="' . esc_attr__( 'Next', 'gce' ) . '" name="' . esc_attr( $nl ) . '" data-gce-grid-paging="' . esc_attr( $paging ) . '">' . $n . '</a>' ) : $n ) . '</div>';
62
- } else {
63
- $n = '<div class="gce-next">' . ( ( $nl ) ? ( '<a href="#" class="gce-change-month" title="' . esc_attr__( 'Next', 'gce' ) . '" name="' . esc_attr( $nl ) . '" data-gce-grid-paging="' . esc_attr( $paging ) . '">' . $n . '</a>' ) : $n ) . '</div>';
64
- }
65
-
66
- $calendar = '<table class="gce-calendar">' . "\n" .
67
- '<caption class="gce-caption">' .
68
- '<div class="gce-navbar">' .
69
- $p .
70
- $n .
71
- '<div class="gce-month-title">' . ( ( $month_href ) ? ( '<a href="' . esc_attr( $month_href ) . '">' . esc_html( $title ) . '</a>' ) : esc_html( $title ) ) . '</div>' .
72
- '</div>' .
73
- '</caption>' . "\n" .
74
- '<tr>' . "\n";
75
-
76
- if ( $day_name_length ) { #if the day names should be shown ($day_name_length > 0)
77
- #if day_name_length is >3, the full name of the day will be printed
78
- foreach ( $day_names as $d ) {
79
- $calendar .= '<th><abbr title="' . esc_attr( $d['full'] ) . '">' . esc_html( $d['initial'] ) . '</abbr></th>';
80
- }
81
-
82
- $calendar .= "</tr>\n<tr>";
83
- }
84
-
85
- $time_now = current_time( 'timestamp' );
86
- $today = mktime( 0, 0, 0, date( 'm', $time_now ), date( 'd', $time_now ), date( 'Y', $time_now ) );
87
-
88
- if ( $weekday > 0 ) $calendar .= '<td colspan="' . esc_attr( $weekday ) . '">&nbsp;</td>'; #initial 'empty' days
89
- for ( $day = 1, $days_in_month = date( 't', $first_of_month ); $day <= $days_in_month; $day++, $weekday++ ) {
90
- if ( 7 == $weekday ) {
91
- $weekday = 0; #start a new week
92
- $calendar .= "</tr>\n<tr>";
93
- }
94
-
95
- $timestamp = mktime( 0, 0, 0, $month, $day, $year );
96
-
97
- if ( isset( $days[$timestamp] ) && is_array( $days[$timestamp] ) ) {
98
- list( $link, $classes, $content ) = $days[$timestamp];
99
- $calendar .= '<td' . ( ( $classes ) ? ( ' class="' . esc_attr( $classes ) . '">' ) : '>' ) . ( ( $link ) ? ( '<a href="' . esc_url( $link ) . '"><span class="gce-day-number">' . esc_html( $day ) . '</span></a>' . $content ) : '<span class="gce-day-number">' . esc_html( $day ) . '</span>' . $content ) . '</td>';
100
- } else {
101
- $css_class = $timestamp < $time_now ? ' gce-day-past ' : ' gce-day-future ';
102
- $css_class .= ' gce-week-day-' . strval( $weekday ) . ' ';
103
- $calendar .= '<td class="' . esc_attr( $css_class ) . '"><span class="gce-day-number">' . esc_html( $day ) . '</span></td>';
104
- }
105
- }
106
-
107
- if ( 7 != $weekday ) $calendar .= '<td colspan="' . esc_attr( ( 7 - $weekday ) ) . '">&nbsp;</td>'; #remaining "empty" days
108
-
109
- return $calendar . "</tr>\n</table>\n";
110
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/post-types.php ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Custom Post Types and Taxonomies
4
+ *
5
+ * @package SimpleCalendar
6
+ */
7
+ namespace SimpleCalendar;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Custom Post Types and Taxonomies.
15
+ *
16
+ * Register and initialize custom post types and custom taxonomies.
17
+ *
18
+ * @since 3.0.0
19
+ */
20
+ class Post_Types {
21
+
22
+ /**
23
+ * Hook in WordPress init to register custom content.
24
+ *
25
+ * @since 3.0.0
26
+ */
27
+ public function __construct() {
28
+ // Register custom taxonomies.
29
+ add_action( 'init', array( __CLASS__, 'register_taxonomies' ), 5 );
30
+ // Register custom post types.
31
+ add_action( 'init', array( __CLASS__, 'register_post_types' ), 5 );
32
+ // Filter the calendar feed post content to display a calendar view.
33
+ add_filter( 'the_content', array( $this, 'filter_post_content' ), 100 );
34
+ // Delete calendar transients and notices upon post deletion.
35
+ add_action( 'before_delete_post', array( $this, 'upon_deletion' ), 10, 1 );
36
+ }
37
+
38
+ /**
39
+ * Register custom taxonomies.
40
+ *
41
+ * @since 3.0.0
42
+ */
43
+ public static function register_taxonomies() {
44
+
45
+ do_action( 'simcal_register_taxonomies' );
46
+
47
+ if ( ! taxonomy_exists( 'calendar_feed' ) ) {
48
+
49
+ // Feed Type.
50
+ $labels = array(
51
+ 'name' => __( 'Events Source Types', 'google-calendar-events' ),
52
+ 'singular_name' => __( 'Events Source Type', 'google-calendar-events' ),
53
+ 'menu_name' => __( 'Events Source Type', 'google-calendar-events' ),
54
+ 'all_items' => __( 'All Events Source Types', 'google-calendar-events' ),
55
+ 'parent_item' => __( 'Parent Events Source Type', 'google-calendar-events' ),
56
+ 'parent_item_colon' => __( 'Parent Events Source Type:', 'google-calendar-events' ),
57
+ 'new_item_name' => __( 'New Events Source Type', 'google-calendar-events' ),
58
+ 'add_new_item' => __( 'Add New Events Source Type', 'google-calendar-events' ),
59
+ 'edit_item' => __( 'Edit Events Source Type', 'google-calendar-events' ),
60
+ 'update_item' => __( 'Update Events Source Type', 'google-calendar-events' ),
61
+ 'view_item' => __( 'View Events Source Type', 'google-calendar-events' ),
62
+ 'separate_items_with_commas' => __( 'Separate events source types with commas', 'google-calendar-events' ),
63
+ 'add_or_remove_items' => __( 'Add or remove events source types', 'google-calendar-events' ),
64
+ 'choose_from_most_used' => __( 'Choose from the most used', 'google-calendar-events' ),
65
+ 'popular_items' => __( 'Popular events source types', 'google-calendar-events' ),
66
+ 'search_items' => __( 'Search Events Source Types', 'google-calendar-events' ),
67
+ 'not_found' => __( 'Not Found', 'google-calendar-events' ),
68
+ );
69
+
70
+ $args = array(
71
+ 'hierarchical' => true,
72
+ 'labels' => $labels,
73
+ 'public' => false,
74
+ 'show_admin_column' => false,
75
+ 'show_in_nav_menus' => false,
76
+ 'show_tagcloud' => false,
77
+ 'show_ui' => false,
78
+ );
79
+ register_taxonomy( 'calendar_feed', array( 'calendar' ), $args );
80
+
81
+ }
82
+
83
+ if ( ! taxonomy_exists( 'calendar_type' ) ) {
84
+
85
+ // Calendar Type.
86
+ $labels = array(
87
+ 'name' => __( 'Calendar Types', 'google-calendar-events' ),
88
+ 'singular_name' => __( 'Calendar Type', 'google-calendar-events' ),
89
+ 'menu_name' => __( 'Calendar Type', 'google-calendar-events' ),
90
+ 'all_items' => __( 'All Calendar Types', 'google-calendar-events' ),
91
+ 'parent_item' => __( 'Parent Calendar Type', 'google-calendar-events' ),
92
+ 'parent_item_colon' => __( 'Parent Calendar Type:', 'google-calendar-events' ),
93
+ 'new_item_name' => __( 'New Calendar Type', 'google-calendar-events' ),
94
+ 'add_new_item' => __( 'Add New Calendar Type', 'google-calendar-events' ),
95
+ 'edit_item' => __( 'Edit Calendar Type', 'google-calendar-events' ),
96
+ 'update_item' => __( 'Update Calendar Type', 'google-calendar-events' ),
97
+ 'view_item' => __( 'View Calendar Type', 'google-calendar-events' ),
98
+ 'separate_items_with_commas' => __( 'Separate calendar types with commas', 'google-calendar-events' ),
99
+ 'add_or_remove_items' => __( 'Add or remove calendar types', 'google-calendar-events' ),
100
+ 'choose_from_most_used' => __( 'Choose from the most used', 'google-calendar-events' ),
101
+ 'popular_items' => __( 'Popular calendar types', 'google-calendar-events' ),
102
+ 'search_items' => __( 'Search Calendar Types', 'google-calendar-events' ),
103
+ 'not_found' => __( 'Not Found', 'google-calendar-events' ),
104
+ );
105
+
106
+ $args = array(
107
+ 'hierarchical' => true,
108
+ 'labels' => $labels,
109
+ 'public' => false,
110
+ 'show_admin_column' => false,
111
+ 'show_in_nav_menus' => false,
112
+ 'show_tagcloud' => false,
113
+ 'show_ui' => false,
114
+ );
115
+ register_taxonomy( 'calendar_type', array( 'calendar' ), $args );
116
+
117
+ }
118
+
119
+ if ( ! taxonomy_exists( 'calendar_category' ) ) {
120
+
121
+ // Feed Category.
122
+ $labels = array(
123
+ 'name' => __( 'Categories', 'google-calendar-events' ),
124
+ 'singular_name' => __( 'Category', 'google-calendar-events' ),
125
+ 'menu_name' => __( 'Categories', 'google-calendar-events' ),
126
+ 'all_items' => __( 'All Categories', 'google-calendar-events' ),
127
+ 'parent_item' => __( 'Parent Category', 'google-calendar-events' ),
128
+ 'parent_item_colon' => __( 'Parent Category:', 'google-calendar-events' ),
129
+ 'new_item_name' => __( 'New Category', 'google-calendar-events' ),
130
+ 'add_new_item' => __( 'Add New Category', 'google-calendar-events' ),
131
+ 'edit_item' => __( 'Edit Category', 'google-calendar-events' ),
132
+ 'update_item' => __( 'Update Category', 'google-calendar-events' ),
133
+ 'view_item' => __( 'View Category', 'google-calendar-events' ),
134
+ 'separate_items_with_commas' => __( 'Separate categories with commas', 'google-calendar-events' ),
135
+ 'add_or_remove_items' => __( 'Add or remove categories', 'google-calendar-events' ),
136
+ 'choose_from_most_used' => __( 'Choose from the most used', 'google-calendar-events' ),
137
+ 'popular_items' => __( 'Popular Categories', 'google-calendar-events' ),
138
+ 'search_items' => __( 'Search Categories', 'google-calendar-events' ),
139
+ 'not_found' => __( 'Not Found', 'google-calendar-events' ),
140
+ );
141
+
142
+ $args = array(
143
+ 'hierarchical' => true,
144
+ 'labels' => $labels,
145
+ 'public' => true,
146
+ 'show_admin_column' => true,
147
+ 'show_in_nav_menus' => true,
148
+ 'show_tagcloud' => false,
149
+ 'show_ui' => true,
150
+ );
151
+
152
+ register_taxonomy( 'calendar_category', array( 'calendar' ), $args );
153
+ }
154
+
155
+ }
156
+
157
+ /**
158
+ * Register custom post types.
159
+ *
160
+ * @since 3.0.0
161
+ */
162
+ public static function register_post_types() {
163
+
164
+ do_action( 'simcal_register_post_types' );
165
+
166
+ if ( ! post_type_exists( 'calendar' ) ) {
167
+
168
+ // Calendar feed post type.
169
+ $labels = array(
170
+ 'name' => _x( 'Calendars', 'Post Type General Name', 'google-calendar-events' ),
171
+ 'singular_name' => _x( 'Calendar', 'Post Type Singular Name', 'google-calendar-events' ),
172
+ 'menu_name' => __( 'Calendars', 'google-calendar-events' ),
173
+ 'name_admin_bar' => __( 'Calendar', 'google-calendar-events' ),
174
+ 'parent_item_colon' => __( 'Parent Calendar:', 'google-calendar-events' ),
175
+ 'all_items' => __( 'All Calendars', 'google-calendar-events' ),
176
+ 'add_new_item' => __( 'Add New Calendar', 'google-calendar-events' ),
177
+ 'add_new' => __( 'Add New', 'google-calendar-events' ),
178
+ 'new_item' => __( 'New Calendar', 'google-calendar-events' ),
179
+ 'edit_item' => __( 'Edit Calendar', 'google-calendar-events' ),
180
+ 'update_item' => __( 'Update Calendar', 'google-calendar-events' ),
181
+ 'view_item' => __( 'View Calendar', 'google-calendar-events' ),
182
+ 'search_items' => __( 'Search Calendar', 'google-calendar-events' ),
183
+ 'not_found' => __( 'Calendars not found', 'google-calendar-events' ),
184
+ 'not_found_in_trash' => __( 'Calendars not found in Trash', 'google-calendar-events' ),
185
+ );
186
+
187
+ $rewrite_rules = array(
188
+ 'feeds' => false,
189
+ 'pages' => false,
190
+ 'with_front' => false,
191
+ 'slug' => 'calendar',
192
+ );
193
+
194
+ $svg_icon = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iU2hhcGVzX3hBMF9JbWFnZV8xXyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDEwMjQgMTAyNCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIGQ9Ik01MTMuNCwxMDEzLjNjLTE2My44LDAtMzI3LjcsMC00OTEuNSwwYy05LjcsMC04LjgsMC45LTguOC04LjljMC0yNDMuNywwLjItNDg3LjMtMC4zLTczMWMtMC4xLTM5LjYsMjcuNy03Ni40LDY5LjYtODIuM2MzLTAuNCw2LTAuNSw5LTAuNWMzNSwwLDcwLDAuMSwxMDUtMC4xYzUuMSwwLDYuNSwxLjQsNi41LDYuNWMtMC4yLDI2LjgsMC4xLDUzLjctMC4yLDgwLjVjLTAuMiwxNS4yLDMuOSwyOC4yLDE1LjksMzguMmMyLjcsMi4yLDUsNC44LDcuNCw3LjRjOCw4LjYsMTguMSwxMi40LDI5LjYsMTIuNGMzMC43LDAuMiw2MS4zLDAuMiw5MiwwYzExLjItMC4xLDIxLjItMy45LDI5LjEtMTIuMmMzLjQtMy42LDctNy4yLDEwLjYtMTAuNmM5LTguNCwxMi43LTE4LjksMTIuNy0zMWMwLjEtMjguMiwwLTU2LjMsMC04NC41YzAtNi42LDAtNi42LDYuNi02LjZjNzEuNSwwLDE0MywwLDIxNC41LDBjNi42LDAsNi42LDAsNi42LDYuN2MwLDI2LjgsMC4yLDUzLjctMC4xLDgwLjVjLTAuMiwxNSw1LjEsMjYuOCwxNS40LDM4YzEzLjYsMTQuNywyOC45LDIwLjgsNDguNywyMC4xYzI1LjYtMSw1MS4zLTAuNCw3Ny0wLjJjMTMuOSwwLjEsMjQuOC01LjEsMzUuNC0xNC40YzE1LjktMTQsMjIuMS0zMC40LDIxLjEtNTEuM2MtMS4xLTI0LjMtMC4xLTQ4LjctMC40LTczYzAtNS4xLDEuNC02LjUsNi41LTYuNWMzNC43LDAuMiw2OS4zLDAsMTA0LDAuMWM0Mi4zLDAuMiw3OC4zLDM2LDc4LjMsNzguM2MwLjEsMjQ2LDAsNDkyLDAsNzM4YzAsNi40LDAsNi40LTcuMyw2LjRDODQyLDEwMTMuMyw2NzcuNywxMDEzLjMsNTEzLjQsMTAxMy4zeiBNNDM5LjUsNjc4LjljMS42LTEsMi4yLTEuNSwzLTEuOGMxMS44LTUuOCwyMS41LTE0LjIsMjktMjQuOGMyNC4zLTM0LjEsMjQuMy03MC45LDguNi0xMDcuOGMtMTMuMy0zMS4zLTM5LjMtNDkuMy03MS01OS40Yy0yNy42LTguOC01Ni05LjQtODQuNS01LjZjLTIwLjMsMi43LTM5LjIsOS42LTU1LjUsMjIuMmMtMzUuNSwyNy4yLTQ4LjQsNjUuMS01MC40LDEwOGMtMC4yLDMuNywyLjEsMy45LDQuOCwzLjljMjIuMiwwLDQ0LjMtMC4xLDY2LjUsMGMzLjgsMCw1LjQtMS4xLDUuMy01LjFjLTAuMS03LjcsMS0xNS4zLDMtMjIuN2M1LjQtMjAsMTYuNS0zNC43LDM3LjgtMzkuN2M4LjEtMS45LDE2LjMtMi4xLDI0LjQtMS4zYzIxLjQsMi4xLDM2LjMsMTMuMSw0My41LDMzLjdjMy41LDEwLjEsMy45LDIwLjQsMi45LDMxYy0yLjIsMjMuNC0xNi4yLDM5LjctMzkuMSw0NC42Yy0xMy4yLDIuOC0yNi42LDQuMS00MC4yLDRjLTMuNywwLTUsMS4yLTQuOSw0LjljMC4xLDE2LjUsMC4yLDMzLDAsNDkuNWMwLDQsMS41LDUuMiw1LjMsNS4xYzEyLjktMC4zLDI1LjYsMC45LDM4LjMsM2MyNi4zLDQuMiw0My41LDE4LjgsNDkuMiw0MmMzLjMsMTMuNSwzLDI3LjEtMC4yLDQwLjZjLTIuMyw5LjctNi44LDE4LjQtMTMuOCwyNS43Yy0xNS40LDE2LjEtMzQuNSwyMS42LTU2LDE4LjljLTI3LjUtMy40LTQzLjUtMTgtNTAuNS00NC44Yy0xLjktNy4zLTMuMS0xNC43LTIuOC0yMi4yYzAuMi00LTEuMS01LjYtNS40LTUuNmMtMjMuNywwLjItNDcuMywwLjItNzEsMGMtNCwwLTUuNCwxLjItNC45LDUuMmMxLjQsMTMuMiwyLjcsMjYuNSw1LjksMzkuNWMxMS4xLDQ1LjIsMzguMiw3NS42LDgzLjQsODguMmMzNi43LDEwLjMsNzMuOCwxMC4xLDExMC41LDAuMWMyNC41LTYuNiw0NC43LTIwLjEsNjEtMzkuOGMyNC41LTI5LjcsMzQuNC02My45LDMxLjQtMTAxLjljLTIuMi0yNy40LTEyLjUtNTEuMy0zMy42LTY5LjhDNDYwLjcsNjg5LjMsNDUxLjksNjgyLDQzOS41LDY3OC45eiBNNzU1LDY5Mi41YzAtNjguNi0wLjEtMTM3LjMsMC4xLTIwNS45YzAtNS4xLTEuNC02LjUtNi41LTYuNGMtMTguMywwLjMtMzYuNywwLjQtNTUsMGMtNS43LTAuMS03LjIsMS45LTguMiw3Yy01LjksMzIuMS0yNC40LDUzLTU2LjMsNjEuMWMtMTcuNSw0LjUtMzUuNiw1LTUzLjUsNS44Yy0zLjksMC4yLTUuMiwxLjQtNS4yLDUuM2MwLjIsMTUuMywwLjIsMzAuNywwLDQ2Yy0wLjEsNC4zLDEuNiw1LjQsNS42LDUuM2MxNC4yLTAuMiwyOC4zLTAuMSw0Mi41LTAuMWMxNS41LDAsMzEsMC4xLDQ2LjUtMC4xYzQuMSwwLDUuOSwxLjMsNS41LDUuNGMtMC4xLDEuNywwLDMuMywwLDVjMCw5Mi4xLDAsMTg0LjMsMCwyNzYuNGMwLDcuMiwwLDcuMiw3LDcuMmMyMy41LDAsNDcsMCw3MC41LDBjNywwLDcsMCw3LTcuMkM3NTUsODI5LjEsNzU1LDc2MC44LDc1NSw2OTIuNXoiLz48cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgZD0iTTM3Ni43LDE5OS4yYzAsMjQuMiwwLDQ4LjMsMCw3Mi41Yy0wLjEsMjMuMS0xNy40LDQwLjUtNDAuNCw0MC42Yy0yMy4zLDA