1
0

refactor: refactor for modern layout

- split frontend and backend.
- update backend with modern Python dev strategies.
This commit is contained in:
2026-04-28 13:17:54 +08:00
parent 8e72e75a15
commit a0e3385670
65 changed files with 479 additions and 139 deletions

483
frontend/static/js/api.js Normal file
View File

@@ -0,0 +1,483 @@
// the api use bool to return status: fail: return false, true: return data(including true and false)
// the api use other type to return data: fail: return undefined, true: return data(if the returned value have change be null, return undefined instaed).
// var cached_salt = undefined
/*
function ccn_api_common_salt(_username) {
// true or false
// gotten salt store in cached_salt.
var gotten_data = undefined;
$.ajax({
url: '/api/common/salt',
type: "POST",
async: false,
data: {
username: _username
},
success: function (data) {
gotten_data = data;
}
});
if (IsResponseOK(gotten_data)) {
cached_salt = gotten_data['data'];
return true;
} else return false;
}
function ccn_api_common_login(_username, password) {
// return true or false, token is managed by this js file self.
// if cached_salt is undefined, return false directly
if (typeof(cached_salt) == undefined) return false;
var gotten_data = undefined;
$.ajax({
url: '/api/common/login',
type: "POST",
async: false,
data: {
username: _username,
password: ComputPasswordWithSalt(password, cached_salt)
},
success: function (data) {
gotten_data = data;
}
});
if (IsResponseOK(gotten_data) && gotten_data['data'] != '') {
ccn_localstorageAssist_SetApiToken(gotten_data['data']);
cached_salt = undefined;
return true;
} else return false;
}
*/
// ============================================ template
// all api can be implemented by these 2 function, except 3 token related func.
// so all api func should use these 2 func except 3 token process api.
function ccn_api_dataTemplate(_url, _data) {
// return data or undefined
var gotten_data = undefined;
$.ajax({
url: _url,
type: "POST",
async: false,
data: _data,
success: function (data) {
gotten_data = data;
}
});
if (IsResponseOK(gotten_data) && !(gotten_data['data'] === null)) return gotten_data['data'];
else return undefined;
}
function ccn_api_boolTemplate(_url, _data) {
// return true or false
var gotten_data = undefined;
$.ajax({
url: _url,
type: "POST",
async: false,
data: _data,
success: function (data) {
gotten_data = data;
}
});
return (IsResponseOK(gotten_data) && gotten_data['data']);
}
// deserialize & serialize calendar description function
function ccn_api_serializeDescription(_description, _color) {
var sobj = {
description: _description,
color: _color
}
return JSON.stringify(sobj);
}
function ccn_api_deserializeDescription(strl) {
try {
return $.parseJSON(strl);
} catch(err) {
return {
description: "",
color: DefaultColor
};
}
}
// ====================================================== common
function ccn_api_common_webLogin(_username, _password) {
var gotten_data = undefined;
$.ajax({
url: '/api/common/webLogin',
type: "POST",
async: false,
data: {
username: _username,
password: _password
},
success: function (data) {
gotten_data = data;
}
});
if (IsResponseOK(gotten_data)) {
ccn_localstorageAssist_SetApiToken(gotten_data['data']);
return true;
} else return false;
}
function ccn_api_common_logout() {
// return true or false
var gotten_data = undefined;
$.ajax({
url: '/api/common/logout',
type: "POST",
async: false,
data: {
token: ccn_localstorageAssist_GetApiToken()
},
success: function (data) {
gotten_data = data;
}
});
if (IsResponseOK(gotten_data) && gotten_data['data']) {
ccn_localstorageAssist_SetApiToken('');
return true;
} return false;
}
function ccn_api_common_tokenValid() {
// get from local database first, then judge it via post
// return true or false
var gotten_token = ccn_localstorageAssist_GetApiToken();
if (gotten_token == '') return false;
var gotten_data = undefined;
$.ajax({
url: '/api/common/tokenValid',
type: "POST",
async: false,
data: {
token: ccn_localstorageAssist_GetApiToken()
},
success: function (data) {
gotten_data = data;
}
});
if (IsResponseOK(gotten_data) && gotten_data['data']) return true;
else {
ccn_localstorageAssist_SetApiToken('');
return false;
}
}
// ====================================================== calendar
function ccn_api_calendar_getFull(_startDateTime, _endDateTime) {
return ccn_api_dataTemplate(
'/api/calendar/getFull',
{
token: ccn_localstorageAssist_GetApiToken(),
startDateTime: _startDateTime,
endDateTime: _endDateTime
}
);
}
function ccn_api_calendar_getDetail(_uuid) {
return ccn_api_dataTemplate(
'/api/calendar/getDetail',
{
token: ccn_localstorageAssist_GetApiToken(),
uuid: _uuid
}
);
}
function ccn_api_calendar_update(_uuid, _belongTo, _title, _description, _eventDateTimeStart, _eventDateTimeEnd, _loopRules, _timezoneOffset, _lastChange) {
var data = {};
if (typeof(_belongTo) != 'undefined')
data.belongTo = _belongTo;
if (typeof(_title) != 'undefined')
data.title = _title;
if (typeof(_description) != 'undefined')
data.description = _description;
if (typeof(_eventDateTimeStart) != 'undefined')
data.eventDateTimeStart = _eventDateTimeStart;
if (typeof(_eventDateTimeEnd) != 'undefined')
data.eventDateTimeEnd = _eventDateTimeEnd;
if (typeof(_loopRules) != 'undefined')
data.loopRules = _loopRules;
if (typeof(_timezoneOffset) != 'undefined')
data.timezoneOffset = _timezoneOffset;
data.token = ccn_localstorageAssist_GetApiToken();
data.uuid = _uuid;
data.lastChange = _lastChange;
return ccn_api_dataTemplate(
'/api/calendar/update',
data
);
}
function ccn_api_calendar_add(_belongTo, _title, _description, _eventDateTimeStart, _eventDateTimeEnd, _loopRules, _timezoneOffset) {
return ccn_api_dataTemplate(
'/api/calendar/add',
{
token: ccn_localstorageAssist_GetApiToken(),
belongTo: _belongTo,
title: _title,
description: _description,
eventDateTimeStart: _eventDateTimeStart,
eventDateTimeEnd: _eventDateTimeEnd,
loopRules: _loopRules,
timezoneOffset: _timezoneOffset
}
);
}
function ccn_api_calendar_delete(_uuid, _lastChange) {
return ccn_api_boolTemplate(
'/api/calendar/delete',
{
token: ccn_localstorageAssist_GetApiToken(),
uuid: _uuid,
lastChange: _lastChange
}
);
}
// ====================================================== collection
function ccn_api_collection_getFullOwn() {
return ccn_api_dataTemplate(
'/api/collection/getFullOwn',
{
token: ccn_localstorageAssist_GetApiToken()
}
);
}
function ccn_api_collection_getDetailOwn(_uuid) {
return ccn_api_dataTemplate(
'/api/collection/getDetailOwn',
{
token: ccn_localstorageAssist_GetApiToken(),
uuid: _uuid
}
);
}
function ccn_api_collection_addOwn(_name) {
return ccn_api_dataTemplate(
'/api/collection/addOwn',
{
token: ccn_localstorageAssist_GetApiToken(),
name: _name
}
);
}
function ccn_api_collection_updateOwn(_uuid, _name, _lastChange) {
return ccn_api_dataTemplate(
'/api/collection/updateOwn',
{
token: ccn_localstorageAssist_GetApiToken(),
uuid: _uuid,
name: _name,
lastChange: _lastChange
}
);
}
function ccn_api_collection_deleteOwn(_uuid, _lastChange) {
return ccn_api_boolTemplate(
'/api/collection/deleteOwn',
{
token: ccn_localstorageAssist_GetApiToken(),
uuid: _uuid,
lastChange: _lastChange
}
);
}
function ccn_api_collection_getSharing(_uuid) {
return ccn_api_dataTemplate(
'/api/collection/getSharing',
{
token: ccn_localstorageAssist_GetApiToken(),
uuid: _uuid
}
);
}
function ccn_api_collection_deleteSharing(_uuid, _target, _lastChange) {
return ccn_api_dataTemplate(
'/api/collection/deleteSharing',
{
token: ccn_localstorageAssist_GetApiToken(),
uuid: _uuid,
target: _target,
lastChange: _lastChange
}
);
}
function ccn_api_collection_addSharing(_uuid, _target, _lastChange) {
return ccn_api_dataTemplate(
'/api/collection/addSharing',
{
token: ccn_localstorageAssist_GetApiToken(),
uuid: _uuid,
target: _target,
lastChange: _lastChange
}
);
}
function ccn_api_collection_getShared() {
return ccn_api_dataTemplate(
'/api/collection/getShared',
{
token: ccn_localstorageAssist_GetApiToken()
}
);
}
// ====================================================== todo
function ccn_api_todo_getFull() {
return ccn_api_dataTemplate(
'/api/todo/getFull',
{
token: ccn_localstorageAssist_GetApiToken()
}
);
}
function ccn_api_todo_add() {
return ccn_api_dataTemplate(
'/api/todo/add',
{
token: ccn_localstorageAssist_GetApiToken()
}
);
}
function ccn_api_todo_update(_uuid, _data, _lastChange) {
return ccn_api_dataTemplate(
'/api/todo/update',
{
token: ccn_localstorageAssist_GetApiToken(),
uuid: _uuid,
data: _data,
lastChange: _lastChange
}
);
}
function ccn_api_todo_delete(_uuid, _lastChange) {
return ccn_api_boolTemplate(
'/api/todo/delete',
{
token: ccn_localstorageAssist_GetApiToken(),
uuid: _uuid,
lastChange: _lastChange
}
);
}
// ====================================================== admin
function ccn_api_admin_get() {
return ccn_api_dataTemplate(
'/api/admin/get',
{
token: ccn_localstorageAssist_GetApiToken()
}
);
}
function ccn_api_admin_add(_username) {
return ccn_api_dataTemplate(
'/api/admin/add',
{
token: ccn_localstorageAssist_GetApiToken(),
username: _username
}
);
}
function ccn_api_admin_update(_username, _password, _isAdmin) {
var data = {};
if (typeof(_password) != 'undefined')
data.password = _password;
if (typeof(_isAdmin) != 'undefined')
data.isAdmin = _isAdmin;
if (Object.getOwnPropertyNames(data).length == 0) return false;
data.token = ccn_localstorageAssist_GetApiToken();
data.username = _username;
return ccn_api_boolTemplate(
'/api/admin/update',
data
);
}
function ccn_api_admin_delete(_username) {
return ccn_api_boolTemplate(
'/api/admin/delete',
{
token: ccn_localstorageAssist_GetApiToken(),
username: _username
}
);
}
// ====================================================== profile
function ccn_api_profile_isAdmin() {
return ccn_api_boolTemplate(
'/api/profile/isAdmin',
{
token: ccn_localstorageAssist_GetApiToken()
}
);
}
function ccn_api_profile_changePassword(_password) {
return ccn_api_boolTemplate(
'/api/profile/changePassword',
{
token: ccn_localstorageAssist_GetApiToken(),
password: _password
}
);
}
function ccn_api_profile_getToken() {
return ccn_api_boolTemplate(
'/api/profile/getToken',
{
token: ccn_localstorageAssist_GetApiToken()
}
);
}
function ccn_api_profile_deleteToken(_deleteToken) {
return ccn_api_boolTemplate(
'/api/profile/deleteToken',
{
token: ccn_localstorageAssist_GetApiToken(),
deleteToken: _deleteToken
}
);
}

View File

@@ -0,0 +1,441 @@
// NOTE: this file is sync with dt.py. if this file or dt.py have bugs, all code should be changed
var ccn_datetime_monthDayCount = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
var ccn_datetime_MIN_YEAR = 1950;
var ccn_datetime_MAX_YEAR = 2200;
var ccn_datetime_MIN_DATETIME = new Date(Date.UTC(ccn_datetime_MIN_YEAR, 0, 1, 0, 0, 0, 0));
var ccn_datetime_MAX_DATETIME = new Date(Date.UTC(ccn_datetime_MAX_YEAR, 0, 1, 0, 0, 0, 0));
var ccn_datetime_MIN_TIMESTAMP = Math.floor(ccn_datetime_MIN_DATETIME.getTime() / 60000);
var ccn_datetime_MAX_TIMESTAMP = Math.floor(ccn_datetime_MAX_DATETIME.getTime() / 60000);
var ccn_datetime_DAY1_SPAN = 60 * 24;
var ccn_datetime_DAY7_SPAN = 7 * ccn_datetime_DAY1_SPAN;
var ccn_datetime_precompiledLoopRules = {
year: new RegExp(/^Y([SR]{1})([1-9]\d*)$/),
month: new RegExp(/^M([SR]{1})([ABCD]{1})([1-9]\d*)$/),
week: new RegExp(/^W([TF]{7})([1-9]\d*)$/),
day: new RegExp(/^D([1-9]\d*)$/)
};
var ccn_datetime_precompiledLoopStopRules = {
infinity: new RegExp(/^F$/),
datetime: new RegExp(/^D([1-9]\d*|0)$/),
times: new RegExp(/^T([1-9]\d*)$/)
}
/*
return format
[loopRules, loopStopRules] or undefined(invalid or no loop)
loopRules:
year loop: [0, isStrict, yearSpan]
month loop: [1, isStrict, monthMode, monthSpan]
week loop: [2, 7 bool item..., weekSpan]
day loop: [3, daySpan]
loopStopRules:
infinity: [0]
datetime: [1, timestamp]
times: [2, times]
*/
function ccn_datetime_ResolveLoopRules4UI(strl) {
if (strl == '') return undefined;
var sp = strl.split('-');
if (sp.length != 2) return undefined;
var loopRules = undefined;
var loopStopRules = undefined;
if (ccn_datetime_precompiledLoopRules.year.test(sp[0])) {
loopRules = [0, RegExp.$1 == 'S', parseInt(RegExp.$2)];
} else if (ccn_datetime_precompiledLoopRules.month.test(sp[0])) {
loopRules = [1, RegExp.$1 == 'S', RegExp.$2, parseInt(RegExp.$3)];
} else if (ccn_datetime_precompiledLoopRules.week.test(sp[0])) {
loopRules = [2];
for (var i = 0; i < 7; i++)
loopRules.push(RegExp.$1[i] == 'T');
loopRules.push(parseInt(RegExp.$2));
} else if (ccn_datetime_precompiledLoopRules.day.test(sp[0])) {
loopRules = [3, parseInt(RegExp.$1)];
} else return undefined;
if (ccn_datetime_precompiledLoopStopRules.infinity.test(sp[1])) {
loopStopRules = [0];
} else if (ccn_datetime_precompiledLoopStopRules.datetime.test(sp[1])) {
loopStopRules = [1, parseInt(RegExp.$1)];
} else if (ccn_datetime_precompiledLoopStopRules.times.test(sp[1])) {
loopStopRules = [2, parseInt(RegExp.$1)];
} else return undefined;
return [loopRules, loopStopRules];
}
// loopDateTimeStart's value is not correspond with database.
// it is calculated by program, should be pointed to the closing
// protential event start datetime.
// also loopDateTimeEnd, it was clamped with the tail of legal event
// clampStartDateTime is real clamp datetime of event start datetime.
// loopDateTimeStart is the start datetime for detect.
// in this section, all time should be analysed with Date((time + timezoneOffset) * 60000)
// and use .getUTC...() functions.
function ccn_datetime_ResolveLoopRules4Event(loopRules, loopDateTimeStart, loopDateTimeEnd, eventDateTimeStart, eventDateTimeEnd, timezoneOffset, clampStartDateTime) {
if (loopRules == '') return [
[Math.max(eventDateTimeStart, clampStartDateTime),
Math.max(loopDateTimeEnd, eventDateTimeEnd)]
];
var sp = loopRules.split('-');
if (sp.length != 2) return undefined;
var loopRules = sp[0]; // we don't need consider stop flag
var result = new Array();
// compute offset and duration
var eventDateTime = new Date((eventDateTimeStart + timezoneOffset) * 60000);
eventDateTime.setUTCHours(0, 0, 0, 0);
var eventOffset = eventDateTimeStart - (Math.floor(eventDateTime.getTime() / 60000) - timezoneOffset);
var eventDuration = eventDateTimeEnd - eventDateTimeStart;
var detectDateTime = new Date(loopDateTimeStart * 60000);
detectDateTime.setUTCHours(0, 0, 0, 0);
var originalYear = eventDateTime.getUTCFullYear();
var originalMonth = eventDateTime.getUTCMonth() + 1;
var originalDay = eventDateTime.getUTCDate();
// compute event
if (ccn_datetime_precompiledLoopRules.year.test(loopRules)) {
var isStrict = RegExp.$1 == 'S';
var loopSpan = parseInt(RegExp.$2);
var yearCount = detectDateTime.getFullYear() - originalYear;
var isSpecial = (originalMonth == 2 && originalDay == 29);
var realLoopSpan = (isSpecial && isStrict) ? LCM(4, loopSpan) : loopSpan;
//var fullSpanCount = Math.floor(yearCount / realLoopSpan);
var remainYear = yearCount % realLoopSpan;
//detectDateTime.setUTCFullYear(fullSpanCount + detectDateTime.getUTCFullYear(), 1, 1);
if (remainYear != 0)
detectDateTime.setUTCFullYear(realLoopSpan - remainYear + detectDateTime.getUTCFullYear(), 1 - 1, 1);
var skipFlag = false;
while(Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset <= loopDateTimeEnd) {
skipFlag = false;
if (isSpecial) {
// is special day, 29 Feb
// try set it in 29 Feb
if (isStrict) {
if (ccn_datetime_IsLeapYear(detectDateTime.getUTCFullYear())) detectDateTime.setUTCMonth(2 - 1, 29);
else skipFlag = true; // order skip
} else {
if (ccn_datetime_IsLeapYear(detectDateTime.getUTCFullYear())) detectDateTime.setUTCMonth(2 - 1, 29);
else detectDateTime.setUTCMonth(2 - 1, 28);
}
} else detectDateTime.setUTCMonth(originalMonth - 1, originalDay);
if (!skipFlag) {
result.push(
[Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset,
Math.floor(detectDateTime.getTime() / 60000) + eventOffset + eventDuration - timezoneOffset]
);
}
detectDateTime.setUTCFullYear(realLoopSpan + detectDateTime.getUTCFullYear());
}
} else if (ccn_datetime_precompiledLoopRules.month.test(loopRules)) {
var isStrict = RegExp.$1 == 'S';
var loopMethod = RegExp.$2;
var loopSpan = parseInt(RegExp.$3);
var monthsCount = ccn_datetime_MonthsCount(detectDateTime.getUTCFullYear(), detectDateTime.getUTCMonth() + 1) -
ccn_datetime_MonthsCount(originalYear, originalMonth);
//var fullSpanCount = Math.floor(monthsCount / loopSpan);
var remainMonth = monthsCount % loopSpan;
//detectDateTime.setUTCMonth(fullSpanCount * loopSpan + detectDateTime.getUTCMonth(), 1);
detectDateTime.setUTCDate(1);
if (remainMonth != 0)
detectDateTime.setUTCMonth(loopSpan - remainMonth + detectDateTime.getUTCMonth(), 1);
while(Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset <= loopDateTimeEnd) {
var data = ccn_datetime_GetRemanagedDayInMonth(originalYear, originalMonth, originalDay, detectDateTime.getUTCFullYear(), detectDateTime.getUTCMonth() + 1, isStrict);
var predictedDay = undefined;
switch(loopMethod) {
case 'A':
if (typeof(data[0]) != 'undefined') predictedDay = data[0];
break;
case 'B':
if (typeof(data[1]) != 'undefined') predictedDay = data[1];
break;
case 'C':
if (typeof(data[2]) != 'undefined') predictedDay = data[2];
break;
case 'D':
if (typeof(data[3]) != 'undefined') predictedDay = data[3];
break;
}
if (typeof(predictedDay) != 'undefined') {
detectDateTime.setUTCDate(predictedDay);
result.push(
[Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset,
Math.floor(detectDateTime.getTime() / 60000) + eventOffset + eventDuration - timezoneOffset]
);
}
detectDateTime.setUTCMonth(loopSpan + detectDateTime.getUTCMonth(), 1);
}
} else if (ccn_datetime_precompiledLoopRules.week.test(loopRules)) {
var loopSpan = parseInt(RegExp.$2);
var weekOption = [];
var weekEventCount = 0
for (var i = 0; i < 7; i++) {
weekOption.push(RegExp.$1[i] == 'T');
if (RegExp.$1[i] == 'T') weekEventCount++;
}
var originalWeek = ccn_datetime_DayOfWeek(originalYear, originalMonth, originalDay);
// try insert original event
if (!weekOption[originalWeek]) {
result.push(
[eventDateTimeStart, eventDateTimeEnd]
);
}
var daysCount = ccn_datetime_DaysCount(detectDateTime.getUTCFullYear(), detectDateTime.getUTCMonth() + 1, detectDateTime.getDate()) -
ccn_datetime_DaysCount(originalYear, originalMonth, originalDay);
//var fullSpanCount = Math.floor(daysCount / (7 * loopSpan));
var remainFullSpanCount = Math.floor((daysCount % (7 * loopSpan)) / 7);
var remainDays = (daysCount % (7 * loopSpan)) % 7;
//detectDateTime.setUTCDate((7 * loopSpan * fullSpanCount) + detectDateTime.getUTCDate());
if (remainFullSpanCount != 0) {
detectDateTime.setUTCDate((loopSpan - remainFullSpanCount) * 7 + detectDateTime.getUTCDate());
}
var weekCounter = remainDays;
while(Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset <= loopDateTimeEnd) {
if (weekOption[(weekCounter + originalWeek) % 7])
result.push(
[Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset,
Math.floor(detectDateTime.getTime() / 60000) + eventOffset + eventDuration - timezoneOffset]
);
weekCounter = (weekCounter + 1) % 7;
detectDateTime.setUTCDate(detectDateTime.getUTCDate() + 1);
if (weekCounter == 0)
detectDateTime.setUTCDate(detectDateTime.getUTCDate() + (loopSpan - 1) * 7);
}
} else if (ccn_datetime_precompiledLoopRules.day.test(loopRules)) {
var loopSpan = parseInt(RegExp.$1);
var daysCount = ccn_datetime_DaysCount(detectDateTime.getUTCFullYear(), detectDateTime.getUTCMonth() + 1, detectDateTime.getUTCDate()) -
ccn_datetime_DaysCount(originalYear, originalMonth, originalDay);
//var fullSpanCount = Math.floor(daysCount / loopSpan);
var remainDays = daysCount % loopSpan;
//detectDateTime.setUTCDate(fullSpanCount * loopSpan + detectDateTime.getUTCDate());
if (remainDays != 0)
detectDateTime.setUTCDate(loopSpan - remainDays + detectDateTime.getUTCDate());
while(Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset <= loopDateTimeEnd) {
result.push(
[Math.floor(detectDateTime.getTime() / 60000) + eventOffset - timezoneOffset,
Math.floor(detectDateTime.getTime() / 60000) + eventOffset + eventDuration - timezoneOffset]
);
detectDateTime.setUTCDate(detectDateTime.getUTCDate() + loopSpan);
}
} else return undefined;
// clamp item
var realResult = [];
for (var i in result) {
var start = result[i][0];
var end = result[i][1];
if (end > clampStartDateTime && start <= loopDateTimeEnd)
realResult.push([Math.max(start, clampStartDateTime), Math.min(end, loopDateTimeEnd)]);
}
return realResult;
}
function ccn_datetime_ResolveLoopRules4Text(strl, startDateTime, timezoneOffset) {
if (strl == '') return "";
var sp = strl.split('-');
if (sp.length != 2) return "";
var loopRules = undefined;
var loopStopRules = undefined;
var datetimeInstance = new Date((startDateTime + timezoneOffset) * 60000)
if (ccn_datetime_precompiledLoopRules.year.test(sp[0])) {
if (RegExp.$1 == 'S')
loopRules = $.i18n.prop('ccn-i18n-datetime-loopRuleText-modeStrict');
else
loopRules = $.i18n.prop('ccn-i18n-datetime-loopRuleText-modeRough');
loopRules += $.i18n.prop('ccn-i18n-datetime-loopRuleText-year')
.format(parseInt(RegExp.$2), datetimeInstance.toLocaleDateString(undefined, {timeZone: "UTC"}));
} else if (ccn_datetime_precompiledLoopRules.month.test(sp[0])) {
if (RegExp.$1 == 'S')
loopRules = $.i18n.prop('ccn-i18n-datetime-loopRuleText-modeStrict');
else
loopRules = $.i18n.prop('ccn-i18n-datetime-loopRuleText-modeRough');
var dayInMonth = ccn_datetime_GetDayInMonth(
datetimeInstance.getUTCFullYear(),
datetimeInstance.getUTCMonth() + 1,
datetimeInstance.getUTCDate());
switch(RegExp.$2) {
case 'A':
loopRules = $.i18n.prop('ccn-i18n-datetime-loopRuleText-monthA')
.format(parseInt(RegExp.$3), dayInMonth[0]);
break;
case 'B':
loopRules = $.i18n.prop('ccn-i18n-datetime-loopRuleText-monthB')
.format(parseInt(RegExp.$3), dayInMonth[1]);
break;
case 'C':
loopRules = $.i18n.prop('ccn-i18n-datetime-loopRuleText-monthC')
.format(parseInt(RegExp.$3), dayInMonth[2], dayInMonth[3]);
break;
case 'D':
loopRules = $.i18n.prop('ccn-i18n-datetime-loopRuleText-monthD')
.format(parseInt(RegExp.$3), dayInMonth[4], dayInMonth[5]);
break;
}
} else if (ccn_datetime_precompiledLoopRules.week.test(sp[0])) {
var weekOfDayCache = [];
for (var i = 0; i < 7; i++) {
if (RegExp.$1[i] == 'T')
weekOfDayCache.push(ccn_i18n_UniversalGetDayOfWeek(i));
}
loopRules = $.i18n.prop('ccn-i18n-datetime-loopRuleText-week')
.format(parseInt(RegExp.$2), weekOfDayCache.join(', '));
} else if (ccn_datetime_precompiledLoopRules.day.test(sp[0])) {
loopRules = $.i18n.prop('ccn-i18n-datetime-loopRuleText-day')
.format(parseInt(RegExp.$1));
} else return "";
if (ccn_datetime_precompiledLoopStopRules.infinity.test(sp[1])) {
loopStopRules = $.i18n.prop('ccn-i18n-datetime-loopStopRuleText-infinity');
} else if (ccn_datetime_precompiledLoopStopRules.datetime.test(sp[1])) {
loopStopRules = $.i18n.prop('ccn-i18n-datetime-loopStopRuleText-datetime')
.format(new Date(parseInt(RegExp.$1)).toLocaleDateString());
} else if (ccn_datetime_precompiledLoopStopRules.times.test(sp[1])) {
loopStopRules = $.i18n.prop('ccn-i18n-datetime-loopStopRuleText-times')
.format(parseInt(RegExp.$1));
} else return "";
return (loopRules + loopStopRules);
}
function ccn_datetime_LeapYearCountEx(endYear, includeThis, baseYear, includeBase) {
if (!includeThis) endYear--;
if (includeBase) baseYear--;
var endly = Math.floor(endYear / 4);
endly -= Math.floor(endYear / 100);
endly += Math.floor(endYear / 400);
var basely = Math.floor(baseYear / 4);
basely -= Math.floor(baseYear / 100);
basely += Math.floor(baseYear / 400);
return (endly - basely);
}
function ccn_datetime_DaysCount(year, month, day) {
var ly = ccn_datetime_LeapYearCountEx(year, false, 1, true);
var days = 365 * (year - 1);
days += ly;
for(var index = 1; index < month; index++)
days += ccn_datetime_monthDayCount[index - 1];
if (month > 2 && ccn_datetime_IsLeapYear(year))
days += 1;
days += day - 1;
return days;
}
function ccn_datetime_MonthsCount(year, month) {
return (year - 1) * 12 + (month - 1);
}
function ccn_datetime_DayOfWeek(year, month, day) {
return ccn_datetime_DaysCount(year, month, day) % 7;
}
function ccn_datetime_GetDayInMonth(year, month, day) {
var days = ccn_datetime_monthDayCount[month - 1] + ((month == 2 && ccn_datetime_IsLeapYear(year)) ? 1 : 0);
var firstDayOfWeek = ccn_datetime_DayOfWeek(year, month, 1);
var dayOfWeek = (firstDayOfWeek + day - 1) % 7;
var dayForwards = day;
var dayBackwards = days - day + 1;
var weeksForward = Math.floor((dayForwards - 1) / 7) + 1;
var weeksBackwards = Math.floor((dayBackwards - 1) / 7) + 1;
return [dayForwards, dayBackwards, weeksForward, dayOfWeek, weeksBackwards, dayOfWeek];
}
function ccn_datetime_GetRemanagedDayInMonth(oldYear, oldMonth, oldDay, newYear, newMonth, isStrict) {
var ddata = ccn_datetime_GetDayInMonth(oldYear, oldMonth, oldDay);
var mdata = ccn_datetime_GetMonthWeekStatistics(newYear, newMonth);
var days = ccn_datetime_monthDayCount[newMonth - 1] + ((newMonth == 2 && ccn_datetime_IsLeapYear(year)) ? 1 : 0);
var firstDayOfWeek = ccn_datetime_DayOfWeek(newYear, newMonth, 1);
//var lastDayOfWeek = (firstDayOfWeek + days - 1) % 7;
if (isStrict) {
var methodA = ddata[0] > days ? undefined : ddata[0];
var methodB = ddata[1] > days ? undefined : (days - ddata[1] + 1);
} else {
var methodA = Math.min(ddata[0], days);
var methodB = days - Math.min(ddata[1], days) + 1;
}
var methodC = undefined;
if (ddata[2] <= mdata[ddata[3]] || !isStrict) {
var targetWeek = Math.min(ddata[2], mdata[ddata[3]]);
methodC = 1 + (targetWeek - 1) * 7 + ((ddata[3] + 7 - firstDayOfWeek) % 7);
}
var methodD = undefined;
if (ddata[4] <= mdata[ddata[5]] || !isStrict) {
// convert to type c and calc
var targetWeek = mdata[ddata[5]] - Math.min(ddata[4], mdata[ddata[5]]) + 1;
methodD = 1 + (targetWeek - 1) * 7 + ((ddata[5] + 7 - firstDayOfWeek) % 7);
}
return [methodA, methodB, methodC, methodD];
}
function ccn_datetime_GetMonthWeekStatistics(year, month) {
var days = ccn_datetime_monthDayCount[month - 1] + ((month == 2 && ccn_datetime_IsLeapYear(year)) ? 1 : 0);
var firstDayOfWeek = ccn_datetime_DayOfWeek(year, month, 1);
var result = [4, 4, 4, 4, 4, 4, 4];
var remain = days % 7;
var week = firstDayOfWeek;
while (remain > 0) {
result[week % 7] += 1;
week++;
remain--;
}
return result;
}
function ccn_datetime_IsLeapYear(year) {
var isLeap = false;
if (year % 4 == 0) isLeap = true;
if (year % 100 == 0) isLeap = false;
if (year % 400 == 0) isLeap = true;
return isLeap;
}

View File

@@ -0,0 +1,522 @@
var ccn_datetimepicker_tabType = {
year: 0,
month: 1,
day: 2,
hour: 3,
minute: 4
};
var ccn_datetimepicker_dialPlateWidth = 200;
var ccn_datetimepicker_dialPlateRadius = ccn_datetimepicker_dialPlateWidth / 2;
var ccn_datetimepicker_dialPlateHourInnerPercent = 0.6;
var ccn_datetimepicker_dialPlateHourOutterPercent = 0.8;
var ccn_datetimepicker_dialPlateHourDistinguishPercent = 0.7;
var ccn_datetimepicker_dialPlateMinutePercent = 0.8;
var ccn_datetimepicker_dialPlateHourResolution = Math.PI * 2 / 12;
var ccn_datetimepicker_dialPlateMinuteResolution = Math.PI * 2 / 60;
var ccn_datetimepicker_mode = undefined;
var ccn_datetimepicker_isUTC = undefined;
var ccn_datetimepicker_pickerIndex = undefined;
var ccn_datetimepicker_enableMinuteDrag = false;
var ccn_datetimepicker_enableHourDrag = false;
var ccn_datetimepicker_internalDateTime = new Date();
var ccn_datetimepicker_displayCacheDateTime = new Date();
// ========================================= export func
function ccn_datetimepicker_Insert() {
$('body').append(ccn_template_datetimepicker.render());
// bind size event and trigge once
$(window).resize(ccn_datetimepicker_RefreshSvg).resize();
// add data attr
for(var i = 0; i < 3; i++) {
for(var j = 0; j < 4; j++) {
$('#ccn-datetimepiacker-panelMonth-table > div:nth-child({0}) > div:nth-child({1})'.format(i + 1, j + 1))
.attr('data', i * 4 + j);
}
}
// bind header event
$('header.pickerHeader > div').click(function() {
ccn_datetimepicker_SwitchTab(ccn_datetimepicker_Str2TabType($(this).attr('type')));
});
// bind button event
$('#ccn-datetimepiacker-panelYear-prevBtn').click(function() {
ccn_datetimepicker_PrevNextYear(true);
});
$('#ccn-datetimepiacker-panelYear-nextBtn').click(function() {
ccn_datetimepicker_PrevNextYear(false);
});
$('#ccn-datetimepiacker-panelMonth-prevBtn').click(function() {
ccn_datetimepicker_PrevNextMonth(true);
});
$('#ccn-datetimepiacker-panelMonth-nextBtn').click(function() {
ccn_datetimepicker_PrevNextMonth(false);
});
$('#ccn-datetimepiacker-panelDay-prevBtn').click(function() {
ccn_datetimepicker_PrevNextDay(true);
});
$('#ccn-datetimepiacker-panelDay-nextBtn').click(function() {
ccn_datetimepicker_PrevNextDay(false);
});
$('#ccn-datetimepiacker-panelYear-table > div > div').click(ccn_datetimepicker_ClickYear);
$('#ccn-datetimepiacker-panelMonth-table > div > div').click(ccn_datetimepicker_ClickMonth);
$('#ccn-datetimepiacker-panelDay-table > div:nth-child(n+1) > div').click(ccn_datetimepicker_ClickDay);
$('#ccn-datetimepicker-panelHour')
.mousedown(ccn_datetimepicker_StartDragHour)
.mousemove(ccn_datetimepicker_DraggingHour)
.mouseup(ccn_datetimepicker_StopDragHour)
.on('touchstart', ccn_datetimepicker_StartDragHour)
.on('touchmove', ccn_datetimepicker_DraggingHour)
.on('touchend', ccn_datetimepicker_StopDragHour);
$('#ccn-datetimepicker-panelMinute')
.mousedown(ccn_datetimepicker_StartDragMinute)
.mousemove(ccn_datetimepicker_DraggingMinute)
.mouseup(ccn_datetimepicker_StopDragMinute)
.on('touchstart', ccn_datetimepicker_StartDragMinute)
.on('touchmove', ccn_datetimepicker_DraggingMinute)
.on('touchend', ccn_datetimepicker_StopDragMinute);
$('#ccn-datetimepicker-btnConfirm').click(ccn_datetimepicker_Confirm);
$('#ccn-datetimepicker-btnCancel').click(ccn_datetimepicker_Cancel);
}
function ccn_datetimepicker_Modal(mode, pickerIndex, isUTC) {
ccn_datetimepicker_mode = mode;
ccn_datetimepicker_isUTC = isUTC;
ccn_datetimepicker_pickerIndex = pickerIndex;
ccn_datetimepicker_internalDateTime = ccn_datetimepicker_Get(pickerIndex, false);
$('header.pickerHeader > div').hide();
switch(mode) {
case ccn_datetimepicker_tabType.minute:
$('header.pickerHeader > div[type=minute]').show();
case ccn_datetimepicker_tabType.hour:
$('header.pickerHeader > div[type=hour]').show();
case ccn_datetimepicker_tabType.day:
$('header.pickerHeader > div[type=day]').show();
case ccn_datetimepicker_tabType.month:
$('header.pickerHeader > div[type=month]').show();
case ccn_datetimepicker_tabType.year:
$('header.pickerHeader > div[type=year]').show();
break;
}
$('#ccn-datetimepicker-modal').addClass('is-active');
ccn_datetimepicker_SwitchTab(mode); // this call is set in there by design. if you don't show the dialog, the call of svg resize will fail.
}
function ccn_datetimepicker_Confirm() {
// update and call callback func
ccn_datetimepicker_Set(
ccn_datetimepicker_pickerIndex,
ccn_datetimepicker_internalDateTime,
ccn_datetimepicker_isUTC,
ccn_datetimepicker_mode
);
$('#ccn-datetimepicker-modal').removeClass('is-active');
}
function ccn_datetimepicker_Cancel() {
$('#ccn-datetimepicker-modal').removeClass('is-active');
}
// ========================================= internal func
function ccn_datetimepicker_OnSvgResize(ele) {
var scale = 200 / Math.min(ele.width(), ele.height());
ele.css('font-size', scale + 'em');
}
function ccn_datetimepicker_SwitchTab(newTab) {
$('div.pickerContainer > *').hide();
ccn_datetimepicker_displayCacheDateTime.setTime(ccn_datetimepicker_internalDateTime.getTime());
ccn_datetimepicker_RefreshDisplay(newTab);
switch(newTab) {
case ccn_datetimepicker_tabType.year:
$('#ccn-datetimepicker-panelYear').show();
break;
case ccn_datetimepicker_tabType.month:
$('#ccn-datetimepicker-panelMonth').show();
break;
case ccn_datetimepicker_tabType.day:
$('#ccn-datetimepicker-panelDay').show();
break;
case ccn_datetimepicker_tabType.hour:
$('#ccn-datetimepicker-panelHour').show();
ccn_datetimepicker_RefreshSvg(); // immediately trigger once svg resize
break;
case ccn_datetimepicker_tabType.minute:
$('#ccn-datetimepicker-panelMinute').show();
ccn_datetimepicker_RefreshSvg(); // immediately trigger once svg resize
break;
}
}
function ccn_datetimepicker_RefreshDisplay(tab) {
// header should be refreshed entirely
$('#ccn-datetimepicker-datetime-year').text(ccn_datetimepicker_internalDateTime.getFullYear());
$('#ccn-datetimepicker-datetime-month').text(ccn_datetimepicker_internalDateTime.getMonth() + 1);
$('#ccn-datetimepicker-datetime-day').text(ccn_datetimepicker_internalDateTime.getDate());
$('#ccn-datetimepicker-datetime-hour').text(ccn_datetimepicker_internalDateTime.getHours());
$('#ccn-datetimepicker-datetime-minute').text(ccn_datetimepicker_internalDateTime.getMinutes());
// refresh tab according to specific `tab`
switch(tab) {
case ccn_datetimepicker_tabType.year:
var startYear = Math.floor((ccn_datetimepicker_displayCacheDateTime.getFullYear() - ccn_datetime_MIN_YEAR) / 12) * 12 + ccn_datetime_MIN_YEAR;
var counter = startYear;
for(var i = 0; i < 3; i++) {
for(var j = 0; j < 4; j++, counter++) {
var ele = $('#ccn-datetimepiacker-panelYear-table > div:nth-child({0}) > div:nth-child({1})'.format(i + 1, j + 1));
if (counter < ccn_datetime_MAX_YEAR) {
ele.attr('data', counter)
.text(counter);
} else {
ele.attr('data', '')
.html('&nbsp;');
}
if (counter == ccn_datetimepicker_internalDateTime.getFullYear()) ele.attr('picked', 'true');
else ele.attr('picked', 'false');
}
}
$('#ccn-datetimepiacker-panelYear-title')
.text('{0} - {1}'.format(startYear, startYear + 12 < ccn_datetime_MAX_YEAR ? startYear + 12 : ccn_datetime_MAX_YEAR));
break;
case ccn_datetimepicker_tabType.month:
$('#ccn-datetimepiacker-panelMonth-table > div > div').attr('picked', 'false');
if (ccn_datetimepicker_internalDateTime.getFullYear() == ccn_datetimepicker_displayCacheDateTime.getFullYear()) {
var month = ccn_datetimepicker_internalDateTime.getMonth();
$('#ccn-datetimepiacker-panelMonth-table > div:nth-child({0}) > div:nth-child({1})'.format(Math.floor(month / 4) + 1, (month % 4) + 1))
.attr('picked', 'true');
}
$('#ccn-datetimepiacker-panelMonth-title')
.text(ccn_datetimepicker_displayCacheDateTime.getFullYear());
break;
case ccn_datetimepicker_tabType.day:
var gottenYear = ccn_datetimepicker_displayCacheDateTime.getFullYear();
var gottenMonth = ccn_datetimepicker_displayCacheDateTime.getMonth() + 1;
var counter = -ccn_datetime_DayOfWeek(gottenYear, gottenMonth, 1);
var days = ccn_datetime_monthDayCount[gottenMonth - 1] + ((gottenMonth == 2 && ccn_datetime_IsLeapYear(gottenYear)) ? 1 : 0);
for(var i = 0; i < 6; i++) {
for(var j = 0; j < 7; j++, counter++) {
var ele = $('#ccn-datetimepiacker-panelDay-table > div:nth-child({0}) > div:nth-child({1})'.format(i + 2, j + 1));
if (counter < 0 || counter >= days) ele.attr('data', '').html('&nbsp;');
else ele.attr('data', counter + 1).text(counter + 1);
if (counter + 1 == ccn_datetimepicker_internalDateTime.getDate()) ele.attr('picked', 'true');
else ele.attr('picked', 'false');
}
}
$('#ccn-datetimepiacker-panelDay-title')
.text('{0} - {1}'.format(
ccn_datetimepicker_displayCacheDateTime.getFullYear(),
ccn_i18n_UniversalGetMonth(ccn_datetimepicker_displayCacheDateTime.getMonth())
));
break;
case ccn_datetimepicker_tabType.hour:
var gottenHour = ccn_datetimepicker_displayCacheDateTime.getHours();
var newX = Math.cos((3 - gottenHour) * Math.PI * 2 / 12);
var newY = Math.sin((3 - gottenHour) * Math.PI * 2 / 12);
var radius = ccn_datetimepicker_dialPlateRadius * (gottenHour < 12 ? ccn_datetimepicker_dialPlateHourOutterPercent : ccn_datetimepicker_dialPlateHourInnerPercent);
newX = newX * radius + ccn_datetimepicker_dialPlateRadius;
newY = (-newY * radius) + ccn_datetimepicker_dialPlateRadius;
$('#ccn-datetimepicker-panelHour > line')
.attr('x2', newX)
.attr('y2', newY);
$('#ccn-datetimepicker-panelHour > circle[type=symbol]')
.attr('cx', newX)
.attr('cy', newY);
break;
case ccn_datetimepicker_tabType.minute:
var gottenMinute = ccn_datetimepicker_displayCacheDateTime.getMinutes();
var newX = Math.cos((15 - gottenMinute) * Math.PI * 2 / 60);
var newY = Math.sin((15 - gottenMinute) * Math.PI * 2 / 60);
var radius = ccn_datetimepicker_dialPlateRadius * ccn_datetimepicker_dialPlateMinutePercent;
newX = newX * radius + ccn_datetimepicker_dialPlateRadius;
newY = (-newY * radius) + ccn_datetimepicker_dialPlateRadius;
$('#ccn-datetimepicker-panelMinute > line')
.attr('x2', newX)
.attr('y2', newY);
$('#ccn-datetimepicker-panelMinute > circle[type=symbol]')
.attr('cx', newX)
.attr('cy', newY);
break;
}
}
function ccn_datetimepicker_RefreshSvg() {
// svg resize only can be called when the svg is showing.
// so call this func in window resize event or
// displaying svg.
$('div.pickerContainer > svg').each(function() {
ccn_datetimepicker_OnSvgResize($(this));
});
}
function ccn_datetimepicker_Str2TabType(strl) {
switch(strl) {
case 'year':
return ccn_datetimepicker_tabType.year
case 'month':
return ccn_datetimepicker_tabType.month
case 'day':
return ccn_datetimepicker_tabType.day
case 'hour':
return ccn_datetimepicker_tabType.hour
case 'minute':
return ccn_datetimepicker_tabType.minute
}
return undefined;
}
function ccn_datetimepicker_GetUniformedXY(mouseOrTouchEvent, elements) {
var offset = {
left: elements.offset().left,
top: elements.offset().top,
halfWidth: elements.width() / 2,
halfHeight: elements.height() / 2,
halfSquareWidthHeight: Math.min(elements.width(), elements.height()) / 2
}
if(typeof(mouseOrTouchEvent.pageX) != 'undefined' && typeof(mouseOrTouchEvent.pageY) != 'undefined') {
offset.realX = mouseOrTouchEvent.pageX;
offset.realY = mouseOrTouchEvent.pageY;
} else if(typeof(mouseOrTouchEvent.targetTouches) != 'undefined' && mouseOrTouchEvent.targetTouches.length >= 1) {
offset.realX = mouseOrTouchEvent.targetTouches[0].pageX;
offset.realY = mouseOrTouchEvent.targetTouches[0].pageY;
} else {
offset.realX = 0;
offset.realY = 0;
}
var _x = (offset.realX - offset.left - offset.halfWidth) / offset.halfSquareWidthHeight * ccn_datetimepicker_dialPlateRadius;
var _y = -((offset.realY - offset.top - offset.halfHeight) / offset.halfSquareWidthHeight * ccn_datetimepicker_dialPlateRadius);
return {x: _x, y: _y};
}
function ccn_datetimepicker_PrevNextYear(isPrev) {
ccn_datetimepicker_displayCacheDateTime.setFullYear(
ccn_datetimepicker_displayCacheDateTime.getFullYear() + (isPrev ? -12 : 12));
ccn_datetimepicker_ClampDateTime(ccn_datetimepicker_displayCacheDateTime);
ccn_datetimepicker_RefreshDisplay(ccn_datetimepicker_tabType.year);
}
function ccn_datetimepicker_PrevNextMonth(isPrev) {
ccn_datetimepicker_displayCacheDateTime.setFullYear(
ccn_datetimepicker_displayCacheDateTime.getFullYear() + (isPrev ? -1 : 1));
ccn_datetimepicker_ClampDateTime(ccn_datetimepicker_displayCacheDateTime);
ccn_datetimepicker_RefreshDisplay(ccn_datetimepicker_tabType.month);
}
function ccn_datetimepicker_PrevNextDay(isPrev) {
ccn_datetimepicker_displayCacheDateTime.setMonth(
ccn_datetimepicker_displayCacheDateTime.getMonth() + (isPrev ? -1 : 1));
ccn_datetimepicker_ClampDateTime(ccn_datetimepicker_displayCacheDateTime);
ccn_datetimepicker_RefreshDisplay(ccn_datetimepicker_tabType.day);
}
function ccn_datetimepicker_ClickYear() {
var ele = $(this);
if (ele.attr('data') == '') return;
ccn_datetimepicker_internalDateTime.setFullYear(parseInt(ele.attr('data')));
ccn_datetimepicker_ClampDateTime(ccn_datetimepicker_internalDateTime);
if (ccn_datetimepicker_mode != ccn_datetimepicker_tabType.year)
ccn_datetimepicker_SwitchTab(ccn_datetimepicker_tabType.month);
else
ccn_datetimepicker_RefreshDisplay(ccn_datetimepicker_tabType.year);
}
function ccn_datetimepicker_ClickMonth() {
var ele = $(this);
if (ele.attr('data') == '') return;
ccn_datetimepicker_internalDateTime.setFullYear(
ccn_datetimepicker_displayCacheDateTime.getFullYear(),
parseInt(ele.attr('data'))
);
ccn_datetimepicker_ClampDateTime(ccn_datetimepicker_internalDateTime);
if (ccn_datetimepicker_mode != ccn_datetimepicker_tabType.month)
ccn_datetimepicker_SwitchTab(ccn_datetimepicker_tabType.day);
else
ccn_datetimepicker_RefreshDisplay(ccn_datetimepicker_tabType.month);
}
function ccn_datetimepicker_ClickDay() {
var ele = $(this);
if (ele.attr('data') == '') return;
ccn_datetimepicker_internalDateTime.setFullYear(
ccn_datetimepicker_displayCacheDateTime.getFullYear(),
ccn_datetimepicker_displayCacheDateTime.getMonth(),
parseInt(ele.attr('data'))
);
ccn_datetimepicker_ClampDateTime(ccn_datetimepicker_internalDateTime);
if (ccn_datetimepicker_mode != ccn_datetimepicker_tabType.day)
ccn_datetimepicker_SwitchTab(ccn_datetimepicker_tabType.hour);
else
ccn_datetimepicker_RefreshDisplay(ccn_datetimepicker_tabType.day);
}
function ccn_datetimepicker_StartDragHour() { ccn_datetimepicker_enableHourDrag = true; }
function ccn_datetimepicker_DraggingHour(e) {
if (!ccn_datetimepicker_enableHourDrag) return;
var offset = ccn_datetimepicker_GetUniformedXY(e, $('#ccn-datetimepicker-panelHour'));
var x = offset.x;
var y = offset.y;
var distance = Math.sqrt(x * x + y * y);
var angle = Math.acos(x / distance);
if (y < 0) angle = Math.PI * 2 - angle; // correct negative y axis angle
angle += (ccn_datetimepicker_dialPlateHourResolution / 2); // correct offset
if (angle > Math.PI * 2)
angle -= Math.PI * 2;
var number = Math.floor(angle / ccn_datetimepicker_dialPlateHourResolution);
if (number >= 12) number = 11; // prevent unexpected result at the edge.
number = (15 - number) % 12;
if (distance < ccn_datetimepicker_dialPlateRadius * ccn_datetimepicker_dialPlateHourDistinguishPercent)
number += 12;
// judge
if (ccn_datetimepicker_displayCacheDateTime.getHours() != number) {
ccn_datetimepicker_displayCacheDateTime.setHours(number);
ccn_datetimepicker_RefreshDisplay(ccn_datetimepicker_tabType.hour);
}
e.preventDefault();
}
function ccn_datetimepicker_StopDragHour() {
ccn_datetimepicker_enableHourDrag = false;
ccn_datetimepicker_internalDateTime.setHours(ccn_datetimepicker_displayCacheDateTime.getHours());
ccn_datetimepicker_ClampDateTime(ccn_datetimepicker_internalDateTime);
if (ccn_datetimepicker_mode != ccn_datetimepicker_tabType.hour)
ccn_datetimepicker_SwitchTab(ccn_datetimepicker_tabType.minute);
}
function ccn_datetimepicker_StartDragMinute() { ccn_datetimepicker_enableMinuteDrag = true; }
function ccn_datetimepicker_DraggingMinute(e) {
if (!ccn_datetimepicker_enableMinuteDrag) return;
var offset = ccn_datetimepicker_GetUniformedXY(e, $('#ccn-datetimepicker-panelMinute'));
var x = offset.x;
var y = offset.y;
var distance = Math.sqrt(x * x + y * y);
var angle = Math.acos(x / distance);
if (y < 0) angle = Math.PI * 2 - angle; // correct negative y axis angle
angle += (ccn_datetimepicker_dialPlateMinuteResolution / 2); // correct offset
if (angle > Math.PI * 2)
angle -= Math.PI * 2;
var number = Math.floor(angle / ccn_datetimepicker_dialPlateMinuteResolution);
if (number >= 60) number = 59; // prevent unexpected result at the edge.
number = (75 - number) % 60;
// judge
if (ccn_datetimepicker_displayCacheDateTime.getMinutes() != number) {
ccn_datetimepicker_displayCacheDateTime.setMinutes(number);
ccn_datetimepicker_RefreshDisplay(ccn_datetimepicker_tabType.minute);
}
e.preventDefault();
}
function ccn_datetimepicker_StopDragMinute() {
ccn_datetimepicker_enableMinuteDrag = false;
ccn_datetimepicker_internalDateTime.setMinutes(ccn_datetimepicker_displayCacheDateTime.getMinutes());
ccn_datetimepicker_ClampDateTime(ccn_datetimepicker_internalDateTime);
// no page need to go to
// but we need refresh current page
ccn_datetimepicker_RefreshDisplay(ccn_datetimepicker_tabType.minute);
}
function ccn_datetimepicker_ClampDateTime(dateObj) {
if (dateObj < ccn_datetime_MIN_DATETIME)
dateObj.setTime(ccn_datetime_MIN_DATETIME.getTime());
if (dateObj >= ccn_datetime_MAX_DATETIME)
dateObj.setTime(ccn_datetime_MAX_DATETIME.getTime());
}
// ========================================================== universal function
function ccn_datetimepicker_Set(pickerIndex, dt, isUTC, mode) {
var ele = $('[datetimepicker=' + pickerIndex + ']');
while(true) {
if (mode < ccn_datetimepicker_tabType.year) break;
ele.attr('datetimepicker-year', isUTC ? dt.getUTCFullYear() : dt.getFullYear());
if (mode < ccn_datetimepicker_tabType.month) break;
ele.attr('datetimepicker-month', (isUTC ? dt.getUTCMonth() : dt.getMonth()) + 1);
if (mode < ccn_datetimepicker_tabType.day) break;
ele.attr('datetimepicker-day', isUTC ? dt.getUTCDate() : dt.getDate());
if (mode < ccn_datetimepicker_tabType.hour) break;
ele.attr('datetimepicker-hour', isUTC ? dt.getUTCHours() : dt.getHours());
if (mode < ccn_datetimepicker_tabType.minute) break;
ele.attr('datetimepicker-minute', isUTC ? dt.getUTCMinutes() : dt.getMinutes());
break;
}
if (typeof(ele.prop('funcs')) != 'undefined' && typeof(ele.prop('funcs').callback) == 'function')
ele.prop('funcs').callback();
}
function ccn_datetimepicker_Get(pickerIndex, isUTC) {
var ele = $('[datetimepicker=' + pickerIndex + ']');
year = ele.attr('datetimepicker-year');
month = ele.attr('datetimepicker-month');
day = ele.attr('datetimepicker-day');
hour = ele.attr('datetimepicker-hour');
minute = ele.attr('datetimepicker-minute');
if (IsUndefinedOrEmpty(year)) year = ccn_datetime_MIN_YEAR;
if (IsUndefinedOrEmpty(month)) month = 1;
if (IsUndefinedOrEmpty(day)) day = 1;
if (IsUndefinedOrEmpty(hour)) hour = 0;
if (IsUndefinedOrEmpty(minute)) minute = 0;
if (isUTC) return new Date(Date.UTC(year, parseInt(month) - 1, day, hour, minute, 0, 0));
else return new Date(year, parseInt(month) - 1, day, hour, minute, 0, 0);
}

View File

@@ -0,0 +1,61 @@
function ccn_headerNav_Insert() {
$('body').prepend(ccn_template_headerNav.render());
}
function ccn_headerNav_LoggedRefresh() {
if (ccn_api_common_tokenValid()) {
// logged, show all nav button and logout button
$("#ccn-header-nav-home").show();
$("#ccn-header-nav-collection").show();
$("#ccn-header-nav-calendar").show();
$("#ccn-header-nav-todo").show();
$("#ccn-header-nav-admin").show();
$("#ccn-header-user-login").hide();
$("#ccn-header-user-logout").show();
} else {
$("#ccn-header-nav-home").show();
$("#ccn-header-nav-collection").hide();
$("#ccn-header-nav-calendar").hide();
$("#ccn-header-nav-todo").hide();
$("#ccn-header-nav-admin").hide();
$("#ccn-header-user-login").show();
$("#ccn-header-user-logout").hide();
}
}
// bind language process and internal process function such as logout and expand menu
function ccn_headerNav_BindEvents() {
// bind function
$("#ccn-header-language > *").each(function(){
$(this).click(function(){
ccn_i18n_ChangeLanguage($(this).attr("language"));
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage();
});
});
// bind logout
$("#ccn-header-user-logout").click(function() {
if (ccn_api_common_logout()) {
// ok, logout
// jump into home page again
window.location.href = '/web/home';
return;
} else ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-logout"));
});
// bind burger menu
// copy from bulma website
// Check for click events on the navbar burger icon
$(".navbar-burger").click(function() {
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
$(".navbar-burger").toggleClass("is-active");
$(".navbar-menu").toggleClass("is-active");
});
}

View File

@@ -0,0 +1,89 @@
var ccn_i18n_i18nSupported = ['en-US', 'zh-CN'];
var ccn_i18n_currentLanguage = 'en-US';
var ccn_pages_enumPages = {
home : 0,
calendar: 1,
todo: 2,
admin: 3,
login: 4,
collection: 5,
event: 6
};
var ccn_pages_currentPage = ccn_pages_enumPages.home;
// judge current language
ccn_i18n_currentLanguage = ccn_localstorageAssist_Get('ccn-i18n', 'en-US');
if (ccn_i18n_i18nSupported.indexOf(ccn_i18n_currentLanguage) == -1){
ccn_localstorageAssist_Set('ccn-i18n', 'en-US');
ccn_i18n_currentLanguage = 'en-US';
}
function ccn_i18n_ChangeLanguage(newLang) {
if (ccn_i18n_i18nSupported.indexOf(newLang) == -1) return false;
ccn_i18n_currentLanguage = newLang;
ccn_localstorageAssist_Set('ccn-i18n', newLang);
return true;
}
function ccn_i18n_LoadLanguage() {
$.i18n.properties({
name: 'strings_' + ccn_i18n_currentLanguage,
path: '/static/i18n/',
encoding: 'utf-8',
mode: 'map',
async: false,
cache: false,
language: ccn_i18n_currentLanguage
});
}
function ccn_i18n_ApplyLanguage() {
//set usual block
var cache = $("[i18n-name]");
cache.each(function() {
$(this).html($.i18n.prop($(this).attr('i18n-name')));
});
//set unusual block
//set title
switch(ccn_pages_currentPage) {
case ccn_pages_enumPages.home:
$('#ccn-pageName').html($.i18n.prop('ccn-i18n-pageName-home'));
break;
case ccn_pages_enumPages.calendar:
$('#ccn-pageName').html($.i18n.prop('ccn-i18n-pageName-calendar'));
break;
case ccn_pages_enumPages.todo:
$('#ccn-pageName').html($.i18n.prop('ccn-i18n-pageName-todo'));
break;
case ccn_pages_enumPages.admin:
$('#ccn-pageName').html($.i18n.prop('ccn-i18n-pageName-admin'));
break;
case ccn_pages_enumPages.login:
$('#ccn-pageName').html($.i18n.prop('ccn-i18n-pageName-login'));
break;
case ccn_pages_enumPages.collection:
$('#ccn-pageName').html($.i18n.prop('ccn-i18n-pageName-collection'));
break;
case ccn_pages_enumPages.event:
$('#ccn-pageName').html($.i18n.prop('ccn-i18n-pageName-event'));
break;
}
}
function ccn_i18n_ApplyLanguage2Content(ctx) {
ctx.find("[i18n-name]").each(function() {
$(this).html($.i18n.prop($(this).attr('i18n-name')));
});
}
// note: month is zero based
function ccn_i18n_UniversalGetMonth(month) {
return $.i18n.prop('ccn-i18n-universal-month-' + (month + 1));
}
// note: day of week is zero based
function ccn_i18n_UniversalGetDayOfWeek(dayOfWeek) {
return $.i18n.prop('ccn-i18n-universal-week-' + (dayOfWeek + 1));
}

View File

@@ -0,0 +1,21 @@
function ccn_localstorageAssist_Get(index, defaultValue) {
var cache = localStorage.getItem(index);
if (cache == null) {
ccn_localstorageAssist_Set(index, defaultValue);
return defaultValue;
} else return cache;
}
function ccn_localstorageAssist_Set(index, value) {
localStorage.setItem(index, value);
}
// =================================== seperated data getter setter
function ccn_localstorageAssist_GetApiToken() {
return ccn_localstorageAssist_Get('ccn-token', '');
}
function ccn_localstorageAssist_SetApiToken(value) {
ccn_localstorageAssist_Set('ccn-token', value);
}

View File

@@ -0,0 +1,19 @@
function ccn_messagebox_Insert() {
$('body').append(ccn_template_messagebox.render());
}
function ccn_messagebox_Show(/*title,*/ info) {
//$('#ccn-messagebox-title').text(title);
$('#ccn-messagebox-body').text(info);
$('#ccn-messagebox-modal').addClass('is-active');
}
function ccn_messagebox_BindEvent() {
$('#ccn-messagebox-btnClose').click(ccn_messagebox_Hide);
$('#ccn-messagebox-btnConfirm').click(ccn_messagebox_Hide);
}
function ccn_messagebox_Hide() {
$('#ccn-messagebox-modal').removeClass('is-active');
}

View File

@@ -0,0 +1,265 @@
var ccn_admin_userListCache = [];
var ccn_admin_tokenListCache = [];
$(document).ready(function() {
ccn_pages_currentPage = ccn_pages_enumPages.admin;
// template process
ccn_template_Load();
// nav process
ccn_headerNav_Insert();
ccn_headerNav_BindEvents();
ccn_headerNav_LoggedRefresh();
// messagebox process
ccn_messagebox_Insert();
ccn_messagebox_BindEvent();
// bind tab control switcher and set current tab
$("#tabcontrol-tab-1-1").click(function(){
ccn_tabcontrol_SwitchTab(1, 1);
});
$("#tabcontrol-tab-1-2").click(function(){
ccn_tabcontrol_SwitchTab(1, 2);
});
$("#tabcontrol-tab-1-3").click(function(){
ccn_tabcontrol_SwitchTab(1, 3);
});
ccn_tabcontrol_SwitchTab(1, 1);
// load user tab according to admin status
if(!ccn_api_profile_isAdmin())
$('#tabcontrol-tab-1-3').hide();
// apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage();
// bind event
$('#ccn-admin-profile-btnChangePassword').click(ccn_admin_profile_ChangePassword);
$('#ccn-admin-tokenList-btnRefresh').click(ccn_admin_tokenList_Refresh);
$('#ccn-admin-userList-btnAdd').click(ccn_admin_userList_Add);
$('#ccn-admin-userList-btnRefresh').click(ccn_admin_userList_Refresh);
});
// ================== profile
function ccn_admin_profile_ChangePassword() {
var newpassword = $('#ccn-admin-profile-inputPassword').val();
if (newpassword == "") return;
var result = ccn_api_profile_changePassword(newpassword);
if(result) {
ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-success"));
$('#ccn-admin-profile-inputPassword').val('');
} else
ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-update"));
}
// ================== token
function ccn_admin_tokenList_Refresh() {
ccn_admin_tokenListCache = new Array();
var listDOM = $('#ccn-admin-tokenList');
listDOM.empty();
var renderdata = {
uuid: undefined,
isMe: undefined,
ua: undefined,
ip: undefined,
expireOn: undefined
}
var gottenDateTime = new Date();
var result = ccn_api_profile_getToken();
if(typeof(result) != 'undefined') {
for(var index in result) {
var item = result[index];
renderdata.uuid = item[1];
renderdata.isMe = ccn_localstorageAssist_GetApiToken() == item[1];
renderdata.ua = item[3];
renderdata.ip = item[4];
gottenDateTime.setTime(item[2] * 1000);
renderdata.expireOn = gottenDateTime.toLocaleString();
listDOM.append(ccn_template_tokenItem.render(renderdata));
// bind event
var uuid = renderdata.uuid;
$("#ccn-tokenItem-btnLogout-" + uuid).click(ccn_admin_tokenList_ItemDelete);
// add into cache
ccn_admin_tokenListCache[uuid] = item;
}
ccn_i18n_ApplyLanguage2Content(listDOM);
}
}
function ccn_admin_tokenList_ItemDelete() {
var uuid = $(this).attr("uuid");
var result = ccn_api_profile_deleteToken(uuid);
if(!result) {
// fail
ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-delete"));
} else {
// remove body
$("#ccn-tokenItem-" + uuid).remove();
}
}
// ================== user list
function ccn_admin_userList_RefreshCacheList() {
ccn_admin_userListCache = new Array();
var result = ccn_api_admin_get();
if(typeof(result) != 'undefined') {
for(var index in result) {
ccn_admin_userListCache[index] = result[index];
}
}
}
function ccn_admin_userList_RenderItem(item, index, listDOM) {
var renderdata = {
uuid: index, // use index for uuid. there are no uuid for user
username: item[0]
}
// render
listDOM.append(ccn_template_userItem.render(renderdata));
// set mode
var uuid = index;
ccn_admin_userList_ChangeDisplayMode(uuid, false, item[1])
// bind event
$("#ccn-userItem-btnEdit-" + uuid).click(ccn_admin_userList_ItemEdit);
$("#ccn-userItem-btnDelete-" + uuid).click(ccn_admin_userList_ItemDelete);
$("#ccn-userItem-btnUpdate-" + uuid).click(ccn_admin_userList_ItemUpdate);
$("#ccn-userItem-btnCancelUpdate-" + uuid).click(ccn_admin_userList_ItemCancelUpdate);
}
function ccn_admin_userList_RenderCacheList() {
$('#ccn-admin-userList').empty();
var listDOM = $('#ccn-admin-userList');
for(var index in ccn_admin_userListCache) {
ccn_admin_userList_RenderItem(
ccn_admin_userListCache[index],
index,
listDOM
)
}
ccn_i18n_ApplyLanguage2Content(listDOM);
}
function ccn_admin_userList_ChangeDisplayMode(uuid, isEdit, isAdmin) {
if (typeof(isAdmin) != 'undefined') {
if (isAdmin)
$("#ccn-userItem-iconIsAdmin-" + uuid).show();
else
$("#ccn-userItem-iconIsAdmin-" + uuid).hide();
}
if (typeof(isEdit) != 'undefined') {
if (isEdit) {
$("#ccn-userItem-btnEdit-" + uuid).hide();
$("#ccn-userItem-btnDelete-" + uuid).hide();
$("#ccn-userItem-btnUpdate-" + uuid).show();
$("#ccn-userItem-btnCancelUpdate-" + uuid).show();
$("#ccn-userItem-boxPassword-" + uuid).show();
$("#ccn-userItem-boxIsAdmin-" + uuid).show();
} else {
$("#ccn-userItem-btnEdit-" + uuid).show();
$("#ccn-userItem-btnDelete-" + uuid).show();
$("#ccn-userItem-btnUpdate-" + uuid).hide();
$("#ccn-userItem-btnCancelUpdate-" + uuid).hide();
$("#ccn-userItem-boxPassword-" + uuid).hide();
$("#ccn-userItem-boxIsAdmin-" + uuid).hide();
}
}
}
function ccn_admin_userList_Refresh() {
// refresh and render once
ccn_admin_userList_RefreshCacheList();
ccn_admin_userList_RenderCacheList();
}
function ccn_admin_userList_Add() {
var username = $('#ccn-admin-userList-inputUsername').val();
if (username == "") return;
var result = ccn_api_admin_add(username);
if (typeof(result) == 'undefined') {
ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-add"));
} else {
// render
var index = ccn_admin_userListCache.push(result) - 1;
var listDOM = $('#ccn-admin-userList');
ccn_admin_userList_RenderItem(result, index, listDOM);
ccn_i18n_ApplyLanguage2Content(listDOM);
}
}
function ccn_admin_userList_ItemEdit() {
var uuid = $(this).attr("uuid");
// copy isAdmin to checkbox and clean password box
$('#ccn-userItem-inputIsAdmin-' + uuid).prop("checked", ccn_admin_userListCache[uuid][1]);
$('#ccn-userItem-inputPassword-' + uuid).val('');
// switch to edit mode
ccn_admin_userList_ChangeDisplayMode(uuid, true, undefined);
}
function ccn_admin_userList_ItemDelete() {
var uuid = $(this).attr("uuid");
var result = ccn_api_admin_delete(ccn_admin_userListCache[uuid][0]);
if(!result) {
// fail
ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-delete"));
} else {
// remove body
$("#ccn-userItem-" + uuid).remove();
}
}
function ccn_admin_userList_ItemUpdate() {
var uuid = $(this).attr("uuid");
var newpassword = $('#ccn-userItem-inputPassword-' + uuid).val();
var isAdmin = $('#ccn-userItem-inputIsAdmin-' + uuid).prop("checked");
var result = ccn_api_admin_update(
ccn_admin_userListCache[uuid][0],
newpassword == "" ? undefined : newpassword,
isAdmin == ccn_admin_userListCache[uuid][1] ? undefined : isAdmin);
if (!result) {
// fail
ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-update"));
} else {
// safely update data
ccn_admin_userListCache[uuid][1] = isAdmin
// switch to normal mode
ccn_admin_userList_ChangeDisplayMode(uuid, false, isAdmin);
}
}
function ccn_admin_userList_ItemCancelUpdate() {
var uuid = $(this).attr("uuid");
ccn_admin_userList_ChangeDisplayMode(uuid, false, undefined);
}

View File

@@ -0,0 +1,376 @@
// 2 list which will store sharing and shared collection's display mode.
// key is uuid, value is bool
var ccn_calendar_owned_displayCache = [];
var ccn_calendar_shared_displayCache = [];
// modal editing object.
// undefined mean add
// not undefined mean update(a copy of calendar event)
var ccn_calendar_eventModal_editing = undefined;
var ccn_calendar_eventModal_collectionCache = [];
var ccn_calendar_calendar_listCache = [];
var ccn_calendar_calendar_displayCache = [];
var ccn_calendar_calendar_displayDateTime = 0;
$(document).ready(function() {
ccn_pages_currentPage = ccn_pages_enumPages.calendar;
// template process
ccn_template_Load();
// nav process
ccn_headerNav_Insert();
ccn_headerNav_BindEvents();
ccn_headerNav_LoggedRefresh();
// messagebox process
ccn_messagebox_Insert();
ccn_messagebox_BindEvent();
// process calendar it self
ccn_calendar_calendar_LoadCalendarBody();
// init datetimepicker and preset
ccn_datetimepicker_Insert();
var nowtime = new Date();
ccn_datetimepicker_Set(1, nowtime, false, ccn_datetimepicker_tabType.month);
// bind tab control switcher and set current tab
$("#tabcontrol-tab-1-1").click(function(){
ccn_tabcontrol_SwitchTab(1, 1);
});
$("#tabcontrol-tab-1-2").click(function(){
ccn_tabcontrol_SwitchTab(1, 2);
});
$("#tabcontrol-tab-1-3").click(function(){
ccn_tabcontrol_SwitchTab(1, 3);
});
ccn_tabcontrol_SwitchTab(1, 1);
// apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage();
//refresh once
ccn_calendar_collection_Refresh();
ccn_calendar_calendar_Refresh();
ccn_calendar_calendar_Analyse();
ccn_calendar_calendar_Render();
// bind event
$('#ccn-calendar-collection-btnRefresh').click(ccn_calendar_collection_Refresh);
$('#ccn-calendar-calendar-btnJump')
.prop('funcs', {callback: ccn_calendar_calendar_btnRefresh})
.click(function() {
ccn_datetimepicker_Modal(
ccn_datetimepicker_tabType.month,
1,
false);
});
$('#ccn-calendar-calendar-btnToday').click(ccn_calendar_calendar_btnToday);
$('#ccn-calendar-calendar-btnAdd').click(ccn_calendar_calendar_btnAdd);
});
// ================== calendar
function ccn_calendar_calendar_LoadCalendarBody() {
$('#ccn-calendar-calendarBody').append(ccn_template_calendarItem.render());
}
// this function only refresh cache list
function ccn_calendar_calendar_Refresh() {
var gottenDateTime = ccn_datetimepicker_Get(1, false);
var gottenYear = gottenDateTime.getFullYear();
var gottenMonth = gottenDateTime.getMonth() + 1;
$('#ccn-calendar-calendar-textMonth').text('{0} - {1}'.format(gottenYear, ccn_i18n_UniversalGetMonth(gottenMonth - 1)));
// don't need to set anything, because its default value is enough to use.
var gottenWeek = ccn_datetime_DayOfWeek(gottenYear, gottenMonth, 1);
var startTimestamp = Math.floor(gottenDateTime.getTime() / 60000) - gottenWeek * ccn_datetime_DAY1_SPAN;
var endTimestamp = startTimestamp + ccn_datetime_DAY1_SPAN * 6 * 7 - 1;
ccn_calendar_calendar_listCache = new Array();
var result = ccn_api_calendar_getFull(startTimestamp, endTimestamp);
if (typeof(result) != 'undefined') {
for(var index in result) {
ccn_calendar_calendar_listCache[result[index][0]] = result[index];
}
}
}
// this function take responsibility to analyse event
// call datetime function to resolve loop event
// and split event if some event cross 2+ days
function ccn_calendar_calendar_Analyse() {
// first, we need construct ccn_calendar_calendar_displayCache
ccn_calendar_calendar_displayCache = new Array();
var gottenDateTime = ccn_datetimepicker_Get(1, false);
var gottenYear = gottenDateTime.getFullYear();
var gottenMonth = gottenDateTime.getMonth() + 1;
var gottenWeek = ccn_datetime_DayOfWeek(gottenYear, gottenMonth, 1);
var startTimestamp = Math.floor(gottenDateTime.getTime() / 60000) - gottenWeek * ccn_datetime_DAY1_SPAN;
var endTimestamp = startTimestamp + ccn_datetime_DAY1_SPAN * 6 * 7 - 1;
gottenDateTime.setTime(startTimestamp * 60000);
for(var index = 0; index < 6 * 7; index++) {
ccn_calendar_calendar_displayCache.push({
month: gottenDateTime.getMonth() + 1,
day: gottenDateTime.getDate(),
dayOfWeek: gottenDateTime.getWeekday() + 1,
subcalendar: "",
isCurrentMonth: (gottenDateTime.getMonth() + 1) == gottenMonth,
events: new Array()
});
gottenDateTime.setTime(gottenDateTime.getTime() + ccn_datetime_DAY1_SPAN * 60000);
}
var mytimezone = -(new Date().getTimezoneOffset());
// then analyse each event
for(var index in ccn_calendar_calendar_listCache) {
var item = ccn_calendar_calendar_listCache[index];
var deserializedDescription = ccn_api_deserializeDescription(item[3]);
var minStartTimestamp = startTimestamp - (item[6] - item[5]);
var result = ccn_datetime_ResolveLoopRules4Event(
item[8],
item[9] < minStartTimestamp ? minStartTimestamp : item[9],
Math.min(item[10], endTimestamp),
item[5],
item[6],
item[7],
startTimestamp
);
if(typeof(result) != 'undefined') {
for(var i in result) {
var it = result[i];
// try get event belong to which cell
var eventDateTime = new Date(it[0] * 60000);
var count = Math.floor((it[0] - startTimestamp) / ccn_datetime_DAY1_SPAN);
var exitFlag = false;
// then split event
while(count < 6 * 7) {
var eventItem = {
uuid: item[0],
belongTo: item[1],
title: item[2],
description: deserializedDescription.description,
color: deserializedDescription.color,
isVisible: true,
isLocked: typeof(ccn_calendar_owned_displayCache[item[0]]) != 'undefined',
loopText: ccn_datetime_ResolveLoopRules4Text(item[8], item[5], item[7]),
timezoneWarning: mytimezone != item[7],
start: eventDateTime.toLocaleTimeString(),
end: undefined // filled in follwing code
}
eventDateTime.setHours(23, 59, 0, 0);
if (it[1] <= Math.floor(eventDateTime.getTime() / 60000)) {
exitFlag = true;
eventDateTime.setTime(it[1] * 60000);
}
eventItem.end = eventDateTime.toLocaleTimeString();
ccn_calendar_calendar_displayCache[count].events.push(eventItem);
if (exitFlag) break;
else eventDateTime.setMinutes(eventDateTime.getMinutes() + 1, 0, 0);
count++;
}
}
}
}
}
// just use produced ccn_calendar_calendar_displayCache
// to re-generate ui
function ccn_calendar_calendar_Render() {
// todo: add / migrate subcalendar feature here
// analyse visible data
for(var i in ccn_calendar_calendar_displayCache) {
for(var j in ccn_calendar_calendar_displayCache[i].events) {
var gottenOwnedVisible = ccn_calendar_owned_displayCache[
ccn_calendar_calendar_displayCache[i].events[j].belongTo
];
if (typeof(gottenOwnedVisible) == 'undefined') gottenOwnedVisible = false;
var gottenSharedVisible = ccn_calendar_shared_displayCache[
ccn_calendar_calendar_displayCache[i].events[j].belongTo
];
if (typeof(gottenSharedVisible) == 'undefined') gottenSharedVisible = false;
ccn_calendar_calendar_displayCache[i].events[j].isVisible = gottenOwnedVisible || gottenSharedVisible;
}
}
// just render them
var listDOM = $('#ccn-calendar-scheduleList');
listDOM.empty();
listDOM.append(ccn_template_scheduleItem.render({renderdata: ccn_calendar_calendar_displayCache}));
// link click event
$('div.schedule-event-outter').click(ccn_calendar_calendar_ItemUpdate);
// all data has been alanysed, feedback to calendar body.
var counter = 0;
for(var i = 0; i < 6; i++) {
for(var j = 0; j < 7; j++) {
var item = ccn_calendar_calendar_displayCache[counter];
var lenEvents = item.events.length;
var eventsCounter = 0;
$('#ccn-calendarItem-' + i + '-' + j).attr('isCurrentMonth', item.isCurrentMonth ? 'true' : 'false');
$('#ccn-calendarItem-title-' + i + '-' + j).text(item.day);
$('#ccn-calendarItem-desc-' + i + '-' + j).text(item.subcalendar);
for(; eventsCounter < Math.min(lenEvents, 4); eventsCounter++) {
$('#ccn-calendarItem-eventBox' + (eventsCounter + 1) + '-' + i + '-' + j)
.css('background', item.events[eventsCounter].color)
.attr('enableDisplay', 'true');
}
if (lenEvents > 4) {
// more than 4 item, write number
$('#ccn-calendarItem-task-' + i + '-' + j).text(
$.i18n.prop('ccn-i18n-calendar-calendar-stripedEvents').format(lenEvents.toString())
);
} else {
// otherwise, wipe out number
$('#ccn-calendarItem-task-' + i + '-' + j).html('&nbsp;');
// set others div are blank
for(; eventsCounter < 4; eventsCounter++) {
$('#ccn-calendarItem-eventBox' + (eventsCounter + 1) + '-' + i + '-' + j)
.attr('enableDisplay', 'false');
}
}
counter++;
}
}
ccn_i18n_ApplyLanguage2Content(listDOM);
}
function ccn_calendar_calendar_btnRefresh() {
ccn_calendar_calendar_Refresh();
ccn_calendar_calendar_Analyse();
ccn_calendar_calendar_Render();
}
function ccn_calendar_calendar_btnToday() {
var nowtime = new Date();
ccn_datetimepicker_Set(1, nowtime, false, ccn_datetimepicker_tabType.month);
ccn_calendar_calendar_Refresh();
ccn_calendar_calendar_Analyse();
ccn_calendar_calendar_Render();
}
function ccn_calendar_calendar_btnAdd() {
window.location.href = '/web/eventAdd';
}
function ccn_calendar_calendar_ItemUpdate() {
var uuid = $(this).attr("uuid");
window.location.href = '/web/eventUpdate/' + uuid;
}
// ============================= collection
function ccn_calendar_collection_Refresh() {
ccn_calendar_owned_displayCache = new Array();
ccn_calendar_shared_displayCache = new Array();
// render shared
var result = ccn_api_collection_getShared();
var listDOM = $('#ccn-calendar-sharedList');
listDOM.empty();
var renderdata = {
uuid: undefined,
name: undefined,
username: undefined
}
if (typeof(result) != 'undefined') {
for(var index in result) {
var item = result[index];
renderdata.uuid = item[0];
renderdata.name = item[1];
renderdata.username = item[2];
listDOM.append(ccn_template_displaySharedItem.render(renderdata));
// change display
var uuid = renderdata.uuid;
ccn_calendar_shared_ChangeDisplayMode(uuid, true);
// push into display list
ccn_calendar_shared_displayCache[uuid] = true;
// bind event
$('#ccn-displaySharedItem-btnHide-' + uuid).click(ccn_calendar_shared_ItemSwitchDisplay);
$('#ccn-displaySharedItem-btnShow-' + uuid).click(ccn_calendar_shared_ItemSwitchDisplay);
}
}
ccn_i18n_ApplyLanguage2Content(listDOM);
// render owned
result = ccn_api_collection_getFullOwn();
listDOM = $('#ccn-calendar-ownedList');
listDOM.empty();
renderdata = {
uuid: undefined,
name: undefined
}
if (typeof(result) != 'undefined') {
for(var index in result) {
var item = result[index];
renderdata.uuid = item[0];
renderdata.name = item[1];
// render
listDOM.append(ccn_template_displayOwnedItem.render(renderdata));
// set mode
var uuid = renderdata.uuid;
ccn_calendar_owned_ChangeDisplayMode(uuid, true);
// push into display list
ccn_calendar_owned_displayCache[uuid] = true;
// bind event
$('#ccn-displayOwnedItem-btnHide-' + uuid).click(ccn_calendar_owned_ItemSwitchDisplay);
$('#ccn-displayOwnedItem-btnShow-' + uuid).click(ccn_calendar_owned_ItemSwitchDisplay);
}
}
}
function ccn_calendar_owned_ItemSwitchDisplay() {
var uuid = $(this).attr("uuid");
ccn_calendar_owned_displayCache[uuid] = !(ccn_calendar_owned_displayCache[uuid]);
ccn_calendar_owned_ChangeDisplayMode(uuid, ccn_calendar_owned_displayCache[uuid]);
}
function ccn_calendar_shared_ItemSwitchDisplay() {
var uuid = $(this).attr("uuid");
ccn_calendar_shared_displayCache[uuid] = !(ccn_calendar_shared_displayCache[uuid]);
ccn_calendar_shared_ChangeDisplayMode(uuid, ccn_calendar_shared_displayCache[uuid]);
}
function ccn_calendar_shared_ChangeDisplayMode(uuid, isShow) {
if (isShow) {
$('#ccn-displaySharedItem-btnHide-' + uuid).show();
$('#ccn-displaySharedItem-btnShow-' + uuid).hide();
} else {
$('#ccn-displaySharedItem-btnHide-' + uuid).hide();
$('#ccn-displaySharedItem-btnShow-' + uuid).show();
}
}
function ccn_calendar_owned_ChangeDisplayMode(uuid, isShow) {
if (isShow) {
$('#ccn-displayOwnedItem-btnHide-' + uuid).show();
$('#ccn-displayOwnedItem-btnShow-' + uuid).hide();
} else {
$('#ccn-displayOwnedItem-btnHide-' + uuid).hide();
$('#ccn-displayOwnedItem-btnShow-' + uuid).show();
}
}

View File

@@ -0,0 +1,288 @@
// 3 used cache list
var ccn_collection_owned_listCache = [];
var ccn_collection_sharing_listCache = [];
// current editing sharing collection
var ccn_collection_sharing_editingOwned = undefined; // the uuid of owned collection
$(document).ready(function() {
ccn_pages_currentPage = ccn_pages_enumPages.collection;
// template process
ccn_template_Load();
// nav process
ccn_headerNav_Insert();
ccn_headerNav_BindEvents();
ccn_headerNav_LoggedRefresh();
// messagebox process
ccn_messagebox_Insert();
ccn_messagebox_BindEvent();
// apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage();
//refresh once
ccn_collection_owned_Refresh();
// bind event
//$('#ccn-calendar-shared-btnRefresh').click(ccn_calendar_shared_Refresh);
$('#ccn-collection-owned-btnAdd').click(ccn_collection_owned_Add);
$('#ccn-collection-owned-btnRefresh').click(ccn_collection_owned_Refresh);
$('#ccn-collection-sharing-btnAdd').click(ccn_collection_sharing_Add);
$('#ccn-collection-sharing-btnRefresh').click(ccn_collection_sharing_Refresh);
});
function ccn_collection_owned_Refresh() {
ccn_collection_owned_listCache = new Array();
ccn_collection_sharing_displayCache = new Array();
var result = ccn_api_collection_getFullOwn();
if(typeof(result) != 'undefined') {
for(var index in result) {
ccn_collection_owned_listCache[result[index][0]] = result[index];
}
}
// render
var listDOM = $('#ccn-collection-ownedList');
listDOM.empty();
for(var index in ccn_collection_owned_listCache) {
ccn_collection_owned_RenderItem(
ccn_collection_owned_listCache[index],
listDOM
);
}
// also, order sharing list clean
ccn_collection_sharing_editingOwned = undefined;
ccn_collection_sharing_Refresh();
}
function ccn_collection_owned_RenderItem(item, listDOM) {
var renderdata = {
uuid: item[0],
name: item[1]
}
// render
listDOM.append(ccn_template_ownedItem.render(renderdata));
// set mode
var uuid = renderdata.uuid;
ccn_collection_owned_ChangeDisplayMode(uuid, false);
// bind event
$('#ccn-ownedItem-btnEdit-' + uuid).click(ccn_collection_owned_ItemEdit);
$('#ccn-ownedItem-btnDelete-' + uuid).click(ccn_collection_owned_ItemDelete);
$('#ccn-ownedItem-btnShare-' + uuid).click(ccn_collection_owned_ItemShare);
$('#ccn-ownedItem-btnUpdate-' + uuid).click(ccn_collection_owned_ItemUpdate);
$('#ccn-ownedItem-btnCancelUpdate-' + uuid).click(ccn_collection_owned_ItemCancelUpdate);
}
function ccn_collection_owned_ChangeDisplayMode(uuid, isEdit) {
if (isEdit) {
$('#ccn-ownedItem-btnEdit-' + uuid).hide();
$('#ccn-ownedItem-btnShare-' + uuid).hide();
$('#ccn-ownedItem-btnDelete-' + uuid).hide();
$('#ccn-ownedItem-btnUpdate-' + uuid).show();
$('#ccn-ownedItem-btnCancelUpdate-' + uuid).show();
$('#ccn-ownedItem-textName-' + uuid).hide();
$('#ccn-ownedItem-boxName-' + uuid).show();
} else {
$('#ccn-ownedItem-btnEdit-' + uuid).show();
$('#ccn-ownedItem-btnShare-' + uuid).show();
$('#ccn-ownedItem-btnDelete-' + uuid).show();
$('#ccn-ownedItem-btnUpdate-' + uuid).hide();
$('#ccn-ownedItem-btnCancelUpdate-' + uuid).hide();
$('#ccn-ownedItem-textName-' + uuid).show();
$('#ccn-ownedItem-boxName-' + uuid).hide();
}
}
function ccn_collection_sharing_Refresh() {
ccn_collection_sharing_listCache = new Array();
if (typeof(ccn_collection_sharing_editingOwned) != 'undefined') {
var result = ccn_api_collection_getSharing(ccn_collection_sharing_editingOwned);
if (typeof(result) != 'undefined') {
for(var index in result) {
ccn_collection_sharing_listCache[index] = result[index];
// also, sharingTarget don't have uuid, use index instead
}
}
}
// update editing text
$('#ccn-collection-sharing-sharingEditing').text(
typeof(ccn_collection_sharing_editingOwned) == 'undefined' ?
'' :
ccn_collection_owned_listCache[ccn_collection_sharing_editingOwned][1]
);
// if editing is undefined, hide container
if (typeof(ccn_collection_sharing_editingOwned) == 'undefined')
$('#ccn-collection-sharing-container').hide();
else
$('#ccn-collection-sharing-container').show();
var listDOM = $('#ccn-collection-sharingList');
listDOM.empty();
for(var index in ccn_collection_sharing_listCache) {
ccn_collection_sharing_RenderItem(
ccn_collection_sharing_listCache[index],
index,
listDOM
)
}
}
function ccn_collection_sharing_RenderItem(item, index, listDOM) {
var renderdata = {
uuid: index,
username: item
}
// render
listDOM.append(ccn_template_sharingItem.render(renderdata));
// bind event
var uuid = index;
$("#ccn-sharingItem-btnDelete-" + uuid).click(ccn_collection_sharing_ItemDelete);
}
// ========================= input operation
function ccn_collection_owned_Add() {
var newname = $('#ccn-collection-owned-inputAdd').val();
if (newname == "") return;
var result = ccn_api_collection_addOwn(newname);
if (typeof(result) == 'undefined') ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-add"));
else {
// second get. get detail
result = ccn_api_collection_getDetailOwn(result);
if (typeof(result) == 'undefined') ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-get"));
else {
// render
ccn_collection_owned_listCache[result[0]] = result;
var listDOM = $('#ccn-collection-ownedList');
ccn_collection_owned_RenderItem(result, listDOM);
}
}
}
function ccn_collection_owned_ItemEdit() {
var uuid = $(this).attr("uuid");
// preset inputbox
$('#ccn-ownedItem-inputName-' + uuid).val(
ccn_collection_owned_listCache[uuid][1]
);
// switch to edit mode
ccn_collection_owned_ChangeDisplayMode(uuid, true);
}
function ccn_collection_owned_ItemDelete() {
var uuid = $(this).attr("uuid");
var result = ccn_api_collection_deleteOwn(
uuid,
ccn_collection_owned_listCache[uuid][2]
);
if (!result) ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-delete"));
else {
$('#ccn-ownedItem-' + uuid).remove();
// also, we should notice sharing target, and try clean it
if (ccn_collection_sharing_editingOwned == uuid) {
ccn_collection_sharing_editingOwned = undefined;
ccn_collection_sharing_Refresh();
}
}
}
function ccn_collection_owned_ItemUpdate() {
var uuid = $(this).attr("uuid");
var newname = $('#ccn-ownedItem-inputName-' + uuid).val();
var result = ccn_api_collection_updateOwn(uuid, newname, ccn_collection_owned_listCache[uuid][2]);
if (typeof(result) == 'undefined') ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-update"));
else {
// update last change
ccn_collection_owned_listCache[uuid][2] = result;
ccn_collection_owned_listCache[uuid][1] = newname;
// update elements
$('#ccn-ownedItem-textName-' + uuid).text(newname);
// if editing, update sharing target
if (ccn_collection_sharing_editingOwned == uuid)
ccn_collection_sharing_Refresh();
// back to normal mode
ccn_collection_owned_ChangeDisplayMode(uuid, false);
}
}
function ccn_collection_owned_ItemCancelUpdate() {
var uuid = $(this).attr("uuid");
ccn_collection_owned_ChangeDisplayMode(uuid, false);
}
function ccn_collection_owned_ItemShare() {
var uuid = $(this).attr("uuid");
ccn_collection_sharing_editingOwned = uuid;
ccn_collection_sharing_Refresh();
}
function ccn_collection_sharing_Add() {
var newusername = $('#ccn-collection-sharing-inputAdd').val();
if (newusername == "" || typeof(ccn_collection_sharing_editingOwned) == 'undefined') return;
var result = ccn_api_collection_addSharing(
ccn_collection_sharing_editingOwned,
newusername,
ccn_collection_owned_listCache[ccn_collection_sharing_editingOwned][2]
);
if (typeof(result) == 'undefined') ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-add"));
else {
// add new item
var index = ccn_collection_sharing_listCache.push(newusername) - 1;
var listDOM = $('#ccn-collection-sharingList');
ccn_collection_sharing_RenderItem(newusername, index, listDOM);
// update last change
ccn_collection_owned_listCache[ccn_collection_sharing_editingOwned][2] = result;
}
}
function ccn_collection_sharing_ItemDelete() {
var uuid = $(this).attr("uuid");
var username = ccn_collection_sharing_listCache[uuid];
var result = ccn_api_collection_deleteSharing(
ccn_collection_sharing_editingOwned,
username,
ccn_collection_owned_listCache[ccn_collection_sharing_editingOwned][2]
);
if (typeof(result) == 'undefined') ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-delete"));
else {
// remove item in ui
$('#ccn-sharingItem-' + uuid).remove();
// update last change
ccn_collection_owned_listCache[ccn_collection_sharing_editingOwned][2] = result;
}
}

View File

@@ -0,0 +1,447 @@
// if it is undefined, current mode is add
// or it is the detail data gotten from api
var ccn_event_editingEvent = undefined;
var ccn_event_collectionCache = [];
$(document).ready(function() {
ccn_pages_currentPage = ccn_pages_enumPages.event;
// template process
ccn_template_Load();
// nav process
ccn_headerNav_Insert();
ccn_headerNav_BindEvents();
ccn_headerNav_LoggedRefresh();
// messagebox process
ccn_messagebox_Insert();
ccn_messagebox_BindEvent();
// init datetimepicker
ccn_datetimepicker_Insert();
// apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage();
// bind event
$('input[type=radio][name=loop-method]').click(ccn_event_RefreshRadioDiaplay);
$('input[type=radio][name=loop-end]').click(ccn_event_RefreshRadioDiaplay);
$('#ccn-event-btnSubmit').click(ccn_event_btnSubmit);
$('#ccn-event-btnCancel').click(ccn_event_btnCancel);
$('#ccn-event-btnSpot').click(ccn_event_btnSpot);
$('#ccn-event-btnFullDay').click(ccn_event_btnFullDay);
$('#ccn-event-btnStartDateTime')
.prop('funcs', {callback: function() {
ccn_event_UpdateDateTimePickerButton(1);
ccn_event_RefreshLoopMonthType();
}})
.click(ccn_event_btnDateTimePicker);
$('#ccn-event-btnEndDateTime')
.prop('funcs', {callback: function() {ccn_event_UpdateDateTimePickerButton(2);}})
.click(ccn_event_btnDateTimePicker);
$('#ccn-event-btnLoopStopDateTime')
.prop('funcs', {callback: function() {ccn_event_UpdateDateTimePickerButton(3);}})
.click(ccn_event_btnDateTimePicker);
// init form
ccn_event_Init();
// refresh once
ccn_event_RefreshRadioDiaplay();
ccn_event_RefreshLoopMonthType();
});
function ccn_event_Init() {
// we need init some elements first
// we need all radio and checkbox's checked is false, not undefined.
$('input[type=radio]').prop("checked", false);
$('input[type=checkbox]').prop("checked", false);
// init span picker
$('.spanpicker').attr('max', 100)
.attr('min', 1)
.attr('step', 1)
.val(1);
// in there, we need get uuid from meta
var uuid = $('meta[name=uuid]').attr('content');
if (uuid != "")
ccn_event_editingEvent = ccn_api_calendar_getDetail(uuid);
// if ccn_event_editingEvent is undefined, init following content with add mode
// otherwise, init as update mode
var isAdd = typeof(ccn_event_editingEvent) == 'undefined';
var deserializeDescription = isAdd ? undefined : ccn_api_deserializeDescription(ccn_event_editingEvent[3]);
// init title and description
$('#ccn-event-inputTitle').val(
isAdd ? '' : ccn_event_editingEvent[2]
);
$('#ccn-event-inputDescription').val(
isAdd ? '' : deserializeDescription.description
);
$('#ccn-event-inputColor').val(
isAdd ? DefaultColor : deserializeDescription.color
);
// init collection picker, first we need query data
// and render it
var collectionDOM = $('#ccn-event-inputCollection');
collectionDOM.empty();
ccn_event_collectionCache = new Array();
var result = ccn_api_collection_getFullOwn();
if (typeof(result) != 'undefined') {
var renderdata = {
val: undefined,
name: undefined
}
for (var index in result) {
var item = result[index];
ccn_event_collectionCache.push(item[0])
renderdata.val = item[0];
renderdata.name = item[1];
collectionDOM.append(
ccn_template_optionItem.render(renderdata)
);
}
}
// in add mode, set as -1, otherwise try to match original data
// indexOf will return -1 if no matched item
collectionDOM.val(isAdd ? '' : ccn_event_editingEvent[1]);
// init start and end datetime
if (isAdd) {
// in add mode, init 2 datetime picker as close hours based time.
var currentDateTime = new Date();
currentDateTime.setMilliseconds(0);
currentDateTime.setSeconds(0);
currentDateTime.setMinutes(0);
ccn_datetimepicker_Set(1, currentDateTime, false);
// time span is 2 hours
currentDateTime.setHours(currentDateTime.getHours() + 2);
ccn_datetimepicker_Set(2, currentDateTime, false);
} else {
// in update mode, match it with original data
var originalDateTime = new Date((ccn_event_editingEvent[5] + ccn_event_editingEvent[7]) * 60000);
ccn_datetimepicker_Set(1, originalDateTime, true);
originalDateTime = new Date((ccn_event_editingEvent[6] + ccn_event_editingEvent[7]) * 60000);
ccn_datetimepicker_Set(2, originalDateTime, true);
}
// setup timezone here
// to prevent some error
// because following isAdd will change its meaning
$('#ccn-event-timezone-radioKeep').prop('checked', true); // give a default value
var nowtime = new Date();
SmarterShowHide(
(!isAdd) && (-nowtime.getTimezoneOffset()) != ccn_event_editingEvent[7],
$('#ccn-event-boxTimezone')
);
// ========================
// now we need resolve loop rules and set related data
if (!isAdd) {
data = ccn_datetime_ResolveLoopRules4UI(ccn_event_editingEvent[8]);
if (typeof(data) == 'undefined') isAdd = true; // init as add
}
// give some value with a default value
$('#ccn-event-loopMonth-radioA').prop('checked', true);
$('#ccn-event-loopWeek-check' + (nowtime.getWeekday() + 1)).prop('checked', true);
$('#ccn-event-strictMode-radioStrict').prop('checked', true);
// real process
if (isAdd) {
$('#ccn-event-radioLoopNever').prop('checked', true);
} else {
switch(data[0][0]) {
case 0:
$('#ccn-event-radioLoopYear').prop('checked', true);
$('#ccn-event-loopYear-inputSpan').val(data[0][2]);
if (data[0][1]) $('#ccn-event-strictMode-radioStrict').prop('checked', true);
else $('#ccn-event-strictMode-radioRough').prop('checked', true);
break;
case 1:
$('#ccn-event-radioLoopMonth').prop('checked', true);
$('#ccn-event-loopMonth-inputSpan').val(data[0][3]);
$('#ccn-event-loopMonth-radio' + data[0][2]).prop('checked', true);
if (data[0][1]) $('#ccn-event-strictMode-radioStrict').prop('checked', true);
else $('#ccn-event-strictMode-radioRough').prop('checked', true);
break;
case 2:
$('#ccn-event-radioLoopWeek').prop('checked', true);
$('#ccn-event-loopWeek-inputSpan').val(data[0][8]);
for(var i = 1; i <= 7; i++) {
$('#ccn-event-loopWeek-check' + i).prop('checked', data[0][i]);
}
break;
case 3:
$('#ccn-event-radioLoopDay').prop('checked', true);
$('#ccn-event-loopDay-inputSpan').val(data[0][1]);
break;
}
}
// give some item a default value
ccn_datetimepicker_Set(3, nowtime, false);
if (isAdd) {
$('#ccn-event-loopStop-radioForever').prop('checked', true);
} else {
switch(data[1][0]) {
case 0:
$('#ccn-event-loopStop-radioForever').prop('checked', true);
break;
case 1:
$('#ccn-event-loopStop-radioDateTime').prop('checked', true);
var stopDatetime = new Date((data[1][1] + ccn_event_editingEvent[7]) * 60000);
ccn_datetimepicker_Set(3, stopDatetime, true);
break;
case 2:
$('#ccn-event-loopStop-radioTimes').prop('checked', true);
$('#ccn-event-loopStop-inputTimes').val(data[1][1]);
break;
}
}
}
// refresh some ui element according to form options
function ccn_event_RefreshRadioDiaplay() {
// loop method
// note: no loop control loop stop's display
// note: year and month loop also control strict mode display
SmarterShowHide(!$('#ccn-event-radioLoopNever').prop('checked'), $('#ccn-event-boxLoopStop'));
SmarterShowHide($('#ccn-event-radioLoopDay').prop('checked'), $('#ccn-event-boxLoopDay'));
SmarterShowHide($('#ccn-event-radioLoopWeek').prop('checked'), $('#ccn-event-boxLoopWeek'));
SmarterShowHide($('#ccn-event-radioLoopMonth').prop('checked'), $('#ccn-event-boxLoopMonth'));
SmarterShowHide($('#ccn-event-radioLoopYear').prop('checked'), $('#ccn-event-boxLoopYear'));
SmarterShowHide(
$('#ccn-event-radioLoopMonth').prop('checked') || $('#ccn-event-radioLoopYear').prop('checked'),
$('#ccn-event-boxStrictMode')
);
// loop stop
SmarterShowHide($('#ccn-event-loopStop-radioForever').prop('checked'), undefined);
SmarterShowHide($('#ccn-event-loopStop-radioDateTime').prop('checked'), $('#ccn-event-boxLoopStopDateTime'));
SmarterShowHide($('#ccn-event-loopStop-radioTimes').prop('checked'), $('#ccn-event-boxLoopStopTimes'));
}
function ccn_event_RefreshLoopMonthType() {
var picker = ccn_datetimepicker_Get(1, false);
var data = ccn_datetime_GetDayInMonth(picker.getFullYear(), picker.getMonth() + 1, picker.getDate());
$('#ccn-event-loopMonth-textA').text($.i18n.prop('ccn-i18n-event-loopWeek-optionA').format(data[0]));
$('#ccn-event-loopMonth-textB').text($.i18n.prop('ccn-i18n-event-loopWeek-optionB').format(data[1]));
$('#ccn-event-loopMonth-textC').text($.i18n.prop('ccn-i18n-event-loopWeek-optionC').format(data[2], data[3] + 1));
$('#ccn-event-loopMonth-textD').text($.i18n.prop('ccn-i18n-event-loopWeek-optionD').format(data[4], data[5] + 1));
}
function ccn_event_UpdateDateTimePickerButton(index) {
switch(index) {
case 1:
$('#ccn-event-btnStartDateTime-text').text(
ccn_datetimepicker_Get(1, false).toLocaleString()
);
break;
case 2:
$('#ccn-event-btnEndDateTime-text').text(
ccn_datetimepicker_Get(2, false).toLocaleString()
);
break;
case 3:
$('#ccn-event-btnLoopStopDateTime-text').text(
ccn_datetimepicker_Get(3, false).toLocaleDateString()
);
break;
}
}
// return undefined to indicate an error
// or
// [belongTo, title, description, eventDateTimeStart, eventDateTimeEnd, timezoneOffset, loopRules]
function ccn_event_GetForm() {
// basic
var title = $('#ccn-event-inputTitle').val();
if (title == '') return undefined;
var description = $('#ccn-event-inputDescription').val();
if (description == '') return undefined;
var color = $('#ccn-event-inputColor').val();
if (color == '') return undefined;
var belongTo = $('#ccn-event-inputCollection').val();
if (belongTo == null) return undefined; // if no selected item, val return null, not undefined
var isAdd = typeof(ccn_event_editingEvent) == 'undefined';
var keepTimezone = $('#ccn-event-timezone-radioKeep').prop('checked');
var isStrict = $('#ccn-event-strictMode-radioStrict').prop('checked');
// time
var eventDateTimeStart = undefined;
var eventDateTimeEnd = undefined;
var timezoneOffset = undefined;
if ((!isAdd) && (!keepTimezone)) {
// get datetime as utc, then minus original timezone to get unix timestamp
timezoneOffset = ccn_event_editingEvent[7]; // keep timezone
eventDateTimeStart = Math.floor(ccn_datetimepicker_Get(1, true).getTime() / 60000) - timezoneOffset;
eventDateTimeEnd = Math.floor(ccn_datetimepicker_Get(2, true).getTime() / 60000) - timezoneOffset;
} else {
// use my timezone, resolve presented data as my local time
var cache = ccn_datetimepicker_Get(1, false);
timezoneOffset = -cache.getTimezoneOffset();
eventDateTimeStart = Math.floor(cache.getTime() / 60000);
eventDateTimeEnd = Math.floor(ccn_datetimepicker_Get(2, false).getTime() / 60000);
}
// loopRules
var loopRules = undefined;
if ($('#ccn-event-radioLoopNever').prop('checked')) {
loopRules = "";
} else if ($('#ccn-event-radioLoopDay').prop('checked')) {
loopRules = "D{0}".format($('#ccn-event-loopDay-inputSpan').val());
} else if ($('#ccn-event-radioLoopWeek').prop('checked')) {
var cache = ""
for(var i = 1; i < 8; i++)
cache += $('#ccn-event-loopWeek-check' + i).prop('checked') ? 'T' : 'F';
loopRules = 'W{0}{1}'.format(
cache,
$('#ccn-event-loopWeek-inputSpan').val()
);
} else if ($('#ccn-event-radioLoopMonth').prop('checked')) {
var cache = undefined;
if ($('#ccn-event-loopMonth-radioA').prop('checked')) cache='A';
else if ($('#ccn-event-loopMonth-radioB').prop('checked')) cache='B';
else if ($('#ccn-event-loopMonth-radioC').prop('checked')) cache='C';
else if ($('#ccn-event-loopMonth-radioD').prop('checked')) cache='D';
else return undefined;
loopRules = "M{0}{1}{2}".format(
isStrict ? "S" : "R",
cache,
$('#ccn-event-loopMonth-inputSpan').val()
);
} else if ($('#ccn-event-radioLoopYear').prop('checked')) {
loopRules = "Y{0}{1}".format(
isStrict ? "S" : "R",
$('#ccn-event-loopYear-inputSpan').val()
);
}
// no need to process stop if this is not a loop event
if (loopRules != "") {
loopRules += '-';
if ($('#ccn-event-loopStop-radioForever').prop('checked')) {
loopRules += 'F';
} else if ($('#ccn-event-loopStop-radioDateTime').prop('checked')) {
var timestamp = undefined;
if ((!isAdd) && (!keepTimezone)) {
// keep timezone
var cache = ccn_datetimepicker_Get(3, true);
cache.setUTCHours(23);
cache.setUTCMinutes(59);
timestamp = Math.floor(cache.getTime() / 60000) - timezoneOffset;
} else {
// use my timezone
timestamp = Math.floor(ccn_datetimepicker_Get(3, false).getTime() / 60000);
}
loopRules += 'D{0}'.format(timestamp);
} else if ($('#ccn-event-loopStop-radioTimes').prop('checked')) {
loopRules += 'T{0}'.format($('#ccn-event-loopStop-inputTimes').val());
}
}
return [
belongTo,
title,
ccn_api_serializeDescription(
description,
color
),
eventDateTimeStart,
eventDateTimeEnd,
timezoneOffset,
loopRules
];
}
function ccn_event_btnSpot() {
var datetime = ccn_datetimepicker_Get(1, false);
datetime.setMinutes(datetime.getMinutes() + 1);
ccn_datetimepicker_Set(2, datetime, false);
}
function ccn_event_btnFullDay() {
var datetime = ccn_datetimepicker_Get(1, false);
datetime.setMinutes(0);
datetime.setHours(0);
ccn_datetimepicker_Set(1, datetime, false);
datetime.setMinutes(59);
datetime.setHours(23);
ccn_datetimepicker_Set(2, datetime, false);
}
function ccn_event_btnDateTimePicker() {
switch(parseInt($(this).attr('datetimepicker'))) {
case 1:
ccn_datetimepicker_Modal(ccn_datetimepicker_tabType.minute, 1, false);
break;
case 2:
ccn_datetimepicker_Modal(ccn_datetimepicker_tabType.minute, 2, false);
break;
case 3:
ccn_datetimepicker_Modal(ccn_datetimepicker_tabType.day, 3, false);
break;
}
}
function ccn_event_btnCancel() {
window.location.href = '/web/calendar';
}
function ccn_event_btnSubmit() {
var submitData = ccn_event_GetForm();
if (typeof(submitData) == 'undefined') {
ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-form"));
return;
}
var isAdd = typeof(ccn_event_editingEvent) == 'undefined';
if (isAdd) {
var result = ccn_api_calendar_add(
submitData[0],
submitData[1],
submitData[2],
submitData[3],
submitData[4],
submitData[6],
submitData[5]
);
if (typeof(result) == 'undefined') ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-add"));
else window.location.href = '/web/calendar';
} else {
var result = ccn_api_calendar_update(
ccn_event_editingEvent[0],
ccn_event_editingEvent[1] == submitData[0] ? undefined : submitData[0],
ccn_event_editingEvent[2] == submitData[1] ? undefined : submitData[1],
ccn_event_editingEvent[3] == submitData[2] ? undefined : submitData[2],
ccn_event_editingEvent[5] == submitData[3] ? undefined : submitData[3],
ccn_event_editingEvent[6] == submitData[4] ? undefined : submitData[4],
ccn_event_editingEvent[8] == submitData[6] ? undefined : submitData[6],
ccn_event_editingEvent[7] == submitData[5] ? undefined : submitData[5],
ccn_event_editingEvent[4]
);
if (typeof(result) == 'undefined') ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-update"));
else window.location.href = '/web/calendar';
}
}

View File

@@ -0,0 +1,19 @@
$(document).ready(function() {
ccn_pages_currentPage = ccn_pages_enumPages.home;
// template process
ccn_template_Load();
// nav process
ccn_headerNav_Insert();
ccn_headerNav_BindEvents();
ccn_headerNav_LoggedRefresh();
// messagebox process
ccn_messagebox_Insert();
ccn_messagebox_BindEvent();
// apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage();
});

View File

@@ -0,0 +1,58 @@
$(document).ready(function() {
ccn_pages_currentPage = ccn_pages_enumPages.login;
// template process
ccn_template_Load();
// nav process
ccn_headerNav_Insert();
ccn_headerNav_BindEvents();
ccn_headerNav_LoggedRefresh();
// messagebox process
ccn_messagebox_Insert();
ccn_messagebox_BindEvent();
// apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage();
// bind login event
$("#ccn-login-form-login").click(ccn_login_startLogin);
});
function ccn_login_startLogin() {
// disable all ui first
$("#ccn-login-form-login").attr("disabled",true);
$("#ccn-login-form-username").attr("disabled",true);
$("#ccn-login-form-password").attr("disabled",true);
// get form data
username = $("#ccn-login-form-username").val();
password = $("#ccn-login-form-password").val();
/*
// try get salt
if (ccn_api_common_salt(username)) {
// continue login
if (ccn_api_common_login(username, password)) {
// ok, logged
// jump into home page again
window.location.href = '/web/home';
} else ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-login"));
} else ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-login"));
*/
if (ccn_api_common_webLogin(username, password)) {
// ok, logged
// jump into home page again
window.location.href = '/web/home';
return;
} else ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-login"));
// retore ui
$("#ccn-login-form-login").removeAttr("disabled");
$("#ccn-login-form-username").removeAttr("disabled");
$("#ccn-login-form-password").removeAttr("disabled");
}

View File

@@ -0,0 +1,190 @@
var ccn_todo_todoListCache = [];
$(document).ready(function() {
ccn_pages_currentPage = ccn_pages_enumPages.todo;
// template process
ccn_template_Load();
// nav process
ccn_headerNav_Insert();
ccn_headerNav_BindEvents();
ccn_headerNav_LoggedRefresh();
// messagebox process
ccn_messagebox_Insert();
ccn_messagebox_BindEvent();
// apply i18n
ccn_i18n_LoadLanguage();
ccn_i18n_ApplyLanguage();
// refresh once
ccn_todo_Refresh();
// bind event
$("#ccn-todo-btnAdd").click(ccn_todo_Add);
$("#ccn-todo-btnRefresh").click(ccn_todo_Refresh);
});
function ccn_todo_RefreshCacheList() {
// clean list cache first
ccn_todo_todoListCache = new Array();
var result = ccn_api_todo_getFull();
if(typeof(result) != 'undefined') {
for(var index in result) {
ccn_todo_todoListCache[result[index][0]] = result[index];
}
}
}
function ccn_todo_RenderCacheList() {
// clean list first
$("#ccn-todo-todoList").empty();
var renderdata = {
uuid: undefined,
data: undefined
};
var listDOM = $("#ccn-todo-todoList");
for(var index in ccn_todo_todoListCache) {
// update render data
var item = ccn_todo_todoListCache[index];
renderdata.uuid = item[0];
renderdata.data = LineBreaker2Br(item[2]);
// render
listDOM.append(ccn_template_todoItem.render(renderdata));
// set mode
var uuid = renderdata.uuid;
ccn_todo_ChangeDisplayMode(uuid, false);
// bind event
$("#ccn-todoItem-btnEdit-" + uuid).click(ccn_todo_ItemEdit);
$("#ccn-todoItem-btnDelete-" + uuid).click(ccn_todo_ItemDelete);
$("#ccn-todoItem-btnUpdate-" + uuid).click(ccn_todo_ItemUpdate);
$("#ccn-todoItem-btnCancelUpdate-" + uuid).click(ccn_todo_ItemCancelUpdate);
}
}
function ccn_todo_ChangeDisplayMode(uuid, isEdit) {
if(isEdit) {
// 4 buttons
$("#ccn-todoItem-btnEdit-" + uuid).hide();
$("#ccn-todoItem-btnDelete-" + uuid).hide();
$("#ccn-todoItem-btnUpdate-" + uuid).show();
$("#ccn-todoItem-btnCancelUpdate-" + uuid).show();
// 2 elements
$("#ccn-todoItem-p-" + uuid).hide();
$("#ccn-todoItem-textarea-" + uuid).show();
} else {
$("#ccn-todoItem-btnEdit-" + uuid).show();
$("#ccn-todoItem-btnDelete-" + uuid).show();
$("#ccn-todoItem-btnUpdate-" + uuid).hide();
$("#ccn-todoItem-btnCancelUpdate-" + uuid).hide();
$("#ccn-todoItem-p-" + uuid).show();
$("#ccn-todoItem-textarea-" + uuid).hide();
}
}
function ccn_todo_Refresh() {
// refresh and render once
ccn_todo_RefreshCacheList();
ccn_todo_RenderCacheList();
}
function ccn_todo_Add() {
var result = ccn_api_todo_add();
if (typeof(result) == 'undefined') {
// fail
ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-add"));
} else {
// add into cache
ccn_todo_todoListCache[result[0]] = result;
// render
var listDOM = $("#ccn-todo-todoList");
listDOM.append(ccn_template_todoItem.render({
uuid: result[0],
data: LineBreaker2Br(result[2])
}));
// set mode
var uuid = result[0];
ccn_todo_ChangeDisplayMode(uuid, false);
// bind event
$("#ccn-todoItem-btnEdit-" + uuid).click(ccn_todo_ItemEdit);
$("#ccn-todoItem-btnDelete-" + uuid).click(ccn_todo_ItemDelete);
$("#ccn-todoItem-btnUpdate-" + uuid).click(ccn_todo_ItemUpdate);
$("#ccn-todoItem-btnCancelUpdate-" + uuid).click(ccn_todo_ItemCancelUpdate);
}
}
function ccn_todo_ItemEdit() {
var uuid = $(this).attr("uuid");
// copy current data to textarea
$("#ccn-todoItem-textarea-" + uuid).val(
ccn_todo_todoListCache[uuid][2]
);
// switch to edit mode
ccn_todo_ChangeDisplayMode(uuid, true);
}
function ccn_todo_ItemDelete() {
var uuid = $(this).attr("uuid");
var result = ccn_api_todo_delete(
uuid,
ccn_todo_todoListCache[uuid][3]
);
if(!result) {
// fail
ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-delete"));
} else {
// remove body
$("#ccn-todoItem-" + uuid).remove();
}
}
function ccn_todo_ItemUpdate() {
var uuid = $(this).attr("uuid");
var newData = $("#ccn-todoItem-textarea-" + uuid).val();
var result = ccn_api_todo_update(
uuid,
newData,
ccn_todo_todoListCache[uuid][3]
);
if (typeof(result) == 'undefined') {
// fail
ccn_messagebox_Show($.i18n.prop("ccn-i18n-js-fail-update"));
} else {
// safely update data & lastChanged and control
ccn_todo_todoListCache[uuid][2] = newData;
ccn_todo_todoListCache[uuid][3] = result;
$("#ccn-todoItem-p-" + uuid).html(LineBreaker2Br(newData));
// switch to normal mode
ccn_todo_ChangeDisplayMode(uuid, false);
}
}
function ccn_todo_ItemCancelUpdate() {
var uuid = $(this).attr("uuid");
// clean data
$("#ccn-todoItem-textarea-" + uuid).val("");
// switch to normal mode
ccn_todo_ChangeDisplayMode(uuid, false);
}

View File

@@ -0,0 +1,10 @@
// all args are based on 1
function ccn_tabcontrol_SwitchTab(tabcontrolGroup, targetTabIndex) {
// close all panel and tab
$(".tabcontrol-tab-" + tabcontrolGroup).removeClass("is-active");
$(".tabcontrol-panel-" + tabcontrolGroup).hide();
// show specific
$("#tabcontrol-tab-" + tabcontrolGroup + "-" + targetTabIndex).addClass("is-active");
$("#tabcontrol-panel-" + tabcontrolGroup + "-" + targetTabIndex).show();
}

View File

@@ -0,0 +1,50 @@
var ccn_template_headerNav = undefined;
var ccn_template_messagebox = undefined;
var ccn_template_datetimepicker = undefined;
var ccn_template_calendarItem = undefined;
var ccn_template_scheduleItem = undefined;
var ccn_template_ownedItem = undefined;
var ccn_template_sharingItem = undefined;
var ccn_template_displayOwnedItem = undefined;
var ccn_template_displaySharedItem = undefined;
var ccn_template_userItem = undefined;
var ccn_template_todoItem = undefined;
var ccn_template_optionItem = undefined;
var ccn_template_tokenItem = undefined;
function ccn_template_Load() {
ccn_template_headerNav = ccn_template_TemplateLoader('headerNav');
ccn_template_messagebox = ccn_template_TemplateLoader('messagebox');
ccn_template_datetimepicker = ccn_template_TemplateLoader('datetimepicker');
ccn_template_calendarItem = ccn_template_TemplateLoader('calendarItem');
ccn_template_scheduleItem = ccn_template_TemplateLoader('scheduleItem');
ccn_template_displayOwnedItem = ccn_template_TemplateLoader('displayOwnedItem');
ccn_template_displaySharedItem = ccn_template_TemplateLoader('displaySharedItem');
ccn_template_todoItem = ccn_template_TemplateLoader('todoItem');
ccn_template_userItem = ccn_template_TemplateLoader('userItem');
ccn_template_tokenItem = ccn_template_TemplateLoader('tokenItem');
ccn_template_ownedItem = ccn_template_TemplateLoader('ownedItem');
ccn_template_sharingItem = ccn_template_TemplateLoader('sharingItem');
ccn_template_optionItem = ccn_template_TemplateLoader('optionItem');
}
function ccn_template_TemplateLoader(templateName) {
var elements = $("#jsrender-tmpl-" + templateName);
if (elements.length == 0) return undefined;
var cache = undefined;
$.ajax({
url: elements.attr('src'),
type: "GET",
async: false,
success: function (data) {
cache = $.templates(data);
}
});
return cache;
}

View File

@@ -0,0 +1,70 @@
/*
function ComputPasswordWithSalt(password, salt) {
return ComputeSHA256(ComputeSHA256(password) + salt.toString());
}
function ComputeSHA256(strl) {
var tempstr = new TextEncoder().encode(strl);
var hashedStrl = undefined
var shitpromise = crypto.subtle.digest('SHA-256', tempstr);
Promise.all(shitpromise).then(function(result) {
hashedStrl = result;
});
var hashArray = Array.from(new Uint8Array(hashedStrl));
var hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
return hashHex.toLowerCase();
}
*/
var DefaultColor = '#536dfe';
function IsResponseOK(data) {
if (typeof (data) == 'undefined') {
console.log("Fail to execute an api!");
return false;
}
if (!data['success']) {
console.log("Fail to execute an api! Reason:");
console.log(data['error']);
return false;
}
return true;
}
function LineBreaker2Br(strl) {
return $('<div>').text(strl).html().replace(/\n/g, '<br />');
}
function IsUndefinedOrEmpty(data) {
return (typeof (data) == 'undefined' || data == "");
}
function SmarterShowHide(boolean, element) {
if (typeof (element) == 'undefined') return;
if (boolean) element.show();
else element.hide();
}
function GCD(a, b) {
if (b == 0) return a;
return GCD(b, a % b);
}
function LCM(a, b) {
return a / GCD(a, b) * b;
}
String.prototype.format = function() {
var e = arguments;
return !!this && this.replace(
/\{(\d+)\}/g,
function (t, n) {
return e[n].toString() ? e[n].toString() : t;
}
);
};
Date.prototype.getWeekday = function() {
var temp = this.getDay();
if (temp == 0) return 6;
else return temp - 1;
};