-
Notifications
You must be signed in to change notification settings - Fork 28
/
Copy pathjquery.gcal_flow.coffee
279 lines (258 loc) · 10.1 KB
/
jquery.gcal_flow.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
$ = jQuery
if window? and window._gCalFlow_debug? and console?
log = console
log.debug ?= log.log
else
log = {}
log.error = log.warn = log.log = log.info = log.debug = ->
pad_zero = (num, size = 2) ->
if 10 * (size-1) <= num then return num
ret = ""
for i in [1..(size-"#{num}".length)]
ret = ret.concat "0"
ret.concat num
class gCalFlow
target: null
template: $("""<div class="gCalFlow">
<div class="gcf-header-block">
<div class="gcf-title-block">
<span class="gcf-title"></span>
</div>
</div>
<div class="gcf-item-container-block">
<div class="gcf-item-block">
<div class="gcf-item-header-block">
<div class="gcf-item-date-block">
[<span class="gcf-item-daterange"></span>]
</div>
<div class="gcf-item-title-block">
<strong class="gcf-item-title"></strong>
</div>
</div>
<div class="gcf-item-body-block">
<div class="gcf-item-description">
</div>
<div class="gcf-item-location">
</div>
</div>
</div>
</div>
<div class="gcf-last-update-block">
LastUpdate: <span class="gcf-last-update"></span>
</div>
</div>""")
opts: {
maxitem: 15
calid: null
mode: 'upcoming'
feed_url: null
auto_scroll: true
scroll_interval: 10 * 1000
link_title: true
link_item_title: true
link_item_description: false
link_target: '_blank'
item_description_in_html: false
callback: null
no_items_html: ''
globalize_culture: navigator? and (navigator.browserLanguage or navigator.language or navigator.userLanguage)
globalize_fmt_datetime: 'f'
globalize_fmt_date: 'D'
globalize_fmt_time: 't'
globalize_fmt_monthday: 'M'
date_formatter: (d, allday_p) ->
if Globalize? and Globalize.format?
if allday_p
fmtstr = @globalize_fmt_date
else
fmtstr = @globalize_fmt_datetime
return Globalize.format d, fmtstr
else
if allday_p
return "#{d.getFullYear()}-#{pad_zero d.getMonth()+1}-#{pad_zero d.getDate()}"
else
return "#{d.getFullYear()}-#{pad_zero d.getMonth()+1}-#{pad_zero d.getDate()} #{pad_zero d.getHours()}:#{pad_zero d.getMinutes()}"
daterange_formatter: (sd, ed, allday_p) ->
ret = @date_formatter sd, allday_p
ed = new Date(ed.getTime() - 86400 * 1000) if allday_p
endstr = ''
if sd.getDate() != ed.getDate() or sd.getMonth() != ed.getMonth()
if Globalize? and Globalize.format?
endstr += Globalize.format ed, @globalize_fmt_monthday
else
endstr += "#{pad_zero ed.getMonth()+1}-#{pad_zero ed.getDate()}"
if not allday_p and (sd.getHours() != ed.getHours() or sd.getMinutes() != ed.getMinutes())
if Globalize? and Globalize.format?
endstr += Globalize.format ed, @globalize_fmt_time
else
endstr += " #{pad_zero ed.getHours()}:#{pad_zero ed.getMinutes()}"
ret += " - #{endstr}" if endstr
return ret
}
constructor: (target, opts) ->
@target = target
target.addClass 'gCalFlow'
if target.children().size() > 0
log.debug "Target node has children, use target element as template."
@template = target
@update_opts opts
update_opts: (new_opts) ->
log.debug "update_opts was called"
log.debug "old options:", @opts
@opts = $.extend {}, @opts, new_opts
log.debug "new options:", @opts
gcal_url: ->
if !@opts.calid && !@opts.feed_url
log.error "Option calid and feed_url are missing. Abort URL generation"
@target.text "Error: You need to set 'calid' or 'feed_url' option."
throw "gCalFlow: calid and feed_url missing"
if @opts.feed_url
@opts.feed_url
else if @opts.mode == 'updates'
"https://www.google.com/calendar/feeds/#{@opts.calid}/public/full?alt=json-in-script&max-results=#{@opts.maxitem}&orderby=lastmodified&sortorder=descending"
else
"https://www.google.com/calendar/feeds/#{@opts.calid}/public/full?alt=json-in-script&max-results=#{@opts.maxitem}&orderby=starttime&futureevents=true&sortorder=ascending&singleevents=true"
fetch: ->
log.debug "Starting ajax call for #{@gcal_url()}"
success_handler = (data) =>
log.debug "Ajax call success. Response data:", data
@render_data data, @
$.ajax {
success: success_handler
dataType: "jsonp"
url: @gcal_url()
}
parse_date: (dstr) ->
date = new Date dstr
unless isNaN date.getTime()
log.debug date
return date
else
if m = dstr.match /^(\d{4})-(\d{2})-(\d{2})$/
return new Date parseInt(m[1], 10), parseInt(m[2], 10) - 1, parseInt(m[3], 10), 0, 0, 0
offset = (new Date()).getTimezoneOffset() * 60 * 1000
year = mon = day = null
hour = min = sec = 0
if m = dstr.match /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|([+-])(\d{2}):(\d{2}))$/
year = parseInt m[1], 10
mon = parseInt m[2], 10
day = parseInt m[3], 10
hour = parseInt m[4], 10
min = parseInt m[5], 10
sec = parseInt m[6], 10
if m[7] != "Z"
offset += (if m[8] is "+" then 1 else -1) * (parseInt(m[9], 10) * 60 + parseInt(m[10], 10)) * 1000 * 60
else
log.warn "Time parse error! Unknown time pattern: #{dstr}"
return new Date 1970, 1, 1, 0, 0, 0
log.debug "time parse (gap to local): #{offset}"
ret = new Date(new Date(year, mon - 1, day, hour, min, sec).getTime() - offset)
log.debug "time parse: #{dstr} -> ", ret
return ret
render_data: (data) ->
log.debug "start rendering for data:", data
feed = data.feed
t = @template.clone()
titlelink = @opts.titlelink ? "http://www.google.com/calendar/embed?src=#{@opts.calid}"
if @opts.link_title
t.find('.gcf-title').html $("<a />").attr({target: @opts.link_target, href: titlelink}).text feed.title.$t
else
t.find('.gcf-title').text feed.title.$t
t.find('.gcf-link').attr {target: @opts.link_target, href: titlelink}
t.find('.gcf-last-update').html @opts.date_formatter @parse_date feed.updated.$t
it = t.find('.gcf-item-block')
it.detach()
it = $(it[0])
log.debug "item block template:", it
items = $()
log.debug "render entries:", feed.entry
if @opts.item_description_as_html
desc_body_method = 'html'
else
desc_body_method = 'text'
if feed.entry? and feed.entry.length > 0
for ent in feed.entry[0..@opts.maxitem]
log.debug "formatting entry:", ent
ci = it.clone()
if ent.gd$when
st = ent.gd$when[0].startTime
sd = @parse_date(st)
stf = @opts.date_formatter sd, st.indexOf(':') < 0
ci.find('.gcf-item-date').html stf
ci.find('.gcf-item-start-date').html stf
et = ent.gd$when[0].endTime
ed = @parse_date(et)
etf = @opts.date_formatter ed, et.indexOf(':') < 0
ci.find('.gcf-item-end-date').html etf
ci.find('.gcf-item-daterange').html @opts.daterange_formatter sd, ed, st.indexOf(':') < 0
ci.find('.gcf-item-update-date').html @opts.date_formatter @parse_date(ent.updated.$t), false
link = $('<a />').attr {target: @opts.link_target, href: ent.link[0].href}
if @opts.link_item_title
ci.find('.gcf-item-title').html link.clone().text ent.title.$t
else
ci.find('.gcf-item-title').text ent.title.$t
if @opts.link_item_description
ci.find('.gcf-item-description').html link.clone()[desc_body_method] ent.content.$t
else
ci.find('.gcf-item-description')[desc_body_method] ent.content.$t
ci.find('.gcf-item-location').text ent.gd$where[0].valueString
ci.find('.gcf-item-link').attr {href: ent.link[0].href}
log.debug "formatted item entry:", ci[0]
items.push ci[0]
else
items = $('<div class=".gcf-no-items"></div>').html @opts.no_items_html
log.debug "formatted item entry array:", items
ic = t.find('.gcf-item-container-block')
log.debug "item container element:", ic
ic.html items
@target.html t.html()
@bind_scroll()
@opts.callback.apply(@target) if @opts.callback
bind_scroll: ->
scroll_container = @target.find('.gcf-item-container-block')
scroll_children = scroll_container.find(".gcf-item-block")
log.debug "scroll container:", scroll_container
if not @opts.auto_scroll or scroll_container.size() < 1 or scroll_children.size() < 2
return
state = {idx: 0}
scroller = ->
log.debug "current scroll position:", scroll_container.scrollTop()
log.debug "scroll capacity:", scroll_container[0].scrollHeight - scroll_container[0].clientHeight
if typeof scroll_children[state.idx] is 'undefined' or scroll_container.scrollTop() >= scroll_container[0].scrollHeight - scroll_container[0].clientHeight
log.debug "scroll to top"
state.idx = 0
scroll_container.animate {scrollTop: scroll_children[0].offsetTop}
else
scroll_to = scroll_children[state.idx].offsetTop
log.debug "scroll to #{scroll_to}px"
scroll_container.animate {scrollTop: scroll_to}
state.idx += 1
scroll_timer = setInterval scroller, @opts.scroll_interval
methods =
init: (opts = {}) ->
data = @data 'gCalFlow'
if !data then @data 'gCalFlow', { target: @, obj: new gCalFlow(@, opts) }
destroy: ->
data = @data 'gCalFlow'
data.obj.target = null
$(window).unbind '.gCalFlow'
data.gCalFlow.remove()
@removeData 'gCalFlow'
render: ->
if Globalize? and Globalize.culture?
Globalize.culture @data('gCalFlow').obj.opts.globalize_culture
@data('gCalFlow').obj.fetch()
$.fn.gCalFlow = (method) ->
orig_args = arguments
if typeof method == 'object' || !method
@each ->
methods.init.apply $(@), orig_args
methods.render.apply $(@), orig_args
else if methods[method]
@each ->
methods[method].apply $(@), Array.prototype.slice.call(orig_args, 1)
else if method == 'version'
"1.2.4"
else
$.error "Method #{method} does not exist on jQuery.gCalFlow"
# vim: set sts=2 sw=2 expandtab: