Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2397657
libcalendaring.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
libcalendaring.js
View Options
/**
* Basic Javascript utilities for calendar-related plugins
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
function
rcube_libcalendaring
(
settings
)
{
// member vars
this
.
settings
=
settings
;
this
.
alarm_ids
=
[];
this
.
alarm_dialog
=
null
;
this
.
snooze_popup
=
null
;
this
.
dismiss_link
=
null
;
// private vars
var
me
=
this
;
var
gmt_offset
=
(
new
Date
().
getTimezoneOffset
()
/
-
60
)
-
(
settings
.
timezone
||
0
)
-
(
settings
.
dst
||
0
);
var
client_timezone
=
new
Date
().
getTimezoneOffset
();
// general datepicker settings
var
datepicker_settings
=
{
// translate from fullcalendar format to datepicker format
dateFormat
:
settings
.
date_format
.
replace
(
/M/g
,
'm'
).
replace
(
/mmmmm/
,
'MM'
).
replace
(
/mmm/
,
'M'
).
replace
(
/dddd/
,
'DD'
).
replace
(
/ddd/
,
'D'
).
replace
(
/yy/g
,
'y'
),
firstDay
:
settings
.
first_day
,
dayNamesMin
:
settings
.
days_short
,
monthNames
:
settings
.
months
,
monthNamesShort
:
settings
.
months
,
changeMonth
:
false
,
showOtherMonths
:
true
,
selectOtherMonths
:
true
};
/**
* Quote html entities
*/
var
Q
=
this
.
quote_html
=
function
(
str
)
{
return
String
(
str
).
replace
(
/</g
,
'<'
).
replace
(
/>/g
,
'>'
).
replace
(
/"/g
,
'"'
);
};
/**
* Create a nice human-readable string for the date/time range
*/
this
.
event_date_text
=
function
(
event
)
{
if
(
!
event
.
start
)
return
''
;
if
(
!
event
.
end
)
event
.
end
=
event
.
start
;
var
fromto
,
duration
=
event
.
end
.
getTime
()
/
1000
-
event
.
start
.
getTime
()
/
1000
;
if
(
event
.
allDay
)
{
fromto
=
this
.
format_datetime
(
event
.
start
,
1
)
+
(
duration
>
86400
||
event
.
start
.
getDay
()
!=
event
.
end
.
getDay
()
?
' — '
+
this
.
format_datetime
(
event
.
end
,
1
)
:
''
);
}
else
if
(
duration
<
86400
&&
event
.
start
.
getDay
()
==
event
.
end
.
getDay
())
{
fromto
=
this
.
format_datetime
(
event
.
start
,
0
)
+
(
duration
>
0
?
' — '
+
this
.
format_datetime
(
event
.
end
,
2
)
:
''
);
}
else
{
fromto
=
this
.
format_datetime
(
event
.
start
,
0
)
+
(
duration
>
0
?
' — '
+
this
.
format_datetime
(
event
.
end
,
0
)
:
''
);
}
return
fromto
;
};
/**
* From time and date strings to a real date object
*/
this
.
parse_datetime
=
function
(
time
,
date
)
{
// we use the utility function from datepicker to parse dates
var
date
=
date
?
$
.
datepicker
.
parseDate
(
datepicker_settings
.
dateFormat
,
date
,
datepicker_settings
)
:
new
Date
();
var
time_arr
=
time
.
replace
(
/\s*[ap][.m]*/i
,
''
).
replace
(
/0([0-9])/g
,
'$1'
).
split
(
/[:.]/
);
if
(
!
isNaN
(
time_arr
[
0
]))
{
date
.
setHours
(
time_arr
[
0
]);
if
(
time
.
match
(
/p[.m]*/i
)
&&
date
.
getHours
()
<
12
)
date
.
setHours
(
parseInt
(
time_arr
[
0
])
+
12
);
else
if
(
time
.
match
(
/a[.m]*/i
)
&&
date
.
getHours
()
==
12
)
date
.
setHours
(
0
);
}
if
(
!
isNaN
(
time_arr
[
1
]))
date
.
setMinutes
(
time_arr
[
1
]);
return
date
;
}
/**
* Convert an ISO 8601 formatted date string from the server into a Date object.
* Timezone information will be ignored, the server already provides dates in user's timezone.
*/
function
parseISO8601
(
s
)
{
// force d to be on check's YMD, for daylight savings purposes
var
fixDate
=
function
(
d
,
check
)
{
if
(
+
d
)
{
// prevent infinite looping on invalid dates
while
(
d
.
getDate
()
!=
check
.
getDate
())
{
d
.
setTime
(
+
d
+
(
d
<
check
?
1
:
-
1
)
*
3600000
);
}
}
}
// derived from http://delete.me.uk/2005/03/iso8601.html
var
m
=
s
&&
s
.
match
(
/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/
);
if
(
!
m
)
{
return
null
;
}
var
date
=
new
Date
(
m
[
1
],
0
,
1
),
check
=
new
Date
(
m
[
1
],
0
,
1
,
9
,
0
);
if
(
m
[
3
])
{
date
.
setMonth
(
m
[
3
]
-
1
);
check
.
setMonth
(
m
[
3
]
-
1
);
}
if
(
m
[
5
])
{
date
.
setDate
(
m
[
5
]);
check
.
setDate
(
m
[
5
]);
}
fixDate
(
date
,
check
);
if
(
m
[
7
])
{
date
.
setHours
(
m
[
7
]);
}
if
(
m
[
8
])
{
date
.
setMinutes
(
m
[
8
]);
}
if
(
m
[
10
])
{
date
.
setSeconds
(
m
[
10
]);
}
if
(
m
[
12
])
{
date
.
setMilliseconds
(
Number
(
"0."
+
m
[
12
])
*
1000
);
}
fixDate
(
date
,
check
);
return
date
;
}
/**
* Format the given date object according to user's prefs
*/
this
.
format_datetime
=
function
(
date
,
mode
)
{
var
res
=
''
;
if
(
!
mode
||
mode
==
1
)
res
+=
$
.
datepicker
.
formatDate
(
datepicker_settings
.
dateFormat
,
date
,
datepicker_settings
);
if
(
!
mode
)
res
+=
' '
;
if
(
!
mode
||
mode
==
2
)
res
+=
this
.
format_time
(
date
);
return
res
;
}
/**
* Clone from fullcalendar.js
*/
this
.
format_time
=
function
(
date
)
{
var
zeroPad
=
function
(
n
)
{
return
(
n
<
10
?
'0'
:
''
)
+
n
;
}
var
formatters
=
{
s
:
function
(
d
)
{
return
d
.
getSeconds
()
},
ss
:
function
(
d
)
{
return
zeroPad
(
d
.
getSeconds
())
},
m
:
function
(
d
)
{
return
d
.
getMinutes
()
},
mm
:
function
(
d
)
{
return
zeroPad
(
d
.
getMinutes
())
},
h
:
function
(
d
)
{
return
d
.
getHours
()
%
12
||
12
},
hh
:
function
(
d
)
{
return
zeroPad
(
d
.
getHours
()
%
12
||
12
)
},
H
:
function
(
d
)
{
return
d
.
getHours
()
},
HH
:
function
(
d
)
{
return
zeroPad
(
d
.
getHours
())
},
t
:
function
(
d
)
{
return
d
.
getHours
()
<
12
?
'a'
:
'p'
},
tt
:
function
(
d
)
{
return
d
.
getHours
()
<
12
?
'am'
:
'pm'
},
T
:
function
(
d
)
{
return
d
.
getHours
()
<
12
?
'A'
:
'P'
},
TT
:
function
(
d
)
{
return
d
.
getHours
()
<
12
?
'AM'
:
'PM'
}
};
var
i
,
i2
,
c
,
formatter
,
res
=
''
,
format
=
settings
[
'time_format'
];
for
(
i
=
0
;
i
<
format
.
length
;
i
++
)
{
c
=
format
.
charAt
(
i
);
for
(
i2
=
Math
.
min
(
i
+
2
,
format
.
length
);
i2
>
i
;
i2
--
)
{
if
(
formatter
=
formatters
[
format
.
substring
(
i
,
i2
)])
{
res
+=
formatter
(
date
);
i
=
i2
-
1
;
break
;
}
}
if
(
i2
==
i
)
{
res
+=
c
;
}
}
return
res
;
}
/**
* Convert the given Date object into a unix timestamp respecting browser's and user's timezone settings
*/
this
.
date2unixtime
=
function
(
date
)
{
var
dst_offset
=
(
client_timezone
-
date
.
getTimezoneOffset
())
*
60
;
// adjust DST offset
return
Math
.
round
(
date
.
getTime
()
/
1000
+
gmt_offset
*
3600
+
dst_offset
);
}
/**
* Turn a unix timestamp value into a Date object
*/
this
.
fromunixtime
=
function
(
ts
)
{
ts
-=
gmt_offset
*
3600
;
var
date
=
new
Date
(
ts
*
1000
),
dst_offset
=
(
client_timezone
-
date
.
getTimezoneOffset
())
*
60
;
if
(
dst_offset
)
// adjust DST offset
date
.
setTime
((
ts
+
3600
)
*
1000
);
return
date
;
}
/**
* Simple plaintext to HTML converter, makig URLs clickable
*/
this
.
text2html
=
function
(
str
,
maxlen
,
maxlines
)
{
var
html
=
Q
(
String
(
str
));
// limit visible text length
if
(
maxlen
)
{
var
morelink
=
' <a href="#more" onclick="$(this).hide().next().show();return false" class="morelink">'
+
rcmail
.
gettext
(
'showmore'
,
'libcalendaring'
)
+
'</a><span style="display:none">'
,
lines
=
html
.
split
(
/\r?\n/
),
words
,
out
=
''
,
len
=
0
;
for
(
var
i
=
0
;
i
<
lines
.
length
;
i
++
)
{
len
+=
lines
[
i
].
length
;
if
(
maxlines
&&
i
==
maxlines
-
1
)
{
out
+=
lines
[
i
]
+
'\n'
+
morelink
;
maxlen
=
html
.
length
*
2
;
}
else
if
(
len
>
maxlen
)
{
len
=
out
.
length
;
words
=
lines
[
i
].
split
(
' '
);
for
(
var
j
=
0
;
j
<
words
.
length
;
j
++
)
{
len
+=
words
[
j
].
length
+
1
;
out
+=
words
[
j
]
+
' '
;
if
(
len
>
maxlen
)
{
out
+=
morelink
;
maxlen
=
html
.
length
*
2
;
}
}
out
+=
'\n'
;
}
else
out
+=
lines
[
i
]
+
'\n'
;
}
if
(
maxlen
>
str
.
length
)
out
+=
'</span>'
;
html
=
out
;
}
// simple link parser (similar to rcube_string_replacer class in PHP)
var
utf_domain
=
'[^?&@"\'/\\(\\)\\s\\r\\t\\n]+\\.([^\x00-\x2f\x3b-\x40\x5b-\x60\x7b-\x7f]{2,}|xn--[a-z0-9]{2,})'
;
var
url1
=
'.:;,'
,
url2
=
'a-z0-9%=#@+?&/_~\\[\\]-'
;
var
link_pattern
=
new
RegExp
(
'([hf]t+ps?://)('
+
utf_domain
+
'(['
+
url1
+
']?['
+
url2
+
']+)*)?'
,
'ig'
);
var
mailto_pattern
=
new
RegExp
(
'([^\\s\\n\\(\\);]+@'
+
utf_domain
+
')'
,
'ig'
);
return
html
.
replace
(
link_pattern
,
'<a href="$1$2" target="_blank">$1$2</a>'
)
.
replace
(
mailto_pattern
,
'<a href="mailto:$1">$1</a>'
)
.
replace
(
/(mailto:)([^"]+)"/g
,
'$1$2" onclick="rcmail.command(\'compose\', \'$2\');return false"'
)
.
replace
(
/\n/g
,
"<br/>"
);
};
this
.
init_alarms_edit
=
function
(
prefix
)
{
// register events on alarm fields
$
(
prefix
+
' select.edit-alarm-type'
).
change
(
function
(){
$
(
this
).
parent
().
find
(
'span.edit-alarm-values'
)[(
this
.
selectedIndex
>
0
?
'show'
:
'hide'
)]();
});
$
(
prefix
+
' select.edit-alarm-offset'
).
change
(
function
(){
var
mode
=
$
(
this
).
val
()
==
'@'
?
'show'
:
'hide'
;
$
(
this
).
parent
().
find
(
'.edit-alarm-date, .edit-alarm-time'
)[
mode
]();
$
(
this
).
parent
().
find
(
'.edit-alarm-value'
).
prop
(
'disabled'
,
mode
==
'show'
);
});
$
(
prefix
+
' .edit-alarm-date'
).
datepicker
(
datepicker_settings
);
}
/***** Alarms handling *****/
/**
* Display a notification for the given pending alarms
*/
this
.
display_alarms
=
function
(
alarms
)
{
// clear old alert first
if
(
this
.
alarm_dialog
)
this
.
alarm_dialog
.
dialog
(
'destroy'
);
this
.
alarm_dialog
=
$
(
'<div>'
).
attr
(
'id'
,
'alarm-display'
);
var
actions
,
adismiss
,
asnooze
,
alarm
,
html
,
event_ids
=
[];
for
(
var
i
=
0
;
i
<
alarms
.
length
;
i
++
)
{
alarm
=
alarms
[
i
];
alarm
.
start
=
parseISO8601
(
alarm
.
start
);
alarm
.
end
=
parseISO8601
(
alarm
.
end
);
event_ids
.
push
(
alarm
.
id
);
html
=
'<h3 class="event-title">'
+
Q
(
alarm
.
title
)
+
'</h3>'
;
html
+=
'<div class="event-section">'
+
Q
(
alarm
.
location
||
''
)
+
'</div>'
;
html
+=
'<div class="event-section">'
+
Q
(
this
.
event_date_text
(
alarm
))
+
'</div>'
;
adismiss
=
$
(
'<a href="#" class="alarm-action-dismiss"></a>'
).
html
(
rcmail
.
gettext
(
'dismiss'
,
'libcalendaring'
)).
click
(
function
(){
me
.
dismiss_link
=
$
(
this
);
me
.
dismiss_alarm
(
me
.
dismiss_link
.
data
(
'id'
),
0
);
});
asnooze
=
$
(
'<a href="#" class="alarm-action-snooze"></a>'
).
html
(
rcmail
.
gettext
(
'snooze'
,
'libcalendaring'
)).
click
(
function
(
e
){
me
.
snooze_dropdown
(
$
(
this
));
e
.
stopPropagation
();
return
false
;
});
actions
=
$
(
'<div>'
).
addClass
(
'alarm-actions'
).
append
(
adismiss
.
data
(
'id'
,
alarm
.
id
)).
append
(
asnooze
.
data
(
'id'
,
alarm
.
id
));
$
(
'<div>'
).
addClass
(
'alarm-item'
).
html
(
html
).
append
(
actions
).
appendTo
(
this
.
alarm_dialog
);
}
var
buttons
=
{};
buttons
[
rcmail
.
gettext
(
'dismissall'
,
'libcalendaring'
)]
=
function
()
{
// submit dismissed event_ids to server
me
.
dismiss_alarm
(
me
.
alarm_ids
.
join
(
','
),
0
);
$
(
this
).
dialog
(
'close'
);
};
this
.
alarm_dialog
.
appendTo
(
document
.
body
).
dialog
({
modal
:
false
,
resizable
:
true
,
closeOnEscape
:
false
,
dialogClass
:
'alarm'
,
title
:
'<span class="ui-icon ui-icon-alarms" style="float:left; margin:0 4px 0 0"></span>'
+
rcmail
.
gettext
(
'alarmtitle'
,
'libcalendaring'
),
buttons
:
buttons
,
close
:
function
()
{
$
(
'#alarm-snooze-dropdown'
).
hide
();
$
(
this
).
dialog
(
'destroy'
).
remove
();
me
.
alarm_dialog
=
null
;
me
.
alarm_ids
=
null
;
},
drag
:
function
(
event
,
ui
)
{
$
(
'#alarm-snooze-dropdown'
).
hide
();
}
});
this
.
alarm_ids
=
event_ids
;
};
/**
* Show a drop-down menu with a selection of snooze times
*/
this
.
snooze_dropdown
=
function
(
link
)
{
if
(
!
this
.
snooze_popup
)
{
this
.
snooze_popup
=
$
(
'#alarm-snooze-dropdown'
);
// create popup if not found
if
(
!
this
.
snooze_popup
.
length
)
{
this
.
snooze_popup
=
$
(
'<div>'
).
attr
(
'id'
,
'alarm-snooze-dropdown'
).
addClass
(
'popupmenu'
).
appendTo
(
document
.
body
);
this
.
snooze_popup
.
html
(
rcmail
.
env
.
snooze_select
)
}
$
(
'#alarm-snooze-dropdown a'
).
click
(
function
(
e
){
var
time
=
String
(
this
.
href
).
replace
(
/.+#/
,
''
);
me
.
dismiss_alarm
(
$
(
'#alarm-snooze-dropdown'
).
data
(
'id'
),
time
);
return
false
;
});
}
// hide visible popup
if
(
this
.
snooze_popup
.
is
(
':visible'
)
&&
this
.
snooze_popup
.
data
(
'id'
)
==
link
.
data
(
'id'
))
{
this
.
snooze_popup
.
hide
();
this
.
dismiss_link
=
null
;
}
else
{
// open popup below the clicked link
var
pos
=
link
.
offset
();
pos
.
top
+=
link
.
height
()
+
2
;
this
.
snooze_popup
.
data
(
'id'
,
link
.
data
(
'id'
)).
css
({
top
:
Math
.
floor
(
pos
.
top
)
+
'px'
,
left
:
Math
.
floor
(
pos
.
left
)
+
'px'
}).
show
();
this
.
dismiss_link
=
link
;
}
};
/**
* Dismiss or snooze alarms for the given event
*/
this
.
dismiss_alarm
=
function
(
id
,
snooze
)
{
$
(
'#alarm-snooze-dropdown'
).
hide
();
rcmail
.
http_post
(
'utils/plugin.alarms'
,
{
action
:
'dismiss'
,
data
:
{
id
:
id
,
snooze
:
snooze
}
});
// remove dismissed alarm from list
if
(
this
.
dismiss_link
)
{
this
.
dismiss_link
.
closest
(
'div.alarm-item'
).
hide
();
var
new_ids
=
jQuery
.
grep
(
this
.
alarm_ids
,
function
(
v
){
return
v
!=
id
;
});
if
(
new_ids
.
length
)
this
.
alarm_ids
=
new_ids
;
else
this
.
alarm_dialog
.
dialog
(
'close'
);
}
this
.
dismiss_link
=
null
;
};
}
// extend jQuery
(
function
(
$
){
$
.
fn
.
serializeJSON
=
function
(){
var
json
=
{};
jQuery
.
map
(
$
(
this
).
serializeArray
(),
function
(
n
,
i
)
{
json
[
n
[
'name'
]]
=
n
[
'value'
];
});
return
json
;
};
})(
jQuery
);
/* libcalendaring plugin initialization */
window
.
rcmail
&&
rcmail
.
addEventListener
(
'init'
,
function
(
evt
)
{
var
libcal
=
new
rcube_libcalendaring
(
rcmail
.
env
.
libcal_settings
);
rcmail
.
addEventListener
(
'plugin.display_alarms'
,
function
(
alarms
){
libcal
.
display_alarms
(
alarms
);
});
});
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Nov 3, 2:43 PM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
359080
Default Alt Text
libcalendaring.js (15 KB)
Attached To
Mode
R14 roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline
Log In to Comment