-
-
Save pavelgordon/4ebebc81c8a8d63c02e4d3403f20e1dd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Main file. | |
# Contains entry point | |
# | |
# @nodoc | |
previously_selected = undefined | |
# @nodoc | |
columns = [] | |
# @nodoc | |
commentsPlugin = undefined | |
# @nodoc | |
hot = undefined | |
# @nodoc | |
table_data = undefined | |
# @nodoc | |
pad = (str, max) -> | |
str = str.toString() | |
if str.length < max then pad('0' + str, max) else str | |
# @nodoc | |
# @nodoc | |
days = [] | |
# @nodoc | |
widths = [] | |
# @nodoc | |
cells = 35 | |
# @nodoc | |
$("#year").change -> | |
window.location.href = window.location.href.replace(/calendars[/]\d{4}/, 'calendars/' + $("#year option:selected").val()) | |
@log_table_data = ()-> | |
console.log table_data | |
# @nodoc | |
i = 1 | |
# @nodoc | |
while i <= lastDayOfMonth(currentMonth, currentYear) | |
day = new Date(currentYear, currentMonth - 1, i).getDay() | |
if day == 0 or day == 6 | |
days.push '<strong style="color:#FF0000">' + i + '</strong>' | |
else | |
days.push i | |
widths.push cells | |
columns.push {data: i} | |
i++ | |
columns.push {data: "total hours", editor: false} | |
days.push "hours" | |
widths.push 50 | |
# | |
# Entry point | |
# Fetches data from server | |
# | |
$ -> | |
$('.nav-pills, .nav-tabs').tabdrop() | |
monthSelect = $('#month-select li:not(.dropdown)') | |
$.each monthSelect, (i) -> | |
$(this).click () -> | |
box = $('.box') | |
start_width = box.width() | |
$('#extra').slideUp 100, () -> | |
end_width = box.width() + 20 | |
box.width start_width | |
box.animate {width: end_width}, | |
duration: 60 | |
monthSelect.filter('.active').removeClass 'active' | |
$(this).addClass 'active' | |
$('#filters').slideUp 50 | |
$('#loading').fadeIn 200 | |
table_data = new TableData [] | |
init_metadata() | |
fetch_cells_data = ()-> | |
$.ajax | |
type: 'GET', | |
dataType: "json", | |
url: '/calendars/data', | |
data: { | |
month: currentMonth, | |
year: currentYear | |
} | |
success: (projects_data) -> | |
table_data.load_from_json projects_data | |
console.log 'fetched_data:', projects_data | |
init_hot(projects_data) | |
init_filters() | |
# Set custom scroll and restore scroll position | |
scroll_pos = $.cookie 'lastCalendarsPosition' | |
if scroll_pos != undefined and scroll_pos > 0 | |
$('.wtHolder').scrollTop(scroll_pos) | |
hot.updateSettings {height: tableHeightByLength(hot.getData().length)} | |
error: (q, status, err)-> | |
document.getElementById("status-line").innerHTML = I18n.t('extra.error') | |
console.err 'Failed fetching cells data' | |
console.err q, status, err | |
init_metadata = (callback)-> | |
$.ajax | |
type: 'GET', | |
dataType: "json", | |
url: '/calendars/metadata', | |
success: (metadata) -> | |
#console.log "metadata", metadata | |
Extradata.events = metadata['events'] | |
Extradata.holidays = metadata['holidays'].map (x) -> x['day'] | |
fetch_cells_data() | |
error: (q, status,err) -> | |
console.log q,status,err | |
# | |
# Init filters | |
# | |
emp_filt = '' | |
proj_filt = [] | |
init_filters = () -> | |
$('#filter-form').submit (e) -> | |
false | |
# Init projects filetr | |
projects_filter = $('#projects-filter') | |
projects_filter.empty() | |
table_data.get_projects().forEach (project_name) -> | |
# removing <b> tag around <b>PRJNAME</b> | |
proj_filt.push project_name.substring(3, project_name.length - 4) | |
projects_filter.append('<option>' + project_name + '</option>') | |
filtrate_proj = () -> | |
proj_filt = $('#projects-filter option:selected').toArray().map((val) -> val.value) | |
deselected = $('#projects-filter option:not(:selected)').toArray().map((val) -> val.value) | |
$.cookie 'deselectedProj', JSON.stringify deselected, {path: '/'} | |
filtrate(emp_filt, proj_filt) | |
projects_filter.multiselect({ | |
maxHeight: 300, | |
onDropdownHidden: (element, cheked) -> | |
filtrate_proj() | |
}) | |
projects_filter.multiselect() | |
projects_filter.multiselect('selectAll', false) | |
if $.cookie('deselectedProj') == undefined | |
$.cookie 'deselectedProj', JSON.stringify [], {path: '/'} | |
deselected = $.cookie 'deselectedProj' | |
if deselected != undefined or deselected != [] | |
deselected = JSON.parse deselected | |
deselected.forEach (elem) -> | |
projects_filter.multiselect('deselect', elem) | |
proj_filt = $('#projects-filter option:selected').toArray().map((val) -> | |
val.value) | |
projects_filter.multiselect('updateButtonText') | |
# Init employees filter | |
if $.cookie('filterQuery') == undefined | |
$.cookie 'filterQuery', '', {path: '/'} | |
search_field = $('#employee-filter') | |
emp_filt = $.cookie('filterQuery') | |
search_field.val(emp_filt) | |
search_field.keyup () -> | |
emp_filt = search_field.val() | |
sensitivity = 2 | |
if emp_filt.length > sensitivity | |
filtrate(emp_filt, proj_filt) | |
else if $.cookie("filterQuery").length > sensitivity | |
filtrate('', proj_filt) | |
$.cookie "filterQuery", emp_filt, {path: '/'} | |
# If filters not empty show filter line | |
if emp_filt != '' or deselected.length > 0 | |
filters_toggle() | |
filtrate(emp_filt, proj_filt) | |
# Init reset button | |
$('#reset-filter').click () -> | |
search_field.val '' | |
projects_filter.multiselect('selectAll', false) | |
projects_filter.multiselect('updateButtonText') | |
$.cookie 'filterQuery', '', {path: '/'} | |
filtrate_proj() | |
# Close filter panel button | |
$('#close-filter').click () -> | |
$('#filters').slideUp 100 | |
# | |
# Update table size on window resize | |
# | |
$(window).resize -> | |
hot.updateSettings { | |
width: tableWidth() | |
height: tableHeightByLength(hot.getData().length) | |
} | |
# | |
# Magic calculations | |
# | |
# Height and width | |
tableHeightByLength = (rows_num) -> | |
maxTableHeight = $(window).height() - 235 | |
filters_height = if $('#filters').is(':visible') then 44 else 0 | |
hot_height = rows_num * 24 + 65 | |
height = if hot_height + filters_height > maxTableHeight | |
hot_height - filters_height | |
else | |
hot_height | |
Math.min(height, maxTableHeight - filters_height) | |
tableHeight = () -> | |
tableHeightByLength(table_data.length()) | |
tableWidth = () -> | |
window_width = $(window).width() - 50 | |
Math.min(widths.length * cells + 199, window_width) | |
# Using to prevent callbacks from contextmenu on headers | |
prevent = (th) -> | |
$(th).contextmenu (e) -> | |
e.stopImmediatePropagation() | |
e.preventDefault() | |
e.stopPropagation() | |
return false | |
# | |
# Big init function | |
# | |
init_hot = (fetched_data)-> | |
event_data = document.getElementById("extra") | |
# Need to set colWidths, width, colHeight, height manually because of slow scrolling bug | |
settings = { | |
data: table_data.to_json() | |
validator: hoursValidator | |
allowInvalid: false | |
columns: columns | |
colHeaders: days | |
rowHeaders: table_data.get_names() | |
comments: true | |
search: true | |
renderAllRows: true | |
fillHandle: false | |
width: tableWidth | |
height: tableHeight | |
colWidths: widths | |
colHeights: 30 | |
maxRows: table_data.length() | |
cells: init_cell | |
customBorders: [{ | |
range: { | |
from: { | |
row: 0, | |
col: columns.length - 1 | |
}, | |
to: { | |
row: table_data.length(), | |
col: columns.length - 1 | |
} | |
}, | |
left: { | |
width: 1, | |
color: 'black' | |
} | |
}] | |
beforeKeyDown: (e) -> | |
range = hot.getSelectedRange() | |
if range == undefined | |
return | |
selectedDays = range.getAll() | |
event_name = "" | |
selected_event = undefined | |
for event in Extradata.events | |
if event.hotkey.charCodeAt(0) - 32 == e.keyCode | |
selected_event = event # have to be stopped after finding first instance | |
break | |
if e.keyCode == 'q'.charCodeAt(0) - 32 | |
e.isImmediatePropagationEnabled = false | |
e.cancelBubble = true | |
e.preventDefault() | |
console.log hot.getCell(selectedDays[0].row, selectedDays[0].col) | |
console.log hot.getCellMeta(selectedDays[0].row, selectedDays[0].col) | |
# console.log hot.getCellAtCoords([selectedDays[0].row, selectedDays[0].col]) | |
console.log hot.getCellRenderer(selectedDays[0].row, selectedDays[0].col) | |
console.log hot.getCellValidator(selectedDays[0].row, selectedDays[0].col) | |
console.log hot.getCellEditor(selectedDays[0].row, selectedDays[0].col) | |
console.log hot.getDataAtCell(selectedDays[0].row, selectedDays[0].col) | |
console.log hot.getSourceDataAtCell(selectedDays[0].row, selectedDays[0].col) | |
return | |
if selected_event == undefined # no event for such letter | |
return | |
e.isImmediatePropagationEnabled = false | |
e.cancelBubble = true | |
e.preventDefault() | |
if selected_event.hotkey.charCodeAt(0) - 32 == 67 # it is clear event | |
deleteEvents selectedDays | |
else | |
setEvents selectedDays, selected_event.id | |
hot.render() | |
afterSelectionEnd: (r, c, r2, c2) -> | |
console.log('afterSelectionEnd'); | |
resetOnCallDuty() | |
previously_selected = { r: r, c: c, r2: r2, c2: c2 } | |
updateSumSelected(r, c, r2, c2) | |
for row_index in [r..r2] | |
for column in [c..c2] | |
row = table_data.get_row_by_index(row_index) | |
day = row.get_day(column + 1) | |
# If day Event is not On Call Duty | |
if day == undefined || day.event_id != 3 | |
console.log('miss') | |
continue | |
cell_meta = hot.getCellMeta row_index, column | |
cell_meta.validator = onCallValidator | |
hot.setDataAtCell row_index, column, "#{day.passive}:#{day.active}", 'donothing' | |
afterDeselect: () -> | |
console.log('afterDeselect') | |
resetOnCallDuty() | |
previously_selected = undefined | |
beforeRender: ()-> | |
remove_week_numbers() | |
afterRender: ()-> | |
append_week_numbers() | |
beforeChange: (changes, source) -> | |
console.log("before_changes", source) | |
if (!changes) || source == 'donothing' | |
return | |
for change in changes | |
console.log change | |
row_number = change[0] | |
day_number = change[1] | |
old_value = change[2] # previous hours in cell | |
new_value = change[3] # new hours in cell | |
if new_value == old_value | |
continue | |
# if old value and new value are empty | |
if (old_value == undefined or old_value == '' or old_value == null ) and (new_value == undefined or new_value == '' or new_value == null ) | |
continue | |
document.getElementById("status-line").innerHTML = I18n.t('extra.saving') | |
row = table_data.get_row_by_index(row_number) | |
day = row.get_day day_number | |
#oncallduty | |
if day.event_id == 3 | |
if new_value == '' or new_value == undefined | |
day.active = undefined | |
day.passive = undefined | |
else | |
arr = new_value.split ':' | |
day.active = parseInt arr[1] | |
day.passive = parseInt arr[0] | |
row.update_day_hours day, (day.active+day.passive) | |
updateSumSelected(row_number,day_number-1,row_number,day_number-1 ) | |
$.ajax | |
type: 'POST' | |
url: '/calendars/save_on_call_duty_hours' | |
dataType: 'json', | |
data: { | |
update: JSON.stringify { | |
employee_project_id: row.id, | |
occurs: "#{currentYear}-#{currentMonth}-#{day_number}", | |
hours: new_value | |
} | |
} | |
success: (data) -> | |
document.getElementById("status-line").innerHTML = I18n.t('extra.all_saved') | |
failure: (errMsg) -> | |
alert errMsg | |
else | |
# setting hours empty | |
if isNaN(new_value) || new_value == '' | |
new_value = null | |
res = row.update_day_hours day | |
#changes[0][3] = res; | |
else | |
new_value = parseFloat new_value | |
res = row.update_day_hours day, new_value | |
changes[0][3] = res; | |
# Need this when cell hours is simply deleted by Del/Backspace | |
updateSumSelected(row_number,day_number-1,row_number,day_number-1 ) | |
hot.render() | |
update = { | |
employee_project_id: row.id, | |
occurs: currentYear + "-" + currentMonth + "-" + day_number, | |
hours: new_value | |
} | |
console.log 'change hours' | |
#TODO if not success then change to old value | |
$.ajax | |
type: 'POST' | |
url: '/calendars/save_hours' | |
dataType: 'json', | |
data: { | |
update: JSON.stringify(update) | |
} | |
success: (data) -> | |
document.getElementById("status-line").innerHTML = I18n.t('extra.all_saved') | |
failure: (errMsg) -> | |
alert errMsg | |
afterSetCellMeta: (row_number, col_number, key, value)-> | |
if key == 'comment' | |
addedit_comment row_number, col_number, value | |
afterGetRowHeader: (row, th) -> | |
prevent(th) | |
afterGetColHeader: (row, th) -> | |
prevent(th) | |
} | |
# This function restores previously changed by selecting data on | |
# 'On Call Duty' cells | |
resetOnCallDuty = () -> | |
console.log('resetOnCallDuty is called....') | |
if previously_selected == undefined | |
console.log('and returned cause of previously_selected undefined') | |
return | |
console.log('and continue') | |
for row_index in [previously_selected.r..previously_selected.r2] | |
for column in [previously_selected.c..previously_selected.c2] | |
row = table_data.get_row_by_index(row_index) | |
day = row.get_day(column + 1) | |
# If day Event is not On Call Duty | |
if day == undefined || day.event_id != 3 || day.active == undefined || day.passive == undefined | |
continue | |
# Update data if something is changed | |
cell_meta = hot.getCellMeta row_index, column | |
cell_meta.validator = undefined | |
hot.setDataAtCell row_index, column, "#{day.passive + day.active}", 'donothing' | |
hot = new Handsontable(event_data, settings ) | |
hot.updateSettings | |
contextMenu: | |
callback: contextmenu_callback | |
items: createContextMenuItems() | |
for k in [0...table_data.rows.length] | |
row = table_data.get_row_by_index(k); | |
for j in [0...row.data.length] | |
cell = row.get_day(j + 1) | |
if (cell.activity) | |
draw_activity k, j, 'darkblue' | |
commentsPlugin = hot.getPlugin('comments'); | |
box = $('.box') | |
end_width = box.width() | |
$('#extra').hide() | |
start_width = box.width() + 50 | |
box.width start_width | |
box.animate {width: end_width}, | |
complete: () -> | |
$('#loading').fadeOut 100 | |
box.width 'auto' | |
$('#extra').slideDown 100 | |
duration: 60 | |
# Init with commentaries and events. | |
# Triggered when hot.render() | |
init_cell = (row_number, col_number, prop) -> | |
if row_number == undefined || col_number == undefined | |
return | |
day_number = col_number + 1 | |
row = table_data.get_row_by_index row_number | |
if row == undefined | |
return | |
day = row.get_day day_number | |
cellProperties = {} | |
# if col_number is last column( with total hours) | |
if col_number == columns.length - 1 | |
if row.type == 'project' #TODO replace by IS_PROJECT | |
cellProperties.renderer = Extra.projectHighlight.renderer | |
cellProperties.readOnly = true | |
else | |
cellProperties.renderer = Extra.totalHours.renderer table_data | |
return cellProperties | |
# possibly bug merge fired and maternity | |
if (row['end_day'] != undefined && col_number >= row['end_day']) || | |
(row['start_day'] != undefined && col_number + 2 <= row['start_day']) | |
return { | |
renderer: Extra.fired.renderer | |
editor: false | |
readOnly: true | |
} | |
if row['maternity']['exists'] | |
if (row['maternity']['start'] && (day['day'] in [row['maternity']['index']..lastDayOfMonth(currentMonth, currentYear)])) || | |
(!row['maternity']['start'] && (day['day'] in [1..row['maternity']['index']])) | |
cellProperties.renderer = Extra.maternity.renderer | |
return cellProperties | |
cellProperties.comment = day.comment | |
cellProperties.renderer = switch day.event_id | |
when m_events.BUSINESS_TRIP then Extra.businesstrip.renderer | |
when m_events.ON_CALL_DUTY then Extra.oncallduty.renderer | |
when m_events.OVERTIME_WORK then Extra.overtimework.renderer | |
when m_events.SICK_LEAVE then Extra.sickleave.renderer | |
when m_events.VACATION then Extra.vacation.renderer | |
when m_events.UNPAID_LEAVE then Extra.unpaidleave.renderer | |
when m_events.HOME_OFFICE then Extra.homeoffice.renderer | |
if day.event_id == undefined || day.event_id == null | |
if day_number in getWeekends(currentYear, currentMonth) | |
cellProperties.renderer = Extra.highlightWeekends.renderer | |
if day_number in Extradata.holidays | |
cellProperties.renderer = Extra.highlightHolidays.renderer | |
if row.type == 'project' #TODO replace by IS_PROJECT | |
cellProperties.renderer = Extra.projectHighlight.renderer | |
cellProperties | |
# Set activity by means of drawing border for cell with given coordinates | |
# @param [Integer] row | |
# @param [Integer] column | |
# @param [String] color | |
draw_activity = (row, column, color) -> | |
customBorders = [ { | |
range: | |
from: | |
row: row | |
col: column | |
to: | |
row: row | |
col: column | |
top: | |
width: 1.5 | |
color: color | |
left: | |
width: 1.5 | |
color: color | |
bottom: | |
width: 1.5 | |
color: color | |
right: | |
width: 1.5 | |
color: color | |
} ] | |
getSettingIndex = (className) -> | |
k = 0 | |
while k < hot.view.wt.selections.length | |
if hot.view.wt.selections[k].settings.className == className | |
return k | |
k++ | |
-1 | |
insertBorderIntoSettings = (border) -> | |
coordinates = | |
row: border.row | |
col: border.col | |
selection = new WalkontableSelection(border, new WalkontableCellRange(coordinates, coordinates, coordinates)) | |
index = getSettingIndex(border.className) | |
if index >= 0 | |
hot.view.wt.selections[index] = selection | |
else | |
hot.view.wt.selections.push selection | |
return | |
createSingleEmptyBorder = -> | |
{ hide: true } | |
createDefaultHtBorder = -> | |
{ | |
width: 1 | |
color: '#000' | |
cornerVisible: false | |
} | |
createEmptyBorders = (row, col) -> | |
{ | |
className: 'border_row' + row + 'col' + col | |
border: createDefaultHtBorder() | |
row: row | |
col: col | |
top: createSingleEmptyBorder() | |
right: createSingleEmptyBorder() | |
bottom: createSingleEmptyBorder() | |
left: createSingleEmptyBorder() | |
} | |
prepareBorderFromCustomAddedRange = (rowObj) -> | |
range = rowObj.range | |
row = range.from.row | |
while row <= range.to.row | |
col = range.from.col | |
while col <= range.to.col | |
border = createEmptyBorders(row, col) | |
add = 0 | |
if row == range.from.row | |
add++ | |
if rowObj.hasOwnProperty('top') | |
border.top = rowObj.top | |
if row == range.to.row | |
add++ | |
if rowObj.hasOwnProperty('bottom') | |
border.bottom = rowObj.bottom | |
if col == range.from.col | |
add++ | |
if rowObj.hasOwnProperty('left') | |
border.left = rowObj.left | |
if col == range.to.col | |
add++ | |
if rowObj.hasOwnProperty('right') | |
border.right = rowObj.right | |
if add > 0 | |
@setCellMeta row, col, 'borders', border | |
insertBorderIntoSettings border | |
col++ | |
row++ | |
return | |
instance = hot | |
prepareBorderFromCustomAddedRange.call instance, customBorders[0] | |
instance.render() | |
instance.view.wt.draw true | |
instance.customBorders = customBorders | |
prepareBorderFromCustomAddedRange.call hot, customBorders[0] | |
hot.render() | |
hot.view.wt.draw true | |
hot.customBorders = customBorders | |
return | |
# Remove border for cell with empty activity list | |
# @param [Integer] row | |
# @param [Integer] column | |
remove_activity = (row, column) -> | |
removeAllBorders = (row, col) -> | |
borderClassName = 'border_row' + row + 'col' + col | |
removeBordersFromDom(borderClassName) | |
@removeCellMeta(row, col, 'borders') | |
return | |
removeBordersFromDom = (borderClassName) -> | |
borders = document.querySelectorAll('.' + borderClassName) | |
for k in [0..borders.length] | |
if (borders[k]) | |
if (borders[k].nodeName != 'TD') | |
parent = borders[k].parentNode | |
if (parent.parentNode) | |
parent.parentNode.removeChild(parent); | |
return | |
removeAllBorders.call hot, row, column | |
hot.render() | |
hot.view.wt.draw true | |
return | |
Element::remove = -> | |
@parentElement.removeChild this | |
return | |
NodeList::remove = | |
HTMLCollection::remove = -> | |
k = @length - 1 | |
while k >= 0 | |
if @[k] and @[k].parentElement | |
@[k].parentElement.removeChild @[k] | |
k-- | |
return | |
# | |
# Set color for cell with given coordinates | |
# @param [Integer] row | |
# @param [Integer] col | |
# @param [Integer] event_id | |
# @param [String] row_type type of row, can be "project" or "employee_project" | |
# accepts column (0..29) | |
@color = (row, col, event_id, row_type)-> | |
cell = hot.getCellMeta(row, col) | |
day_number = col + 1 | |
if event_id == undefined || event_id == null | |
if day_number in getWeekends(currentYear, currentMonth) | |
cell.renderer = Extra.highlightWeekends.renderer | |
if day_number in Extradata.holidays | |
cell.renderer = Extra.highlightHolidays.renderer | |
else | |
cell.renderer = switch event_id | |
when m_events.BUSINESS_TRIP then Extra.businesstrip.renderer | |
when m_events.ON_CALL_DUTY then Extra.oncallduty.renderer | |
when m_events.OVERTIME_WORK then Extra.overtimework.renderer | |
when m_events.SICK_LEAVE then Extra.sickleave.renderer | |
when m_events.VACATION then Extra.vacation.renderer | |
when m_events.UNPAID_LEAVE then Extra.unpaidleave.renderer | |
when m_events.HOME_OFFICE then Extra.homeoffice.renderer | |
if row_type == 'project' #TODO replace by IS_PROJECT | |
cell.renderer = Extra.projectHighlight.renderer | |
# | |
# Wrappers for using color function | |
# | |
colorize_cell_by_index = (row_index, col, event_id)-> | |
row = table_data.get_row_by_index(row_index) | |
@color(row_index, col, event_id, row.type) | |
@colorize_cell = (row, col, event_id)-> | |
t_row = table_data.get row | |
@color(row, col, event_id, t_row.type) | |
# Add or edit comment in cell with coordinates given | |
# @param [Integer] row_number | |
# @param [Integer] col_number | |
# @param [String] comment | |
addedit_comment = (row_number, col_number, comment)-> | |
day_number = col_number + 1 | |
day = table_data.get_day row_number, day_number | |
if comment == '' | |
day.comment = undefined | |
commentsPlugin.removeCommentAtCell(row_number, col_number); | |
else | |
day.comment = comment | |
update = { | |
employee_project_id: table_data.get_row_by_index(row_number).id, | |
occurs: currentYear + "-" + currentMonth + "-" + day_number | |
comment: comment | |
} | |
$.ajax | |
type: 'POST', | |
url: '/calendars/save_comment', | |
data: { | |
update: JSON.stringify(update) | |
} | |
success: (response)-> | |
console.log response | |
error:(err)-> | |
console.error err | |
# | |
# Delete comment in cell with coordinates given | |
# @param [Integer] row_number | |
# @param [Integer] col_number | |
@delete_comment = (row_number, col_number)-> | |
log_comments "delete_comment", row_number, col_number | |
day_number = col_number + 1 | |
day = table_data.get_day row_number, day_number | |
day.comment = null | |
commentsPlugin.removeCommentAtCell(row_number, col_number) | |
update = { | |
employee_project_id: table_data.get_row_by_index(row_number).id, | |
occurs: currentYear + "-" + currentMonth + "-" + day_number, | |
comment: '' | |
} | |
$.ajax | |
type: 'POST', | |
url: '/calendars/save_comment', | |
data: { | |
update: JSON.stringify(update) | |
} | |
success: (responce)-> | |
console.log responce | |
error: (q, status, err)-> | |
console.err q, status, err | |
# Set events to array of cells coordinates given | |
# @param [Array<WalkontableCellCoords>] selected_days - array of selected days | |
# @param event_id id of event to be set | |
setEvents = (selected_days, event_id) -> | |
update = { | |
event_id: event_id, | |
events: [] | |
} | |
for selected_day in selected_days | |
unless validate_event(selected_day, event_id) | |
continue | |
current_row = table_data.get_row_by_index selected_day.row | |
if current_row.type == 'project' | |
continue | |
employee_project_id = current_row.id | |
day_number = selected_day.col + 1 | |
if (current_row['end_day'] != undefined && day_number - 1 >= current_row['end_day']) || | |
(current_row['start_day'] != undefined && day_number + 1 <= current_row['start_day']) | |
continue | |
if day_number < 10 | |
day_number = '0' + day_number | |
# update total_hours and day hours | |
res = current_row.update_hours_by_event(day_number, event_id) | |
# set cell data | |
hot.setDataAtCell selected_day.row, selected_day.col, res | |
# update day event (and global) | |
table_data.set_event selected_day.row, selected_day.col, event_id, hot | |
month = currentMonth | |
if currentMonth < 10 | |
month = '0' + currentMonth | |
occurs = currentYear + "-" + month + "-" + (day_number) | |
update.events.push [employee_project_id, occurs] | |
if update.events.length == 0 | |
return | |
document.getElementById("status-line").innerHTML = I18n.t('extra.saving') | |
$.ajax | |
type: 'POST' | |
url: '/calendars/events' | |
data: { | |
data: JSON.stringify(update) | |
} | |
success: (responce) -> | |
document.getElementById("status-line").innerHTML = I18n.t('extra.all_saved') | |
error: (q, status, err)-> | |
console.err q, status, err | |
document.getElementById("status-line").innerHTML = I18n.t('extra.error') | |
filtered_rows = table_data.filtrate emp_filt, proj_filt | |
hot.updateSettings | |
data: (new TableData filtered_rows).to_json() | |
# Check if event can be set on selected day | |
# @param [WalkontableCellCoords] selected_day | |
# @param [Integer] new_event_id | |
validate_event = (selected_day, new_event_id)-> | |
row = table_data.get_row_by_index selected_day.row | |
day = row.get_day selected_day.col + 1 | |
current_event_id = day.event_id | |
if current_event_id == new_event_id | |
return false | |
return true | |
deleteEvents = (selected_days)-> | |
console.log 'deleteEvents' | |
employee_project_ids = [] | |
min_day = 42 | |
max_day = 0 | |
for selected_day in selected_days | |
row = table_data.get_row_by_index(selected_day.row) | |
if row.type == 'project' | |
continue | |
employee_project_id = row.id | |
day_number = selected_day.col + 1 | |
max_day = day_number if day_number > max_day | |
min_day = day_number if day_number < min_day | |
employee_project_ids.push employee_project_id | |
# update total_hours and day hours | |
res = row.update_hours_by_clear(day_number) | |
console.log('res', res) | |
# set cell data | |
table_data.clear_event selected_day.row, selected_day.col, hot | |
updateSumSelected(selected_day.row, selected_day.col,selected_day.row,selected_day.col) | |
day = row.get_day(day_number) | |
hot.render() | |
if employee_project_ids.length == 0 | |
return | |
data = { | |
start_day: currentYear + "-" + currentMonth + "-" + min_day | |
end_day: currentYear + "-" + currentMonth + "-" + max_day | |
pe_ids: employee_project_ids | |
} | |
#TODO write changes to table_data | |
document.getElementById("status-line").innerHTML = I18n.t('extra.saving') | |
console.log data | |
$.ajax | |
type: 'DELETE' | |
url: '/calendars/events' | |
data: { | |
data: JSON.stringify(data) | |
} | |
success: (response) -> | |
document.getElementById("status-line").innerHTML = I18n.t('extra.all_saved') | |
error: (q, status, err)-> | |
document.getElementById("status-line").innerHTML = I18n.t('extra.error') | |
console.err q, status, err | |
# | |
# Catch Ctrl+f and creates/removes search_field. | |
# While creating, keyup event is added. | |
# | |
$(window).keydown (e) -> | |
if (e.ctrlKey or e.metaKey) and e.keyCode == 70 | |
e.preventDefault() | |
# alert 'ctrl+f pressed!' | |
filters_toggle() | |
sizeOfFilterFieldBox = 31 | |
filters_toggle = () -> | |
filters = $("#filters") | |
filters.slideToggle 100, () -> | |
if filters.is(':visible') | |
hot.deselectCell() | |
setTimeout((() -> $('#employee-filter').focus()), 0) | |
hot.updateSettings {height: tableHeightByLength(hot.getData().length)} | |
filtrate = (emp_filt, proj_filt) -> | |
filtered_rows = table_data.filtrate emp_filt, proj_filt | |
console.log "new_length", tableHeightByLength(filtered_rows.length) | |
hot.updateSettings | |
data: (new TableData filtered_rows).to_json() | |
rowHeaders: filtered_rows.map (elem)-> | |
elem.get_name() | |
height: tableHeightByLength(filtered_rows.length) | |
remove_week_numbers = ()-> | |
$(".header-grouping").remove() | |
append_week_numbers = ()-> | |
weeknumbers = @getWeekNumbers(currentYear, currentMonth) | |
#console.log "weeknumbers", weeknumbers | |
newHeaderRow = '<tr id="weeknumbers" class="header-grouping" style="' + | |
'height: 25px;width: ' + tableWidth() + 'px' + | |
'"><th></th>' | |
for w_number, d_count of weeknumbers | |
newHeaderRow += '<th colspan="' + d_count + '">' + w_number + '</th>' | |
i = 2 | |
while i <= d_count | |
newHeaderRow += '<th class="hiddenHeader"/>' | |
i++ | |
newHeaderRow += '<th>total<th>' | |
newHeaderRow += '</tr>' | |
$('#extra').find('thead').find('tr').before(newHeaderRow) | |
$('.header-grouping').each () -> | |
prevent(this) | |
cnt = 0 | |
init_cnt = 0 | |
act_show_row = 0 | |
act_show_col = 0 | |
$('#activity_show').on('hidden.bs.modal', -> | |
if (cnt > 0) && (init_cnt == 0) | |
draw_activity act_show_row, act_show_col, 'darkblue' | |
return | |
if (cnt == 0) && (init_cnt > 0) | |
remove_activity act_show_row, act_show_col | |
return | |
) | |
contextmenu_callback = (key, options) -> | |
col_number = options.start.col | |
row_number = options.start.row | |
selectedDays = hot.getSelectedRange().getAll() | |
event = Extradata.events.find (x, i,arr ) -> return x if x.name.replace(/\s+/g, '').toLocaleLowerCase() == key | |
switch key | |
when 'commentsAddEdit' then log_comments 'comments editing', key, options | |
when 'activities' | |
document.getElementById('act_body').innerHTML = '' | |
document.getElementById('act_show').innerHTML = '' | |
get_act = | |
employee_project_id: table_data.get_row_by_index(row_number).id | |
occurs: currentYear + '-' + currentMonth + '-' + (col_number + 1) | |
$.ajax | |
type: 'POST' | |
url: '/calendars/activities' | |
dataType: 'JSON' | |
data: 'get_act': JSON.stringify(get_act) | |
response: 'text' | |
success: (data) -> | |
# data - array of activities | |
# data[0] - types of activities | |
# data[cur][0] - type of current activity | |
# data[cur][1] - description of current activity | |
# data[cur][2] - cell_id | |
cnt = data.length - 1 | |
init_cnt = data.length - 1 | |
end = '' | |
if cnt == 1 | |
end = 'y ' | |
else | |
end = 'ies ' | |
document.getElementById('act_show').innerHTML = cnt + ' activit' + end | |
body = document.createElement('div') | |
body.setAttribute 'style', 'max-height:300px; overflow: auto; ' | |
add = document.createElement('button') | |
add.innerHTML = '+' | |
add.className = 'btn btn-small' | |
add.style.backgroundColor = '#ffffff' | |
add.style.fontSize = '20px' | |
add.style.fontWeight = 'bold' | |
add.style.color = '#1ab7ea' | |
body.appendChild add | |
body.appendChild document.createElement('br') | |
k = 1 | |
while k <= cnt | |
tmp = document.createElement('div') | |
cross = document.createElement('span') | |
cross.className = 'glyphicon glyphicon-remove pull-right' | |
cross.id = k | |
cross.style.fontSize = '20px' | |
cross.style.color = '#f08080' | |
hr = document.createElement('hr') | |
br = document.createElement('br') | |
tmp.appendChild br | |
tmp.appendChild hr | |
tmp.appendChild cross | |
typeof_activity = document.createElement('p') | |
typeof_activity.innerHTML = data[k][0] | |
typeof_activity.setAttribute 'style', 'font-weight: bold; font-size: 22px; color: #1ab7ea;' | |
description_activity = document.createElement('p') | |
description_activity.innerHTML = data[k][1] | |
description_activity.setAttribute 'style', 'font-weight: bold; font-size: 16px; color: #000000;' | |
tmp.appendChild typeof_activity | |
tmp.appendChild description_activity | |
body.appendChild tmp | |
k++ | |
open_add = false | |
body.onclick = (e) -> | |
target = e.target | |
if target.className == 'glyphicon glyphicon-remove pull-right' | |
if confirm('are you sure? ') | |
act_id = target.id | |
document.getElementById(act_id).parentNode.remove() | |
cnt = cnt - 1 | |
if cnt == 1 | |
end = 'y ' | |
else | |
end = 'ies ' | |
document.getElementById('act_show').innerHTML = cnt + ' activit' + end | |
destroy = | |
type: data[act_id][0] | |
description: data[act_id][1] | |
id: data[act_id][2] | |
$.ajax | |
type: 'POST' | |
url: '/calendars/activities' | |
dataType: 'JSON' | |
data: 'destroy': JSON.stringify(destroy) | |
return | |
return | |
if target.className == 'btn btn-small' | |
if open_add | |
if target.id == 'hide' | |
open_add = false | |
document.getElementById('body_add').remove() | |
document.getElementById('hide').remove() | |
add.innerHTML = '+' | |
add.style.fontSize = '20px' | |
add.style.width = 'auto' | |
return | |
else | |
row = table_data.get_row_by_index(row_number) | |
day_number = col_number + 1 | |
type_id = document.getElementById('type_added') | |
description = document.getElementById('description') | |
if type_id.value == '0' | |
alert 'Select the type of activity, please' | |
else | |
if !(description.value == '' and !confirm('Description is empty. Are you sure?')) | |
$.ajax | |
type: 'POST' | |
url: '/calendars/activities' | |
data: | |
employee_project_id: row.id | |
occurs: currentYear + '-' + currentMonth + '-' + day_number | |
type: type_id.value | |
description: description.value | |
dataType: 'json' | |
success: (cell_id) -> | |
if cell_id == null | |
open_add = false | |
document.getElementById('body_add').remove() | |
document.getElementById('hide').remove() | |
add.innerHTML = '+' | |
add.style.fontSize = '20px' | |
add.style.width = 'auto' | |
return | |
open_add = false | |
cnt = cnt + 1 | |
if cnt == 1 | |
end = 'y ' | |
else | |
end = 'ies ' | |
tmp = document.createElement('div') | |
cross = document.createElement('span') | |
cross.className = 'glyphicon glyphicon-remove pull-right' | |
cross.id = cnt | |
cross.style.fontSize = '20px' | |
cross.style.color = '#f08080' | |
hr = document.createElement('hr') | |
br = document.createElement('br') | |
tmp.appendChild br | |
tmp.appendChild hr | |
tmp.appendChild cross | |
typeof_activity = document.createElement('p') | |
typeof_activity.innerHTML = type_id.options[type_id.selectedIndex].text | |
typeof_activity.setAttribute 'style', 'font-weight: bold; font-size: 22px; color: #1ab7ea;' | |
description_activity = document.createElement('p') | |
description_activity.innerHTML = description.value | |
description_activity.setAttribute 'style', 'font-weight: bold; font-size: 16px; color: #000000;' | |
tmp.appendChild typeof_activity | |
tmp.appendChild description_activity | |
body.appendChild tmp | |
data.push [ | |
type_id.options[type_id.selectedIndex].text | |
description.value | |
cell_id | |
] | |
document.getElementById('act_show').innerHTML = cnt + ' activit' + end | |
document.getElementById('body_add').remove() | |
document.getElementById('hide').remove() | |
add.innerHTML = '+' | |
add.style.fontSize = '20px' | |
add.style.width = 'auto' | |
row.get_day(col_number + 1).activity = true | |
return | |
return | |
else | |
open_add = true | |
hide_add = document.createElement('button') | |
hide_add.id = 'hide' | |
hide_add.innerHTML = 'Hide' | |
hide_add.className = 'btn btn-small' | |
hide_add.style.backgroundColor = '#ffffff' | |
hide_add.style.fontSize = '15px' | |
hide_add.style.color = '#1ab7ea' | |
hide_add.style.width = '50%' | |
hide_add.style.fontWeight = 'bold' | |
selector = document.createElement('select') | |
selector.style = 'height: 2em; width: 25em' | |
selector.id = 'type_added' | |
option = document.createElement('option') | |
option.value = 0 | |
option.selected = true | |
option.text = '...' | |
selector.appendChild option | |
body_add = document.createElement('div') | |
body_add.id = 'body_add' | |
type = undefined | |
type = 0 | |
while type < data[0].length | |
option = document.createElement('option') | |
option.value = data[0][type][1] | |
option.text = data[0][type][0] | |
selector.appendChild option | |
type++ | |
selector.style.color = 'black' | |
act_t = document.createElement('p') | |
act_t.innerHTML = 'Activity type: ' | |
act_t.style.fontWeight = 'bold' | |
act_t.style.fontSize = '20px' | |
act_t.style.color = 'black' | |
body_add.appendChild act_t | |
body_add.appendChild selector | |
body_add.appendChild document.createElement('br') | |
body_add.appendChild document.createElement('br') | |
body_add.appendChild document.createElement('br') | |
desc = document.createElement('p') | |
desc.innerHTML = 'Description: ' | |
desc.style.color = 'black' | |
desc.style.fontWeight = 'bold' | |
desc.style.fontSize = '20px' | |
body_add.appendChild desc | |
description = document.createElement('textarea') | |
description.cols = 5 | |
description.rows = 5 | |
description.id = 'description' | |
description.style = 'width: 25em;' | |
description.style.color = 'black' | |
description.placeholder = 'Type the description here' | |
body_add.appendChild description | |
add.style.width = '50%' | |
add.style.fontSize = '15px' | |
add.innerHTML = 'Add' | |
add.parentNode.insertBefore hide_add, add.nextSibling | |
hide_add.parentNode.insertBefore body_add, hide_add.nextSibling | |
return | |
return | |
document.getElementById('act_body').appendChild body | |
act_show_row = row_number | |
act_show_col = col_number | |
$('#activity_show').modal('show') | |
return | |
when 'comments_remove' | |
setTimeout (-> | |
@delete_comment row_number, col_number | |
), 100 | |
when "clearevent" then deleteEvents( selectedDays) | |
else setEvents selectedDays, event.id | |
createContextMenuItems = () -> | |
items = {} | |
items['commentsAddEdit'] = { | |
disabled: ()-> | |
contextmenu_forbidden(hot.getSelected()) | |
} | |
items['comments_remove'] = { | |
name: 'Delete comments' | |
# Disable 'delete comment' option if there is no comment in that day | |
disabled: ()-> | |
contextmenu_forbidden(hot.getSelected()) | |
} | |
items['hsep'] = {name: "---------"} | |
clearevent = Extradata.events.find (x, i,arr ) -> return x if x.name == "Clear Event" | |
items['clearevent'] = { | |
name: createContextMenuItem(clearevent.color, updateLabel(clearevent.name, clearevent.hotkey)) | |
} | |
for event in Extradata.events | |
name = event.name.replace(/\s+/g, '').toLocaleLowerCase(); | |
items[name] = { | |
'name': createContextMenuItem(event.color, updateLabel(event.name, event.hotkey)) | |
disabled: ()-> | |
contextmenu_forbidden(hot.getSelected()) | |
} | |
items['activity_separator'] = { | |
'name': "---------" | |
}; | |
items['activities'] = { | |
'name': 'Activities' | |
disabled: ()-> | |
contextmenu_forbidden(hot.getSelected()) | |
} | |
items | |
contextmenu_forbidden = (on_selected) -> | |
row_number = on_selected[0] | |
col = on_selected[1] | |
row = table_data.get_row_by_index(row_number) | |
project = (row.type == 'project') | |
maternity = false | |
if row['maternity']['exists'] | |
maternity = ((col + 1) in [row['maternity']['index']..lastDayOfMonth(currentMonth, currentYear)] && row['maternity']['start']) || | |
(!row['maternity']['start'] && (col + 1) in [1..row['maternity']['index']]) | |
total = false | |
if col == columns.length - 1 | |
total = !project | |
fired = (row.end_day != undefined && col >= row.end_day) || (row.start_day != undefined && col + 2 <= row.start_day) | |
project || maternity || fired || total | |
updateSumSelected = (begin_row, begin_column, end_row, end_column)-> | |
sum = 0 | |
for row_index in [begin_row..end_row] | |
for column in [begin_column..end_column] | |
row = table_data.get_row_by_index(row_index) | |
day = row.get_day(column + 1) | |
# selected cell is not a day, e.g. it is total_hours | |
if day == undefined | |
return | |
if (row.workload == undefined || row.workload == null) || | |
type_of_day(day.day) != DayTypes.WORKDAY || | |
(row['end_day'] != undefined || column >= row['end_day']) || | |
(row['start_day'] != undefined && column + 2 <= row['start_day']) | |
workload = 0 | |
else | |
workload = row.workload * 8 / 100 | |
if !(isNaN(day.hours) || day.hours == null || day.hours == undefined) | |
hours = day.hours | |
else if day.event_id == 5 || day.event_id == 6 || day.event_id == 7 | |
hours = 0 | |
else | |
hours = workload | |
sum += parseFloat hours | |
if isFloat sum # check if is float. @see Number.isInteger in ES6 will be implemented in future | |
sum = sum.toFixed(1) | |
$("#selected_sum").html("<b>SUM: " + sum + "</b>") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment