File: /home/barbeatleanalyti/public_html/public_html/webmail/modules/CoreWebclient/js/CSelector.js
'use strict';
var
_ = require('underscore'),
$ = require('jquery'),
ko = require('knockout'),
Types = require('%PathToCoreWebclientModule%/js/utils/Types.js'),
Utils = require('%PathToCoreWebclientModule%/js/utils/Common.js'),
App = require('%PathToCoreWebclientModule%/js/App.js'),
Browser = require('%PathToCoreWebclientModule%/js/Browser.js'),
Popups = require('%PathToCoreWebclientModule%/js/Popups.js')
;
/**
* @param {Function} list (knockout)
* @param {Function=} fSelectCallback
* @param {Function=} fDeleteCallback
* @param {Function=} fDblClickCallback
* @param {Function=} fEnterCallback
* @param {Function=} multiplyLineFactor (knockout)
* @param {boolean=} bResetCheckedOnClick = false
* @param {boolean=} bCheckOnSelect = false
* @param {boolean=} bUnselectOnCtrl = false
* @param {boolean=} bDisableMultiplySelection = false
* @param {boolean=} bChangeOnSelect = true
* @constructor
*/
function CSelector(list, fSelectCallback, fDeleteCallback, fDblClickCallback, fEnterCallback, multiplyLineFactor,
bResetCheckedOnClick, bCheckOnSelect, bUnselectOnCtrl, bDisableMultiplySelection, bChangeOnSelect)
{
this.fSelectCallback = fSelectCallback || function() {};
this.fDeleteCallback = fDeleteCallback || function() {};
this.fDblClickCallback = (!App.isMobile() && fDblClickCallback) ? fDblClickCallback : function() {};
this.fEnterCallback = fEnterCallback || function() {};
this.bResetCheckedOnClick = !!bResetCheckedOnClick;
this.bCheckOnSelect = !!bCheckOnSelect;
this.bUnselectOnCtrl = !!bUnselectOnCtrl;
this.bDisableMultiplySelection = !!bDisableMultiplySelection;
this.bChangeOnSelect = (typeof bChangeOnSelect === 'undefined') ? true : !!bChangeOnSelect;
this.useKeyboardKeys = ko.observable(false);
this.list = ko.observableArray([]);
if (list && list['subscribe'])
{
list['subscribe'](function (mValue) {
this.list(mValue);
}, this);
}
this.multiplyLineFactor = multiplyLineFactor;
this.oLast = null;
this.oListScope = null;
this.oScrollScope = null;
this.iTimer = 0;
this.iFactor = 1;
this.KeyUp = Enums.Key.Up;
this.KeyDown = Enums.Key.Down;
this.KeyLeft = Enums.Key.Up;
this.KeyRight = Enums.Key.Down;
if (this.multiplyLineFactor)
{
if (this.multiplyLineFactor.subscribe)
{
this.multiplyLineFactor.subscribe(function (iValue) {
this.iFactor = 0 < iValue ? iValue : 1;
}, this);
}
else
{
this.iFactor = Types.pInt(this.multiplyLineFactor);
}
this.KeyUp = Enums.Key.Up;
this.KeyDown = Enums.Key.Down;
this.KeyLeft = Enums.Key.Left;
this.KeyRight = Enums.Key.Right;
if ($('html').hasClass('rtl'))
{
this.KeyLeft = Enums.Key.Right;
this.KeyRight = Enums.Key.Left;
}
}
this.sActionSelector = '';
this.sSelectabelSelector = '';
this.sCheckboxSelector = '';
var self = this;
// reading returns a list of checked items.
// recording (bool) puts all checked, or unchecked.
this.listChecked = ko.computed({
'read': function () {
var aList = _.filter(this.list(), function (oItem) {
var
bC = oItem.checked(),
bS = oItem.selected()
;
return bC || (self.bCheckOnSelect && bS);
});
return aList;
},
'write': function (bValue) {
bValue = !!bValue;
_.each(this.list(), function (oItem) {
oItem.checked(bValue);
});
this.list.valueHasMutated();
},
'owner': this
});
this.checkAll = ko.computed({
'read': function () {
return 0 < this.listChecked().length;
},
'write': function (bValue) {
this.listChecked(!!bValue);
},
'owner': this
});
this.selectorHook = ko.observable(null);
this.selectorHook.subscribe(function () {
var oPrev = this.selectorHook();
if (oPrev)
{
oPrev.selected(false);
}
}, this, 'beforeChange');
this.selectorHook.subscribe(function (oGroup) {
if (oGroup)
{
oGroup.selected(true);
}
}, this);
this.itemSelected = ko.computed({
'read': this.selectorHook,
'write': function (oItemToSelect) {
this.selectorHook(oItemToSelect);
if (oItemToSelect)
{
self.scrollToSelected();
this.oLast = oItemToSelect;
}
},
'owner': this
});
this.list.subscribe(function (aList) {
if (_.isArray(aList))
{
var oSelected = this.itemSelected();
if (oSelected)
{
if (!_.find(aList, function (oItem) {
return oSelected === oItem;
}))
{
this.itemSelected(null);
}
}
}
else
{
this.itemSelected(null);
}
}, this);
this.listCheckedOrSelected = ko.computed({
'read': function () {
var
oSelected = this.itemSelected(),
aChecked = this.listChecked()
;
return 0 < aChecked.length ? aChecked : (oSelected ? [oSelected] : []);
},
'write': function (bValue) {
if (!bValue)
{
this.itemSelected(null);
this.listChecked(false);
}
else
{
this.listChecked(true);
}
},
'owner': this
});
this.listCheckedAndSelected = ko.computed({
'read': function () {
var
aResult = [],
oSelected = this.itemSelected(),
aChecked = this.listChecked()
;
if (aChecked)
{
aResult = aChecked.slice(0);
}
if (oSelected && _.indexOf(aChecked, oSelected) === -1)
{
aResult.push(oSelected);
}
return aResult;
},
'write': function (bValue) {
if (!bValue)
{
this.itemSelected(null);
this.listChecked(false);
}
else
{
this.listChecked(true);
}
},
'owner': this
});
this.isIncompleteChecked = ko.computed(function () {
var
iM = this.list().length,
iC = this.listChecked().length
;
return 0 < iM && 0 < iC && iM > iC;
}, this);
this.onKeydownBound = _.bind(this.onKeydown, this);
}
CSelector.prototype.iTimer = 0;
CSelector.prototype.bResetCheckedOnClick = false;
CSelector.prototype.bCheckOnSelect = false;
CSelector.prototype.bUnselectOnCtrl = false;
CSelector.prototype.bDisableMultiplySelection = false;
CSelector.prototype.getLastOrSelected = function ()
{
var
iCheckedCount = 0,
oLastSelected = null
;
_.each(this.list(), function (oItem) {
if (oItem.checked())
{
iCheckedCount++;
}
if (oItem.selected())
{
oLastSelected = oItem;
}
});
return 0 === iCheckedCount && oLastSelected ? oLastSelected : this.oLast;
};
/**
* @param {string} sActionSelector css-selector for the active for pressing regions of the list
* @param {string} sSelectabelSelector css-selector to the item that was selected
* @param {string} sCheckboxSelector css-selector to the element that checkbox in the list
* @param {*} oListScope
* @param {*} oScrollScope
*/
CSelector.prototype.initOnApplyBindings = function (sActionSelector, sSelectabelSelector, sCheckboxSelector, oListScope, oScrollScope)
{
$(document).on('keydown', this.onKeydownBound);
this.oListScope = oListScope;
this.oScrollScope = oScrollScope;
this.sActionSelector = sActionSelector;
this.sSelectabelSelector = sSelectabelSelector;
this.sCheckboxSelector = sCheckboxSelector;
var
self = this,
fEventClickFunction = function (oLast, oItem, oEvent) {
var
iIndex = 0,
iLength = 0,
oListItem = null,
bChangeRange = false,
bIsInRange = false,
aList = [],
bChecked = false
;
oItem = oItem ? oItem : null;
if (oEvent && oEvent.shiftKey)
{
if (null !== oItem && null !== oLast && oItem !== oLast)
{
aList = self.list();
bChecked = oItem.checked();
for (iIndex = 0, iLength = aList.length; iIndex < iLength; iIndex++)
{
oListItem = aList[iIndex];
bChangeRange = false;
if (oListItem === oLast || oListItem === oItem)
{
bChangeRange = true;
}
if (bChangeRange)
{
bIsInRange = !bIsInRange;
}
if (bIsInRange || bChangeRange)
{
oListItem.checked(bChecked);
}
}
}
}
if (oItem)
{
self.oLast = oItem;
}
}
;
$(this.oListScope).on('dblclick', sActionSelector, function (oEvent) {
var oItem = ko.dataFor(this);
if (oItem && oEvent && !oEvent.ctrlKey && !oEvent.altKey && !oEvent.shiftKey)
{
self.onDblClick(oItem);
}
});
if (Browser.mobileDevice)
{
$(this.oListScope).on('touchstart', sActionSelector, function (e) {
if (!e)
{
return;
}
var
t2 = e.timeStamp,
t1 = $(this).data('lastTouch') || t2,
dt = t2 - t1,
fingers = e.originalEvent && e.originalEvent.touches ? e.originalEvent.touches.length : 0
;
$(this).data('lastTouch', t2);
if (!dt || dt > 250 || fingers > 1)
{
return;
}
e.preventDefault();
$(this).trigger('dblclick');
});
}
$(this.oListScope).on('click', sActionSelector, function (oEvent) {
var
bClick = true,
oSelected = null,
oLast = self.getLastOrSelected(),
oItem = ko.dataFor(this)
;
if (oItem && oEvent)
{
if (oEvent.shiftKey)
{
bClick = false;
if (!self.bDisableMultiplySelection)
{
if (null === self.oLast)
{
self.oLast = oItem;
}
oItem.checked(!oItem.checked());
fEventClickFunction(oLast, oItem, oEvent);
}
}
else if (oEvent.ctrlKey)
{
bClick = false;
if (!self.bDisableMultiplySelection)
{
self.oLast = oItem;
oSelected = self.itemSelected();
if (oSelected && !oSelected.checked() && !oItem.checked())
{
oSelected.checked(true);
}
if (self.bUnselectOnCtrl && oItem === self.itemSelected())
{
oItem.checked(!oItem.selected());
self.itemSelected(null);
}
else
{
oItem.checked(!oItem.checked());
}
}
}
if (bClick)
{
self.selectionFunc(oItem);
}
}
});
$(this.oListScope).on('click', sCheckboxSelector, function (oEvent) {
var oItem = ko.dataFor(this);
if (oItem && oEvent && !self.bDisableMultiplySelection)
{
if (oEvent.shiftKey)
{
if (null === self.oLast)
{
self.oLast = oItem;
}
fEventClickFunction(self.getLastOrSelected(), oItem, oEvent);
}
else
{
self.oLast = oItem;
}
}
if (oEvent && oEvent.stopPropagation)
{
oEvent.stopPropagation();
}
});
$(this.oListScope).on('dblclick', sCheckboxSelector, function (oEvent) {
if (oEvent && oEvent.stopPropagation)
{
oEvent.stopPropagation();
}
});
};
/**
* @param {Object} oSelected
* @param {number} iEventKeyCode
*
* @return {Object}
*/
CSelector.prototype.getResultSelection = function (oSelected, iEventKeyCode)
{
var
self = this,
bStop = false,
bNext = false,
oResult = null,
iPageStep = this.iFactor,
bMultiply = !!this.multiplyLineFactor,
iIndex = 0,
iLen = 0,
aList = []
;
if (!oSelected && -1 < $.inArray(iEventKeyCode, [this.KeyUp, this.KeyDown, this.KeyLeft, this.KeyRight,
Enums.Key.PageUp, Enums.Key.PageDown, Enums.Key.Home, Enums.Key.End]))
{
aList = this.list();
if (aList && 0 < aList.length)
{
if (-1 < $.inArray(iEventKeyCode, [this.KeyDown, this.KeyRight, Enums.Key.PageUp, Enums.Key.Home]))
{
oResult = aList[0];
}
else if (-1 < $.inArray(iEventKeyCode, [this.KeyUp, this.KeyLeft, Enums.Key.PageDown, Enums.Key.End]))
{
oResult = aList[aList.length - 1];
}
}
}
else if (oSelected)
{
aList = this.list();
iLen = aList ? aList.length : 0;
if (0 < iLen)
{
if (
Enums.Key.Home === iEventKeyCode || Enums.Key.PageUp === iEventKeyCode ||
Enums.Key.End === iEventKeyCode || Enums.Key.PageDown === iEventKeyCode ||
(bMultiply && (Enums.Key.Left === iEventKeyCode || Enums.Key.Right === iEventKeyCode)) ||
(!bMultiply && (Enums.Key.Up === iEventKeyCode || Enums.Key.Down === iEventKeyCode))
)
{
_.each(aList, function (oItem) {
if (!bStop)
{
switch (iEventKeyCode) {
case self.KeyUp:
case self.KeyLeft:
if (oSelected === oItem)
{
bStop = true;
}
else
{
oResult = oItem;
}
break;
case Enums.Key.Home:
case Enums.Key.PageUp:
oResult = oItem;
bStop = true;
break;
case self.KeyDown:
case self.KeyRight:
if (bNext)
{
oResult = oItem;
bStop = true;
}
else if (oSelected === oItem)
{
bNext = true;
}
break;
case Enums.Key.End:
case Enums.Key.PageDown:
oResult = oItem;
break;
}
}
});
}
else if (bMultiply && this.KeyDown === iEventKeyCode)
{
for (; iIndex < iLen; iIndex++)
{
if (oSelected === aList[iIndex])
{
iIndex += iPageStep;
if (iLen - 1 < iIndex)
{
iIndex -= iPageStep;
}
oResult = aList[iIndex];
break;
}
}
}
else if (bMultiply && this.KeyUp === iEventKeyCode)
{
for (iIndex = iLen; iIndex >= 0; iIndex--)
{
if (oSelected === aList[iIndex])
{
iIndex -= iPageStep;
if (0 > iIndex)
{
iIndex += iPageStep;
}
oResult = aList[iIndex];
break;
}
}
}
}
}
return oResult;
};
/**
* @param {Object} oResult
* @param {Object} oSelected
* @param {number} iEventKeyCode
*/
CSelector.prototype.shiftClickResult = function (oResult, oSelected, iEventKeyCode)
{
if (oSelected)
{
var
bMultiply = !!this.multiplyLineFactor,
bInRange = false,
bSelected = false
;
if (-1 < $.inArray(iEventKeyCode,
bMultiply ? [Enums.Key.Left, Enums.Key.Right] : [Enums.Key.Up, Enums.Key.Down]))
{
oSelected.checked(!oSelected.checked());
}
else if (-1 < $.inArray(iEventKeyCode, bMultiply ?
[Enums.Key.Up, Enums.Key.Down, Enums.Key.PageUp, Enums.Key.PageDown, Enums.Key.Home, Enums.Key.End] :
[Enums.Key.Left, Enums.Key.Right, Enums.Key.PageUp, Enums.Key.PageDown, Enums.Key.Home, Enums.Key.End]
))
{
bSelected = !oSelected.checked();
_.each(this.list(), function (oItem) {
var Add = false;
if (oItem === oResult || oSelected === oItem)
{
bInRange = !bInRange;
Add = true;
}
if (bInRange || Add)
{
oItem.checked(bSelected);
Add = false;
}
});
if (bMultiply && oResult && (iEventKeyCode === Enums.Key.Up || iEventKeyCode === Enums.Key.Down))
{
oResult.checked(!oResult.checked());
}
}
}
};
/**
* @param {number} iEventKeyCode
* @param {boolean} bShiftKey
*/
CSelector.prototype.clickNewSelectPosition = function (iEventKeyCode, bShiftKey)
{
var
oSelected = this.itemSelected(),
oResult = this.getResultSelection(oSelected, iEventKeyCode)
;
if (oResult)
{
if (bShiftKey)
{
this.shiftClickResult(oResult, oSelected, iEventKeyCode);
}
this.selectionFunc(oResult);
}
};
/**
* @param {Object} oEvent
*
* @return {boolean}
*/
CSelector.prototype.onKeydown = function (oEvent)
{
var
bResult = true,
iCode = 0
;
if (this.useKeyboardKeys() && oEvent && !Utils.isTextFieldFocused() && !Popups.hasOpenedMaximizedPopups())
{
iCode = oEvent.keyCode;
if (!oEvent.ctrlKey &&
(
this.KeyUp === iCode || this.KeyDown === iCode ||
this.KeyLeft === iCode || this.KeyRight === iCode ||
Enums.Key.PageUp === iCode || Enums.Key.PageDown === iCode ||
Enums.Key.Home === iCode || Enums.Key.End === iCode
)
)
{
this.clickNewSelectPosition(iCode, oEvent.shiftKey);
bResult = false;
}
else if (Enums.Key.Del === iCode && !oEvent.ctrlKey && !oEvent.shiftKey)
{
if (0 < this.list().length)
{
this.onDelete();
bResult = false;
}
}
else if (Enums.Key.Enter === iCode)
{
if (0 < this.list().length && !oEvent.ctrlKey)
{
this.onEnter(this.itemSelected());
bResult = false;
}
}
else if (oEvent.ctrlKey && !oEvent.altKey && !oEvent.shiftKey && Enums.Key.a === iCode)
{
this.checkAll(!(this.checkAll() && !this.isIncompleteChecked()));
bResult = false;
}
}
return bResult;
};
CSelector.prototype.onDelete = function ()
{
this.fDeleteCallback.call(this, this.listCheckedOrSelected());
};
/**
* @param {Object} oItem
*/
CSelector.prototype.onEnter = function (oItem)
{
this.fEnterCallback.call(this, oItem);
};
/**
* @param {Object} oItem
*/
CSelector.prototype.selectionFunc = function (oItem)
{
if (this.bChangeOnSelect)
{
this.itemSelected(null);
}
if (this.bResetCheckedOnClick)
{
this.listChecked(false);
}
if (this.bChangeOnSelect)
{
this.itemSelected(oItem);
}
this.fSelectCallback.call(this, oItem);
};
/**
* @param {Object} oItem
*/
CSelector.prototype.onDblClick = function (oItem)
{
this.fDblClickCallback.call(this, oItem);
};
CSelector.prototype.koCheckAll = function ()
{
return ko.computed({
'read': this.checkAll,
'write': this.checkAll,
'owner': this
});
};
CSelector.prototype.koCheckAllIncomplete = function ()
{
return ko.computed({
'read': this.isIncompleteChecked,
'write': this.isIncompleteChecked,
'owner': this
});
};
/**
* @return {boolean}
*/
CSelector.prototype.scrollToSelected = function ()
{
if (!this.oListScope || !this.oScrollScope)
{
return false;
}
var
iOffset = 20,
oSelected = $(this.sSelectabelSelector, this.oScrollScope),
oPos = oSelected.position(),
iVisibleHeight = this.oScrollScope.height(),
iSelectedHeight = oSelected.outerHeight()
;
if (oPos && (oPos.top < 0 || oPos.top + iSelectedHeight > iVisibleHeight))
{
if (oPos.top < 0)
{
this.oScrollScope.scrollTop(this.oScrollScope.scrollTop() + oPos.top - iOffset);
}
else
{
this.oScrollScope.scrollTop(this.oScrollScope.scrollTop() + oPos.top - iVisibleHeight + iSelectedHeight + iOffset);
}
return true;
}
return false;
};
module.exports = CSelector;