mhc 1.2.3 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.rubocop.yml +14 -0
- data/README.org +37 -0
- data/bin/mhc +216 -4
- data/emacs/Cask +1 -1
- data/emacs/mhc-date.el +4 -11
- data/emacs/mhc-db.el +42 -7
- data/emacs/mhc-record.el +1 -1
- data/emacs/mhc-summary.el +5 -2
- data/emacs/mhc-vars.el +1 -1
- data/emacs/mhc.el +31 -7
- data/lib/mhc/caldav.rb +1 -1
- data/lib/mhc/calendar.rb +20 -0
- data/lib/mhc/command/completions.rb +1 -1
- data/lib/mhc/config.rb +5 -1
- data/lib/mhc/converter.rb +74 -3
- data/lib/mhc/datastore.rb +9 -5
- data/lib/mhc/formatter/html.rb +32 -0
- data/lib/mhc/formatter.rb +3 -0
- data/lib/mhc/templates/full-calendar.html.erb +417 -0
- data/lib/mhc/version.rb +1 -1
- data/mhc.gemspec +2 -1
- data/samples/japanese-holidays.mhcc +10 -9
- metadata +22 -6
data/lib/mhc/converter.rb
CHANGED
@@ -131,15 +131,86 @@ module Mhc
|
|
131
131
|
end
|
132
132
|
end
|
133
133
|
|
134
|
+
# https://github.com/rubyredrick/ri_cal
|
135
|
+
def value_part(unit, diff) # :nodoc:
|
136
|
+
(diff == 0) ? nil : "#{diff}#{unit}"
|
137
|
+
end
|
138
|
+
|
139
|
+
# https://github.com/rubyredrick/ri_cal
|
140
|
+
def from_datetimes(start, finish, sign = '') # :nodoc:
|
141
|
+
if start > finish
|
142
|
+
from_datetimes(finish, start, '-')
|
143
|
+
else
|
144
|
+
diff = finish - start
|
145
|
+
days_diff = diff.to_i
|
146
|
+
hours = (diff - days_diff) * 24
|
147
|
+
hour_diff = hours.to_i
|
148
|
+
minutes = (hours - hour_diff) * 60
|
149
|
+
min_diff = minutes.to_i
|
150
|
+
seconds = (minutes - min_diff) * 60
|
151
|
+
sec_diff = seconds.to_i
|
152
|
+
|
153
|
+
day_part = value_part('D',days_diff)
|
154
|
+
hour_part = value_part('H', hour_diff)
|
155
|
+
min_part = value_part('M', min_diff)
|
156
|
+
sec_part = value_part('S', sec_diff)
|
157
|
+
t_part = (hour_diff.abs + min_diff.abs + sec_diff.abs) == 0 ? "" : "T"
|
158
|
+
"P#{day_part}#{t_part}#{hour_part}#{min_part}#{sec_part}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Convert ~X-SC-Days:~ to RFC5545 3.8.2 ~RDATE~
|
163
|
+
#
|
164
|
+
# RDATE allows three types of format: PERIOD, DATE, DATE-TIME.
|
165
|
+
# + DATE :: 20140301
|
166
|
+
# + DATE-TIME :: 20140301T123000
|
167
|
+
# + PERIOD :: 20140301T123000/20140301T143000 or 20140301T123000/PT2H
|
168
|
+
#
|
169
|
+
# NOTE: *Google calenadr does not accept PERIOD*
|
170
|
+
# That's why we cannot make X-SC-Day: have different duration-period for each occurrence.
|
171
|
+
#
|
134
172
|
def rdates(event)
|
135
173
|
return nil if event.dates.empty?
|
136
|
-
|
174
|
+
# Get all RDATE candidates from X-SC-Date
|
175
|
+
ocs = Mhc::OccurrenceEnumerator.new(event, event.dates, empty_dates, empty_condition, empty_duration).map do |oc|
|
176
|
+
if oc.dtstart.respond_to?(:hour)
|
177
|
+
# duration = from_datetimes(oc.dtstart, oc.dtend)
|
178
|
+
|
179
|
+
# 20140301T123000/20140301T143000
|
180
|
+
dtstart = IcalendarImporter.tz_convert(oc.dtstart, dst_tzid: 'UTC').strftime("%Y%m%dT%H%M00Z")
|
181
|
+
dtend = IcalendarImporter.tz_convert(oc.dtend, dst_tzid: 'UTC').strftime("%Y%m%dT%H%M00Z")
|
182
|
+
|
183
|
+
dtstart + "/" + dtend
|
184
|
+
# dtstart + "/" + duration
|
185
|
+
else
|
186
|
+
oc.dtstart
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Check if all in X-SC-Date should be converted into RDATE,
|
191
|
+
# because the first occurrence of X-SC-Date might be dedicated
|
192
|
+
# to DTSTART in some particular case.
|
193
|
+
#
|
137
194
|
if event.recurring?
|
195
|
+
# Use all values of X-SC-Date as RDATE, because DTSTART
|
196
|
+
# should be calculated from recurrence-rule (X-SC-Cond and
|
197
|
+
# X-SC-Duration).
|
138
198
|
ocs
|
139
199
|
else
|
140
|
-
|
141
|
-
|
200
|
+
# This is Workaround for avoiding the serious bug in Android.
|
201
|
+
# Android badly ignores DTSTART-DTEND if RDATE is set.
|
202
|
+
# As a result, the first occurence is vanished.
|
203
|
+
# So, I set the first occurence twice; in RDATE and DTSTART.
|
204
|
+
return nil if ocs[1..-1].empty?
|
142
205
|
return ocs
|
206
|
+
|
207
|
+
# This is what I wanted to do:
|
208
|
+
# Remove the first occurence of X-SC-Date, because the first
|
209
|
+
# value should be dedicated to DTSTART.
|
210
|
+
#
|
211
|
+
# ocs = ocs[1..-1]
|
212
|
+
# return nil if ocs.empty?
|
213
|
+
# return ocs
|
143
214
|
end
|
144
215
|
end
|
145
216
|
|
data/lib/mhc/datastore.rb
CHANGED
@@ -25,7 +25,7 @@ module Mhc
|
|
25
25
|
Dir.foreach(".") do |ent|
|
26
26
|
parse_mhcc(ent).each {|ev|
|
27
27
|
next if category && !ev.in_category?(category)
|
28
|
-
next if recurrence && !ev.
|
28
|
+
next if recurrence && !(ev.recurrence_tag.to_s == recurrence)
|
29
29
|
yielder << ev
|
30
30
|
} if /\.mhcc$/ =~ ent
|
31
31
|
next unless /\.mhc$/ =~ ent
|
@@ -52,22 +52,25 @@ module Mhc
|
|
52
52
|
return Event.parse_file(path)
|
53
53
|
end
|
54
54
|
|
55
|
-
def create(event)
|
55
|
+
def create(event, draft = false)
|
56
56
|
if find_by_uid(event.uid)
|
57
57
|
raise "Already exist uid:#{uid} in #{@basedir}"
|
58
58
|
end
|
59
|
+
path = uid_to_path(event.uid, draft)
|
59
60
|
File.open(path, "w") do |f|
|
60
61
|
f.write(event.dump)
|
61
62
|
end
|
63
|
+
return path.to_s
|
62
64
|
end
|
63
65
|
|
64
|
-
def update(event)
|
65
|
-
unless path = uid_to_path(event.uid)
|
66
|
+
def update(event, draft = false)
|
67
|
+
unless path = uid_to_path(event.uid, draft)
|
66
68
|
raise "Not found uid:#{uid} in #{@basedir}"
|
67
69
|
end
|
68
70
|
File.open(path, "w") do |f|
|
69
71
|
f.write(event.dump)
|
70
72
|
end
|
73
|
+
return path.to_s
|
71
74
|
end
|
72
75
|
|
73
76
|
def delete(uid_or_event)
|
@@ -105,7 +108,8 @@ module Mhc
|
|
105
108
|
return Dir.glob(glob).first
|
106
109
|
end
|
107
110
|
|
108
|
-
def uid_to_path(uid)
|
111
|
+
def uid_to_path(uid, draft = false)
|
112
|
+
return @basedir + ('draft/' + uid + '.mhc') if draft
|
109
113
|
return @basedir + ('spool/' + uid + '.mhc')
|
110
114
|
end
|
111
115
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Mhc
|
2
|
+
class Formatter
|
3
|
+
class Html < Base
|
4
|
+
TEMPLATE_DIR = File.expand_path("../../templates", __FILE__)
|
5
|
+
|
6
|
+
def initialize(date_range:, options:)
|
7
|
+
super(date_range: date_range, options: options)
|
8
|
+
@json_formatter = Json.new(date_range: date_range, options: options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(occurrence)
|
12
|
+
@json_formatter << occurrence
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def format_header(context)
|
18
|
+
require "erb"
|
19
|
+
template_path = File.expand_path("full-calendar.html.erb", TEMPLATE_DIR)
|
20
|
+
@template = ERB.new(File.open(template_path).read, nil, "-")
|
21
|
+
return ""
|
22
|
+
end
|
23
|
+
|
24
|
+
def format_body(context)
|
25
|
+
env = Struct.new(:json_event_array, :tzid)
|
26
|
+
mhc = env.new(@json_formatter.to_s, Mhc.default_tzid) # used in ERB template
|
27
|
+
@template.result(binding)
|
28
|
+
end
|
29
|
+
|
30
|
+
end # class Html
|
31
|
+
end # class Formatter
|
32
|
+
end # module Mhc
|
data/lib/mhc/formatter.rb
CHANGED
@@ -21,6 +21,8 @@ module Mhc
|
|
21
21
|
Howm.new(date_range: date_range, options:options)
|
22
22
|
when :json
|
23
23
|
Json.new(date_range: date_range, options:options)
|
24
|
+
when :html
|
25
|
+
Html.new(date_range: date_range, options:options)
|
24
26
|
else
|
25
27
|
raise Formatter::NameError.new("Unknown format: #{formatter} (#{formatter.class})")
|
26
28
|
end
|
@@ -37,6 +39,7 @@ module Mhc
|
|
37
39
|
autoload :OrgTable, "#{dir}/org_table.rb"
|
38
40
|
autoload :SymbolicExpression, "#{dir}/symbolic_expression.rb"
|
39
41
|
autoload :Text, "#{dir}/text.rb"
|
42
|
+
autoload :Html, "#{dir}/html.rb"
|
40
43
|
|
41
44
|
end # class Formatter
|
42
45
|
end # module Mhc
|
@@ -0,0 +1,417 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
7
|
+
|
8
|
+
<title>Calendar</title>
|
9
|
+
|
10
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.9.0/fullcalendar.min.css" />
|
11
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" />
|
12
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap-theme.min.css" />
|
13
|
+
|
14
|
+
<style media="screen" type="text/css">
|
15
|
+
.container {
|
16
|
+
// max-width: 100%;
|
17
|
+
width: auto;
|
18
|
+
}
|
19
|
+
|
20
|
+
.ui-draggable {
|
21
|
+
cursor: grab;
|
22
|
+
}
|
23
|
+
|
24
|
+
a {
|
25
|
+
cursor: pointer;
|
26
|
+
}
|
27
|
+
|
28
|
+
.fc-sun {
|
29
|
+
color:#gray;
|
30
|
+
background-color: #fff0f0;
|
31
|
+
}
|
32
|
+
|
33
|
+
.fc-sat {
|
34
|
+
color:#gray;
|
35
|
+
background-color: #f0f0ff;
|
36
|
+
}
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
#calendar .mhc-allday {
|
41
|
+
background-color: lightblue;
|
42
|
+
border-color: lightblue;
|
43
|
+
color: black;
|
44
|
+
}
|
45
|
+
|
46
|
+
#calendar .mhc-time-range {
|
47
|
+
background-color: #f7f7ff;
|
48
|
+
border-color: #f7f7ff;
|
49
|
+
color: black;
|
50
|
+
}
|
51
|
+
|
52
|
+
#calendar .mhc-time-range:hover {
|
53
|
+
background-color: aliceblue;
|
54
|
+
border-color: lavender;
|
55
|
+
color: black;
|
56
|
+
}
|
57
|
+
|
58
|
+
#calendar .mhc-category-holiday {
|
59
|
+
background-color: orangered;
|
60
|
+
border-color: orangered;
|
61
|
+
color: white;
|
62
|
+
}
|
63
|
+
|
64
|
+
#calendar .mhc-category-birthday {
|
65
|
+
background-color: #ffffe8;
|
66
|
+
border-color: #ffffe8;
|
67
|
+
color: black;
|
68
|
+
}
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
#side-calendar .mhc-category-birthday {
|
74
|
+
display: none;
|
75
|
+
}
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
#stuck-events .fc-list-heading {
|
81
|
+
display: none;
|
82
|
+
}
|
83
|
+
|
84
|
+
</style>
|
85
|
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
|
86
|
+
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.js"></script>
|
87
|
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.js"></script>
|
88
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/moment.js"></script>
|
89
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.9.0/fullcalendar.js"></script>
|
90
|
+
|
91
|
+
</head>
|
92
|
+
<body>
|
93
|
+
<!--
|
94
|
+
<div class="navmenu navmenu-default navmenu-fixed-left offcanvas">
|
95
|
+
<a class="navmenu-brand" href="#">brand</a>
|
96
|
+
<ul class="nav navmenu-nav">
|
97
|
+
<li><a href="#">Menu text 1</a></li>
|
98
|
+
<li><a href="#">Menu text 2</a></li>
|
99
|
+
<li><a href="#">Menu text 3</a></li>
|
100
|
+
</ul>
|
101
|
+
</div>
|
102
|
+
<div class="navbar navbar-default navbar-fixed-top">
|
103
|
+
<button type="button" class="navbar-toggle" data-toggle="offcanvas" data-target=".navmenu" data-canvas="body">
|
104
|
+
<span class="icon-bar"></span>
|
105
|
+
<span class="icon-bar"></span>
|
106
|
+
<span class="icon-bar"></span>
|
107
|
+
</button>
|
108
|
+
</div>
|
109
|
+
-->
|
110
|
+
|
111
|
+
<div class="container">
|
112
|
+
<div class="row">
|
113
|
+
|
114
|
+
<div class="col-xs-3">
|
115
|
+
<ul class="nav nav-tabs">
|
116
|
+
<li class="active"><a data-toggle="tab" href="#side-calendar">Past Events</a></li>
|
117
|
+
<li><a data-toggle="tab" href="#stuck-events">Stuck Events</a></li>
|
118
|
+
</ul>
|
119
|
+
<div class="tab-content">
|
120
|
+
<div id="side-calendar" class="tab-pane fade in active"></div>
|
121
|
+
<div id="stuck-events" class="tab-pane fade"></div>
|
122
|
+
</div>
|
123
|
+
</div><!-- col-xs-3 -->
|
124
|
+
|
125
|
+
<div class="col-xs-9" id="calendar"></div> <!-- col-xs-9 -->
|
126
|
+
</div><!-- row -->
|
127
|
+
</div><!-- /.container -->
|
128
|
+
|
129
|
+
<script>
|
130
|
+
function create_uuid() {
|
131
|
+
var d = +new Date();
|
132
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
|
133
|
+
.replace(/[xy]/g, function(c) {
|
134
|
+
var r = (d + Math.random() * 16) % 16 | 0;
|
135
|
+
return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16).toUpperCase();
|
136
|
+
});
|
137
|
+
}
|
138
|
+
|
139
|
+
function max_height_for(element) {
|
140
|
+
var top = $(element).position().top;
|
141
|
+
return $(window).height() - top - 7;
|
142
|
+
}
|
143
|
+
|
144
|
+
function adjust_height(element) {
|
145
|
+
var height = max_height_for(element);
|
146
|
+
$(element).fullCalendar('option', 'height', height);
|
147
|
+
}
|
148
|
+
|
149
|
+
var json_event_array = [<%= mhc.json_event_array %>];
|
150
|
+
|
151
|
+
$(document).ready(function() {
|
152
|
+
|
153
|
+
$('#side-calendar').fullCalendar({
|
154
|
+
defaultView: 'listMonth',
|
155
|
+
header: {
|
156
|
+
left: '',
|
157
|
+
center: 'title',
|
158
|
+
right: ''
|
159
|
+
},
|
160
|
+
height: max_height_for('#side-calendar'),
|
161
|
+
editable: true,
|
162
|
+
eventResourceEditable: true,
|
163
|
+
eventLimit: true,
|
164
|
+
selectable: true,
|
165
|
+
droppable: true,
|
166
|
+
timezone: '<%= mhc.tzid %>',
|
167
|
+
timeFormat: "HH:mm",
|
168
|
+
displayEventEnd: false,
|
169
|
+
allDayText: "",
|
170
|
+
month: 'HH:mm',
|
171
|
+
week: 'HH:mm',
|
172
|
+
day: 'HH:mm',
|
173
|
+
// eventSources: [<%= $stuck_recurrences_json %>],
|
174
|
+
eventSources: json_event_array,
|
175
|
+
XeventDragStart: function(calEvent, jsEvent, ui, view) {
|
176
|
+
alert('Drag Event: ' + calEvent.title);
|
177
|
+
alert('Event ID: ' + calEvent.id);
|
178
|
+
alert('Coordinates: ' + jsEvent.pageX + ',' + jsEvent.pageY);
|
179
|
+
alert('View: ' + view.name);
|
180
|
+
// change the border color just for fun
|
181
|
+
$(this).css('border-color', 'red');
|
182
|
+
},
|
183
|
+
viewRender: function(view, element) {
|
184
|
+
$('tr.fc-list-item').each(function() {
|
185
|
+
var duration;
|
186
|
+
var seg = $(this).data('fc-seg');
|
187
|
+
var myevent = seg.footprint.getEventLegacy();
|
188
|
+
if (myevent.end) {
|
189
|
+
var minutes = moment.duration(myevent.end.diff(myevent.start)).asMinutes();
|
190
|
+
var days = Math.floor(minutes/(24*60));
|
191
|
+
minutes -= days * 24 * 60;
|
192
|
+
var hours = Math.floor(minutes/60);
|
193
|
+
minutes -= hours * 60;
|
194
|
+
duration = days + "." + hours + ":" + minutes;
|
195
|
+
} else {
|
196
|
+
duration = "0.00:00";
|
197
|
+
}
|
198
|
+
if (myevent.title == "母の日") {
|
199
|
+
// alert('My Event: ' + myevent.title);
|
200
|
+
// alert('Duration: ' + duration);
|
201
|
+
}
|
202
|
+
var uuid = create_uuid();
|
203
|
+
// alert('Seg: ' + seg);
|
204
|
+
$(this).data('event', {
|
205
|
+
title: myevent.title,
|
206
|
+
allDay: myevent.allDay,
|
207
|
+
start: myevent.start.format("HH:mm"),
|
208
|
+
duration: duration,
|
209
|
+
stick: true,
|
210
|
+
className: myevent.className
|
211
|
+
});
|
212
|
+
});
|
213
|
+
|
214
|
+
$('tr.fc-list-item').draggable({
|
215
|
+
appendTo: "#calendar",
|
216
|
+
cursorAt: {top: 5, left: 5},
|
217
|
+
iframeFix: true,
|
218
|
+
refreshPositions: true,
|
219
|
+
scroll: false,
|
220
|
+
cursor: "move",
|
221
|
+
revert: true, // will cause the event to go back to its
|
222
|
+
revertDuration: 0, // original position after the drag
|
223
|
+
helper: "clone",
|
224
|
+
zIndex: 100,
|
225
|
+
opacity: 0.35,
|
226
|
+
|
227
|
+
start: function(event, ui) {
|
228
|
+
}
|
229
|
+
});
|
230
|
+
if(view.name == 'listMonth') {
|
231
|
+
adjust_height('#side-calendar');
|
232
|
+
}
|
233
|
+
},
|
234
|
+
windowResize: function(view) {
|
235
|
+
if(view.name == 'listMonth') {
|
236
|
+
adjust_height('#side-calendar');
|
237
|
+
}
|
238
|
+
}
|
239
|
+
});
|
240
|
+
|
241
|
+
$('#stuck-events').fullCalendar({
|
242
|
+
defaultView: 'listall',
|
243
|
+
Xheader: {
|
244
|
+
left: '',
|
245
|
+
center: '',
|
246
|
+
right: ''
|
247
|
+
},
|
248
|
+
header: false,
|
249
|
+
views: {
|
250
|
+
listall: {
|
251
|
+
type: 'list',
|
252
|
+
duration: { days: 3650},
|
253
|
+
dayCount: 30
|
254
|
+
}
|
255
|
+
},
|
256
|
+
height: max_height_for('#side-calendar'), // XXX
|
257
|
+
editable: true,
|
258
|
+
eventResourceEditable: true,
|
259
|
+
eventLimit: true,
|
260
|
+
selectable: true,
|
261
|
+
droppable: true,
|
262
|
+
timezone: '<%= mhc.tzid %>',
|
263
|
+
timeFormat: " ",
|
264
|
+
allDayText: "",
|
265
|
+
month: 'HH:mm',
|
266
|
+
week: 'HH:mm',
|
267
|
+
day: 'HH:mm',
|
268
|
+
eventSources: [<%= $stuck_recurrences_json %>],
|
269
|
+
XeventDragStart: function(calEvent, jsEvent, ui, view) {
|
270
|
+
alert('Drag Event: ' + calEvent.title);
|
271
|
+
alert('Event ID: ' + calEvent.id);
|
272
|
+
alert('Coordinates: ' + jsEvent.pageX + ',' + jsEvent.pageY);
|
273
|
+
alert('View: ' + view.name);
|
274
|
+
// change the border color just for fun
|
275
|
+
$(this).css('border-color', 'red');
|
276
|
+
},
|
277
|
+
viewRender: function(view, element) {
|
278
|
+
$('tr.fc-list-item').each(function() {
|
279
|
+
var duration;
|
280
|
+
var seg = $(this).data('fc-seg');
|
281
|
+
var myevent = seg.footprint.getEventLegacy();
|
282
|
+
if (myevent.end) {
|
283
|
+
var minutes = moment.duration(myevent.end.diff(myevent.start)).asMinutes();
|
284
|
+
var days = Math.floor(minutes/(24*60));
|
285
|
+
minutes -= days * 24 * 60;
|
286
|
+
var hours = Math.floor(minutes/60);
|
287
|
+
minutes -= hours * 60;
|
288
|
+
duration = days + "." + hours + ":" + minutes;
|
289
|
+
} else {
|
290
|
+
duration = "0.00:00";
|
291
|
+
}
|
292
|
+
if (myevent.title == "母の日") {
|
293
|
+
// alert('My Event: ' + myevent.title);
|
294
|
+
// alert('Duration: ' + duration);
|
295
|
+
}
|
296
|
+
var uuid = create_uuid();
|
297
|
+
// alert('Seg: ' + seg);
|
298
|
+
$(this).data('event', {
|
299
|
+
title: myevent.title,
|
300
|
+
allDay: myevent.allDay,
|
301
|
+
start: myevent.start.format("HH:mm"),
|
302
|
+
duration: duration,
|
303
|
+
stick: true,
|
304
|
+
className: myevent.className
|
305
|
+
});
|
306
|
+
});
|
307
|
+
|
308
|
+
$('tr.fc-list-item').draggable({
|
309
|
+
appendTo: "#calendar",
|
310
|
+
cursorAt: {top: 5, left: 5},
|
311
|
+
iframeFix: true,
|
312
|
+
refreshPositions: true,
|
313
|
+
scroll: false,
|
314
|
+
cursor: "move",
|
315
|
+
revert: true, // will cause the event to go back to its
|
316
|
+
revertDuration: 0, // original position after the drag
|
317
|
+
helper: "clone",
|
318
|
+
zIndex: 100,
|
319
|
+
opacity: 0.35,
|
320
|
+
|
321
|
+
start: function(event, ui) {
|
322
|
+
}
|
323
|
+
});
|
324
|
+
if(view.name == 'listMonth') {
|
325
|
+
adjust_height('#stuck-events');
|
326
|
+
}
|
327
|
+
},
|
328
|
+
windowResize: function(view) {
|
329
|
+
if(view.name == 'listMonth') {
|
330
|
+
adjust_height('#stuck-events');
|
331
|
+
}
|
332
|
+
}
|
333
|
+
});
|
334
|
+
|
335
|
+
$('#calendar').fullCalendar({
|
336
|
+
header: {
|
337
|
+
left: 'prev,next today',
|
338
|
+
center: 'title',
|
339
|
+
right: 'month,agendaWeek,agendaDay,listMonth'
|
340
|
+
},
|
341
|
+
firstDay: 1,
|
342
|
+
height: max_height_for('#calendar'),
|
343
|
+
editable: true,
|
344
|
+
eventResourceEditable: true,
|
345
|
+
eventLimit: true,
|
346
|
+
selectable: true,
|
347
|
+
droppable: true,
|
348
|
+
|
349
|
+
// drop: Event ではない draggable からドロップされたとき.
|
350
|
+
// draggable が event として認識できないようなときでも発火する
|
351
|
+
drop: function(date) {
|
352
|
+
// alert("Dropped on " + date.format());
|
353
|
+
},
|
354
|
+
|
355
|
+
// eventReceive: Event ではない draggable からドロップされたとき
|
356
|
+
// draggable が event として認識可能な property を持っている場合だけ発火する
|
357
|
+
eventReceive: function(event) {
|
358
|
+
event.id = create_uuid(); // XXX! これがうまくいかない
|
359
|
+
$('#calendar').fullCalendar('updateEvent', event);
|
360
|
+
// alert("Drop event from ourter world! ID: " + event.id);
|
361
|
+
},
|
362
|
+
|
363
|
+
// eventDrop: 既に登録されている event を変更したとき
|
364
|
+
eventDrop: function(event, delta, revertFunc, jsEvent, ui, view) {
|
365
|
+
// alert("eventDrop:" + event.title);
|
366
|
+
// alert("eventDrop:" + event.id);
|
367
|
+
},
|
368
|
+
|
369
|
+
// return (this.eventInstance || this.eventDef).toLegacy()
|
370
|
+
eventClick: function(calEvent, jsEvent, view) {
|
371
|
+
var yesno = prompt('Do you delete ' + calEvent.title + '?');
|
372
|
+
// alert('Event ID: ' + calEvent.id);
|
373
|
+
if (yesno == "yes"){
|
374
|
+
$('#calendar').fullCalendar('removeEvents', [calEvent.id]);
|
375
|
+
}
|
376
|
+
// alert('This Event: ' + $(this).getEventLegacy().title);
|
377
|
+
//alert('Coordinates: ' + jsEvent.pageX + ',' + jsEvent.pageY);
|
378
|
+
//alert('View: ' + view.name);
|
379
|
+
// change the border color just for fun
|
380
|
+
// $(this).css('border-color', 'red');
|
381
|
+
},
|
382
|
+
|
383
|
+
timezone: '<%= mhc.tzid %>',
|
384
|
+
timeFormat: "HH:mm",
|
385
|
+
allDayText: "",
|
386
|
+
month: 'HH:mm',
|
387
|
+
week: 'HH:mm',
|
388
|
+
day: 'HH:mm',
|
389
|
+
eventSources: json_event_array,
|
390
|
+
// eventSources: [<%= mhc.json_event_array %>],
|
391
|
+
viewRender: function(view, element) {
|
392
|
+
if(view.name == 'month') {
|
393
|
+
adjust_height('#calendar');
|
394
|
+
adjust_height('#stuck-events');
|
395
|
+
}
|
396
|
+
var currentDate = $('#calendar').fullCalendar('getDate');
|
397
|
+
// alert("The current date of the calendar is " + currentDate.format());
|
398
|
+
$('#side-calendar').fullCalendar('gotoDate', currentDate.subtract(1, 'y'));
|
399
|
+
$('#stuck-events').fullCalendar('gotoDate', currentDate.subtract(5, 'y'));
|
400
|
+
},
|
401
|
+
windowResize: function(view) {
|
402
|
+
if(view.name == 'month') {
|
403
|
+
adjust_height('#calendar');
|
404
|
+
adjust_height('#stuck-events');
|
405
|
+
}
|
406
|
+
}
|
407
|
+
}) // $('#calendar')
|
408
|
+
|
409
|
+
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
410
|
+
var target = $(e.target).attr("href") // activated tab
|
411
|
+
adjust_height(target);
|
412
|
+
});
|
413
|
+
|
414
|
+
});
|
415
|
+
</script>
|
416
|
+
</body>
|
417
|
+
</html>
|
data/lib/mhc/version.rb
CHANGED
data/mhc.gemspec
CHANGED
@@ -23,7 +23,8 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
|
26
|
-
spec.add_runtime_dependency "thor", ">=
|
26
|
+
spec.add_runtime_dependency "thor", ">= 1.2.0"
|
27
|
+
spec.add_runtime_dependency "rexml", ">= 3.2.4"
|
27
28
|
spec.add_runtime_dependency "ri_cal", ">= 0.8.8"
|
28
29
|
spec.add_runtime_dependency "tzinfo", ">= 1.2.2"
|
29
30
|
spec.add_runtime_dependency "tzinfo-data", ">= 1.2015.4"
|
@@ -31,14 +31,14 @@ X-SC-Record-Id: AC1B378C-BAFE-4B6E-AF9C-78C2377E8AA4
|
|
31
31
|
X-SC-Subject: 天皇誕生日
|
32
32
|
X-SC-Category: Holiday Japanese
|
33
33
|
X-SC-Cond: 29 Apr
|
34
|
-
X-SC-Duration:
|
34
|
+
X-SC-Duration: 19490429-19880429
|
35
35
|
X-SC-Record-Id: B78EAAEE-9963-4573-9EC7-0879F8940AEE
|
36
36
|
|
37
37
|
X-SC-Subject: みどりの日
|
38
38
|
X-SC-Category: Holiday Japanese
|
39
39
|
X-SC-Cond: 29 Apr
|
40
40
|
X-SC-Duration: 19890429-20060429
|
41
|
-
X-SC-Record-Id:
|
41
|
+
X-SC-Record-Id: 8C05BF80-006C-4FA7-8CF1-5F1C18CB8641
|
42
42
|
|
43
43
|
X-SC-Subject: 昭和の日
|
44
44
|
X-SC-Category: Holiday Japanese
|
@@ -72,14 +72,14 @@ X-SC-Record-Id: 5E03776A-4280-4337-85C0-F1322A3AAE6E
|
|
72
72
|
|
73
73
|
X-SC-Subject: 海の日
|
74
74
|
X-SC-Category: Holiday Japanese
|
75
|
-
X-SC-Day: !20200720 20200723
|
75
|
+
X-SC-Day: !20200720 20200723 !20210719 20210722
|
76
76
|
X-SC-Cond: 3rd Mon Jul
|
77
77
|
X-SC-Duration: 20030701-
|
78
78
|
X-SC-Record-Id: BD4156C2-5546-4146-8297-09CF4210A346
|
79
79
|
|
80
80
|
X-SC-Subject: 山の日
|
81
81
|
X-SC-Category: Holiday Japanese
|
82
|
-
X-SC-Day: 20200810 !20200811
|
82
|
+
X-SC-Day: 20200810 !20200811 20210808 !20210811
|
83
83
|
X-SC-Cond: 11 Aug
|
84
84
|
X-SC-Duration: 20160811-
|
85
85
|
X-SC-Record-Id: FC7C481B-7BF5-4639-B700-3B60CB99E05D
|
@@ -110,7 +110,7 @@ X-SC-Record-Id: C17D9CA5-28AF-4826-AD33-8B75473E5B52
|
|
110
110
|
|
111
111
|
X-SC-Subject: スポーツの日
|
112
112
|
X-SC-Category: Holiday Japanese
|
113
|
-
X-SC-Day: 20200724 !20201012
|
113
|
+
X-SC-Day: 20200724 !20201012 20210723 !20211011
|
114
114
|
X-SC-Cond: 2nd Mon Oct
|
115
115
|
X-SC-Duration: 20200101-
|
116
116
|
X-SC-Record-Id: F10B2B18-B0FB-477E-A7D3-A067B31F4736
|
@@ -184,13 +184,14 @@ X-SC-Day: 19730430 19730924 19740506 19740916 19741104 19751124
|
|
184
184
|
20081124 20090506 20100322 20120102 20120430 20121224 20130506
|
185
185
|
20131104 20140506 20141124 20150506 20160321 20170102 20180212
|
186
186
|
20180430 20180924 20181224 20190506 20190812 20191104 20200224 20200506
|
187
|
+
20210809
|
187
188
|
20230102 20240212 20240506 20240812 20240923 20241104 20250224 20250506
|
188
|
-
20251124 20260506 20270322 20290212 20290430 20290924
|
189
|
+
20251124 20260506 20270322 20290212 20290430 20290924
|
189
190
|
20300506 20300812 20301104 20310224 20310506 20311124 20330321 20340102
|
190
|
-
20350212 20350430 20350924
|
191
|
-
20400102 20400430
|
191
|
+
20350212 20350430 20350924 20360506 20361124 20370506
|
192
|
+
20400102 20400430 20410506 20410812 20411104 20420224 20420506
|
192
193
|
20421124 20430506 20440321 20450102 20460212 20460430 20460924
|
193
|
-
|
194
|
+
20470506 20470812 20471104 20480224 20480506 20500321
|
194
195
|
X-SC-Category: Holiday Japanese
|
195
196
|
X-SC-Record-Id: E2D696DF-EB49-44D7-B06C-812B10BD99A2
|
196
197
|
|