Files
iTop/js/layouts/activity-panel.js
2020-10-02 10:54:40 +02:00

295 lines
9.5 KiB
JavaScript

/*
* Copyright (C) 2013-2020 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
;
$(function()
{
$.widget( 'itop.activity_panel',
{
// default options
options:
{
datetime_format: null,
datetimes_reformat_limit: 14, // In days
},
css_classes:
{
is_expanded: 'ibo-is-expanded',
is_opened: 'ibo-is-opened',
is_closed: 'ibo-is-closed',
is_active: 'ibo-is-active',
is_hidden: 'ibo-is-hidden',
},
js_selectors:
{
panel_size_toggler: '[data-role="ibo-activity-panel--size-toggler"]',
tab: '[data-role="ibo-activity-panel--tab"]',
tab_title: '[data-role="ibo-activity-panel--tab-title"]',
activity_tab_filter: '[data-role="ibo-activity-panel--activity-filter"]',
caselog_tab_open_all: '[data-role="ibo-activity-panel--caselog-open-all"]',
caselog_tab_close_all: '[data-role="ibo-activity-panel--caselog-close-all"]',
entry_group: '[data-role="ibo-activity-panel--entry-group"]',
entry: '[data-role="ibo-activity-entry"]',
entry_main_information: '[data-role="ibo-activity-entry--main-information"]',
entry_datetime: '[data-role="ibo-activity-entry--datetime"]',
edits_entry_long_description: '[data-role="ibo-edits-entry--long-description"]',
edits_entry_long_description_toggler: '[data-role="ibo-edits-entry--long-description-toggler"]',
},
// the constructor
_create: function()
{
this.element.addClass('ibo-activity-panel');
this._bindEvents();
this._ReformatDateTimes();
},
// events bound via _bind are removed automatically
// revert other modifications here
_destroy: function()
{
this.element.removeClass('ibo-activity-panel');
},
_bindEvents: function()
{
const me = this;
const oBodyElem = $('body');
// Click on collapse/expand toggler
this.element.find(this.js_selectors.panel_size_toggler).on('click', function(oEvent){
me._onTogglerClick(oEvent);
});
// Click on tab title
this.element.find(this.js_selectors.tab_title).on('click', function(oEvent){
me._onTabTitleClick(oEvent, $(this));
});
// Change on activity filters
this.element.find(this.js_selectors.activity_tab_filter).on('change', function(){
me._onActivityFilterChange($(this));
});
// Click on open all case log messages
this.element.find(this.js_selectors.caselog_tab_open_all).on('click', function(){
me._onCaseLogOpenAllClick($(this));
});
// Click on close all case log messages
this.element.find(this.js_selectors.caselog_tab_close_all).on('click', function(){
me._onCaseLogCloseAllClick($(this));
});
// Click on a closed case log message
this.element.find(this.js_selectors.entry_group).on('click', '.'+this.css_classes.is_closed + ' ' + this.js_selectors.entry_main_information, function(oEvent){
me._onCaseLogClosedMessageClick($(this).closest(me.js_selectors.entry));
});
// Click on an edits entry long description toggler
this.element.find(this.js_selectors.edits_entry_long_description_toggler).on('click', function(oEvent){
me._onEditsTogglerClick(oEvent, $(this).closest(me.js_selectors.entry));
});
// Mostly for outside clicks that should close elements
oBodyElem.on('click', function(oEvent){
me._onBodyClick(oEvent);
});
// Mostly for hotkeys
oBodyElem.on('keyup', function(oEvent){
me._onBodyKeyUp(oEvent);
});
},
// Events callbacks
_onTogglerClick: function(oEvent)
{
// Avoid anchor glitch
oEvent.preventDefault();
// Toggle menu
this.element.toggleClass(this.css_classes.is_expanded);
},
_onTabTitleClick: function(oEvent, oTabTitleElem)
{
// Avoid anchor glitch
oEvent.preventDefault();
const oTabElem = oTabTitleElem.closest(this.js_selectors.tab);
this.element.find(this.js_selectors.tab).removeClass(this.css_classes.is_active);
oTabElem.addClass(this.css_classes.is_active);
if(oTabElem.attr('data-tab-type') === 'caselog')
{
this._ShowCaseLogTab(oTabElem.attr('data-caselog-attribute-code'))
}
else
{
this._ShowActivityTab();
}
},
_onActivityFilterChange: function(oInputElem)
{
this._ApplyEntryFilters();
},
_onCaseLogOpenAllClick: function(oIconElem)
{
const sCaseLogAttCode = oIconElem.closest(this.js_selectors.tab).attr('data-caselog-attribute-code');
this._OpenAllMessages(sCaseLogAttCode);
},
_onCaseLogCloseAllClick: function(oIconElem)
{
const sCaseLogAttCode = oIconElem.closest(this.js_selectors.tab).attr('data-caselog-attribute-code');
this._CloseAllMessages(sCaseLogAttCode);
},
_onCaseLogClosedMessageClick: function(oEntryElem)
{
this._OpenMessage(oEntryElem);
},
_onEditsTogglerClick: function(oEvent, oEntryElem)
{
// Avoid anchor glitch
oEvent.preventDefault();
oEntryElem.toggleClass(this.css_classes.is_opened);
},
_onBodyClick: function(oEvent)
{
},
_onBodyKeyUp: function(oEvent)
{
},
// Methods
// - Helpers on dates
/**
* Reformat date times to be relative (only if they are not too far in the past)
* @private
*/
_ReformatDateTimes: function()
{
const me = this;
this.element.find(this.js_selectors.entry_datetime).each(function(){
const oEntryDateTime = moment($(this).text(), me.options.datetime_format);
const oNowDateTime = moment();
// Reformat date time only if it is not too far in the past (eg. "2 years ago" is not easy to interpret)
const fDays = moment.duration(oNowDateTime.diff(oEntryDateTime)).asDays();
if(fDays < me.options.datetimes_reformat_limit)
{
$(this).text( moment($(this).text(), me.options.datetime_format).fromNow() );
}
});
},
// - Helpers on tabs
_ShowCaseLogTab: function(sCaseLogAttCode)
{
// Show only entries from this case log
this._HideAllEntries();
this.element.find(this.js_selectors.entry+'[data-entry-caselog-attribute-code="'+sCaseLogAttCode+'"]').removeClass(this.css_classes.is_hidden);
this._UpdateEntryGroupsVisibility();
this.element.trigger('show-caselog-tab', ['caselog', sCaseLogAttCode]);
},
_ShowActivityTab: function()
{
// Show all entries but regarding the current filters
this._OpenAllMessages();
this._ShowAllEntries();
this._ApplyEntryFilters();
this.element.trigger('show-caselog-tab', 'activity');
},
// - Helpers on messages
_OpenMessage: function(oEntryElem)
{
oEntryElem.removeClass(this.css_classes.is_closed);
},
_OpenAllMessages: function(sCaseLogAttCode = null)
{
this._SwitchAllMessages('open', sCaseLogAttCode);
},
_CloseAllMessages: function(sCaseLogAttCode = null)
{
this._SwitchAllMessages('close', sCaseLogAttCode);
},
_SwitchAllMessages: function(sMode, sCaseLogAttCode = null)
{
const sExtraSelector = (sCaseLogAttCode === null) ? '' : '[data-entry-caselog-attribute-code="' + sCaseLogAttCode+'"]';
const sCallback = (sMode === 'open') ? 'removeClass' : 'addClass';
this.element.find(this.js_selectors.entry + sExtraSelector)[sCallback](this.css_classes.is_closed);
},
// - Helpers on entries
_ApplyEntryFilters: function()
{
const me = this;
this.element.find(this.js_selectors.activity_tab_filter).each(function(){
const aTargetEntryTypes = $(this).attr('data-target-entry-types').split(' ');
const sCallbackMethod = ($(this).prop('checked')) ? '_ShowEntries' : '_HideEntries';
for(let iIdx in aTargetEntryTypes)
{
me[sCallbackMethod](aTargetEntryTypes[iIdx]);
}
});
},
_ShowAllEntries: function()
{
this.element.find(this.js_selectors.entry).removeClass(this.css_classes.is_hidden);
this._UpdateEntryGroupsVisibility();
},
_HideAllEntries: function()
{
this.element.find(this.js_selectors.entry).addClass(this.css_classes.is_hidden);
this._UpdateEntryGroupsVisibility();
},
/**
* Show entries of type sEntryType but do not hide the others
*
* @param sEntryType string
* @private
*/
_ShowEntries: function(sEntryType)
{
this.element.find(this.js_selectors.entry+'[data-entry-type="'+sEntryType+'"]').removeClass(this.css_classes.is_hidden);
this._UpdateEntryGroupsVisibility();
},
/**
* Hide entries of type sEntryType but do not hide the others
*
* @param sEntryType string
* @private
*/
_HideEntries: function(sEntryType)
{
this.element.find(this.js_selectors.entry+'[data-entry-type="'+sEntryType+'"]').addClass(this.css_classes.is_hidden);
this._UpdateEntryGroupsVisibility();
},
_UpdateEntryGroupsVisibility: function()
{
const me = this;
this.element.find(this.js_selectors.entry_group).each(function(){
if($(this).find(me.js_selectors.entry + ':not(.' + me.css_classes.is_hidden + ')').length === 0)
{
$(this).addClass(me.css_classes.is_hidden);
}
else
{
$(this).removeClass(me.css_classes.is_hidden);
}
});
}
});
});