snappymail/dev/Common/Selector.js
2013-12-13 03:23:47 +04:00

536 lines
11 KiB
JavaScript

/* RainLoop Webmail (c) RainLoop Team | Licensed under CC BY-NC-SA 3.0 */
/**
* @constructor
* @param {koProperty} oKoList
* @param {koProperty} oKoSelectedItem
* @param {string} sItemSelector
* @param {string} sItemSelectedSelector
* @param {string} sItemCheckedSelector
*/
function Selector(oKoList, oKoSelectedItem, sItemSelector, sItemSelectedSelector, sItemCheckedSelector)
{
this.list = oKoList;
this.selectedItem = oKoSelectedItem;
this.selectedItem.extend({'toggleSubscribe': [null,
function (oPrev) {
if (oPrev)
{
oPrev.selected(false);
}
}, function (oNext) {
if (oNext)
{
oNext.selected(true);
}
}
]});
this.oContentVisible = null;
this.oContentScrollable = null;
this.sItemSelector = sItemSelector;
this.sItemSelectedSelector = sItemSelectedSelector;
this.sItemCheckedSelector = sItemCheckedSelector;
this.sLastUid = '';
this.oCallbacks = {};
this.iSelectTimer = 0;
this.bUseKeyboard = true;
this.emptyFunction = function () {};
this.useItemSelectCallback = true;
this.throttleSelection = false;
this.selectedItem.subscribe(function (oItem) {
if (this.useItemSelectCallback)
{
if (this.throttleSelection)
{
this.throttleSelection = false;
this.selectItemCallbacksThrottle(oItem);
}
else
{
this.selectItemCallbacks(oItem);
}
}
}, this);
var
self = this,
aCheckedCache = [],
mSelected = null
;
this.list.subscribe(function () {
var self = this, aItems = this.list();
if (Utils.isArray(aItems))
{
_.each(aItems, function (oItem) {
if (oItem.checked())
{
aCheckedCache.push(self.getItemUid(oItem));
}
if (null === mSelected && oItem.selected())
{
mSelected = self.getItemUid(oItem);
}
});
}
}, this, 'beforeChange');
this.list.subscribe(function (aItems) {
this.useItemSelectCallback = false;
this.selectedItem(null);
if (Utils.isArray(aItems))
{
var self = this, iLen = aCheckedCache.length;
_.each(aItems, function (oItem) {
if (0 < iLen && -1 < Utils.inArray(self.getItemUid(oItem), aCheckedCache))
{
oItem.checked(true);
iLen--;
}
if (null !== mSelected && mSelected === self.getItemUid(oItem))
{
oItem.selected(true);
mSelected = null;
self.selectedItem(oItem);
}
});
}
this.useItemSelectCallback = true;
aCheckedCache = [];
mSelected = null;
}, this);
this.list.setSelectedByUid = function (sUid) {
self.selectByUid(sUid, false);
};
this.selectItemCallbacksThrottle = _.debounce(this.selectItemCallbacks, 300);
}
Selector.prototype.selectItemCallbacks = function (oItem)
{
(this.oCallbacks['onItemSelect'] || this.emptyFunction)(oItem);
};
Selector.prototype.goDown = function ()
{
this.newSelectPosition(Enums.EventKeyCode.Down, false);
};
Selector.prototype.goUp = function ()
{
this.newSelectPosition(Enums.EventKeyCode.Up, false);
};
Selector.prototype.init = function (oContentVisible, oContentScrollable)
{
this.oContentVisible = oContentVisible;
this.oContentScrollable = oContentScrollable;
if (this.oContentVisible && this.oContentScrollable)
{
var
self = this
;
$(this.oContentVisible)
.on('click', this.sItemSelector, function (oEvent) {
self.actionClick(ko.dataFor(this), oEvent);
}).on('click', this.sItemCheckedSelector, function (oEvent) {
var oItem = ko.dataFor(this);
if (oItem)
{
if (oEvent && oEvent.shiftKey)
{
self.actionClick(oItem, oEvent);
}
else
{
self.sLastUid = self.getItemUid(oItem);
if (oItem.selected())
{
oItem.checked(false);
self.selectedItem(null);
}
else
{
oItem.checked(!oItem.checked());
}
}
}
})
;
$(window.document).on('keydown', function (oEvent) {
var bResult = true;
if (oEvent && self.bUseKeyboard && !Utils.inFocus())
{
if (-1 < Utils.inArray(oEvent.keyCode, [Enums.EventKeyCode.Up, Enums.EventKeyCode.Down, Enums.EventKeyCode.Insert,
Enums.EventKeyCode.Home, Enums.EventKeyCode.End, Enums.EventKeyCode.PageUp, Enums.EventKeyCode.PageDown]))
{
self.newSelectPosition(oEvent.keyCode, oEvent.shiftKey);
bResult = false;
}
else if (Enums.EventKeyCode.Delete === oEvent.keyCode && !oEvent.ctrlKey && !oEvent.shiftKey)
{
if (self.oCallbacks['onDelete'])
{
self.oCallbacks['onDelete']();
}
bResult = false;
}
}
return bResult;
});
}
};
Selector.prototype.selectByUid = function (mUid, bUseCallback)
{
bUseCallback = Utils.isUnd(bUseCallback) ? true : !!bUseCallback;
this.useItemSelectCallback = bUseCallback;
var
oItem = _.find(this.list(), function (oItem) {
return mUid === this.getItemUid(oItem);
}, this)
;
if (oItem)
{
this.selectedItem(oItem);
}
this.useItemSelectCallback = true;
};
Selector.prototype.useKeyboard = function (bValue)
{
this.bUseKeyboard = !!bValue;
};
/**
* @param {Object} oItem
* @returns {string}
*/
Selector.prototype.getItemUid = function (oItem)
{
var
sUid = '',
fGetItemUidCallback = this.oCallbacks['onItemGetUid'] || null
;
if (fGetItemUidCallback && oItem)
{
sUid = fGetItemUidCallback(oItem);
}
return sUid.toString();
};
/**
* @param {number} iEventKeyCode
* @param {boolean} bShiftKey
*/
Selector.prototype.newSelectPosition = function (iEventKeyCode, bShiftKey)
{
var
self = this,
iIndex = 0,
iPageStep = 10,
bNext = false,
bStop = false,
oResult = null,
aList = this.list(),
iListLen = aList ? aList.length : 0,
oSelected = this.selectedItem()
;
if (0 < iListLen)
{
if (!oSelected)
{
if (Enums.EventKeyCode.Down === iEventKeyCode || Enums.EventKeyCode.Insert === iEventKeyCode || Enums.EventKeyCode.Home === iEventKeyCode || Enums.EventKeyCode.PageUp === iEventKeyCode)
{
oResult = aList[0];
}
else if (Enums.EventKeyCode.Up === iEventKeyCode || Enums.EventKeyCode.End === iEventKeyCode || Enums.EventKeyCode.PageDown === iEventKeyCode)
{
oResult = aList[aList.length - 1];
}
}
else if (oSelected)
{
if (Enums.EventKeyCode.Down === iEventKeyCode || Enums.EventKeyCode.Up === iEventKeyCode || Enums.EventKeyCode.Insert === iEventKeyCode)
{
_.each(aList, function (oItem) {
if (!bStop)
{
switch (iEventKeyCode) {
case Enums.EventKeyCode.Up:
if (oSelected === oItem)
{
bStop = true;
}
else
{
oResult = oItem;
}
break;
case Enums.EventKeyCode.Down:
case Enums.EventKeyCode.Insert:
if (bNext)
{
oResult = oItem;
bStop = true;
}
else if (oSelected === oItem)
{
bNext = true;
}
break;
}
}
});
}
else if (Enums.EventKeyCode.Home === iEventKeyCode || Enums.EventKeyCode.End === iEventKeyCode)
{
if (Enums.EventKeyCode.Home === iEventKeyCode)
{
oResult = aList[0];
}
else if (Enums.EventKeyCode.End === iEventKeyCode)
{
oResult = aList[aList.length - 1];
}
}
else if (Enums.EventKeyCode.PageDown === iEventKeyCode)
{
for (; iIndex < iListLen; iIndex++)
{
if (oSelected === aList[iIndex])
{
iIndex += iPageStep;
iIndex = iListLen - 1 < iIndex ? iListLen - 1 : iIndex;
oResult = aList[iIndex];
break;
}
}
}
else if (Enums.EventKeyCode.PageUp === iEventKeyCode)
{
for (iIndex = iListLen; iIndex >= 0; iIndex--)
{
if (oSelected === aList[iIndex])
{
iIndex -= iPageStep;
iIndex = 0 > iIndex ? 0 : iIndex;
oResult = aList[iIndex];
break;
}
}
}
}
}
if (oResult)
{
if (oSelected)
{
if (bShiftKey)
{
if (Enums.EventKeyCode.Up === iEventKeyCode || Enums.EventKeyCode.Down === iEventKeyCode)
{
oSelected.checked(!oSelected.checked());
}
}
else if (Enums.EventKeyCode.Insert === iEventKeyCode)
{
oSelected.checked(!oSelected.checked());
}
}
this.throttleSelection = true;
this.selectedItem(oResult);
this.throttleSelection = true;
if (0 !== this.iSelectTimer)
{
window.clearTimeout(this.iSelectTimer);
this.iSelectTimer = window.setTimeout(function () {
self.iSelectTimer = 0;
self.actionClick(oResult);
}, 1000);
}
else
{
this.iSelectTimer = window.setTimeout(function () {
self.iSelectTimer = 0;
}, 200);
this.actionClick(oResult);
}
this.scrollToSelected();
}
else if (oSelected)
{
if (bShiftKey && (Enums.EventKeyCode.Up === iEventKeyCode || Enums.EventKeyCode.Down === iEventKeyCode))
{
oSelected.checked(!oSelected.checked());
}
else if (Enums.EventKeyCode.Insert === iEventKeyCode)
{
oSelected.checked(!oSelected.checked());
}
}
};
/**
* @return {boolean}
*/
Selector.prototype.scrollToSelected = function ()
{
if (!this.oContentVisible || !this.oContentScrollable)
{
return false;
}
var
iOffset = 20,
oSelected = $(this.sItemSelectedSelector, this.oContentScrollable),
oPos = oSelected.position(),
iVisibleHeight = this.oContentVisible.height(),
iSelectedHeight = oSelected.outerHeight()
;
if (oPos && (oPos.top < 0 || oPos.top + iSelectedHeight > iVisibleHeight))
{
if (oPos.top < 0)
{
this.oContentScrollable.scrollTop(this.oContentScrollable.scrollTop() + oPos.top - iOffset);
}
else
{
this.oContentScrollable.scrollTop(this.oContentScrollable.scrollTop() + oPos.top - iVisibleHeight + iSelectedHeight + iOffset);
}
return true;
}
return false;
};
Selector.prototype.eventClickFunction = function (oItem, oEvent)
{
var
sUid = this.getItemUid(oItem),
iIndex = 0,
iLength = 0,
oListItem = null,
sLineUid = '',
bChangeRange = false,
bIsInRange = false,
aList = [],
bChecked = false
;
if (oEvent && oEvent.shiftKey)
{
if ('' !== sUid && '' !== this.sLastUid && sUid !== this.sLastUid)
{
aList = this.list();
bChecked = oItem.checked();
for (iIndex = 0, iLength = aList.length; iIndex < iLength; iIndex++)
{
oListItem = aList[iIndex];
sLineUid = this.getItemUid(oListItem);
bChangeRange = false;
if (sLineUid === this.sLastUid || sLineUid === sUid)
{
bChangeRange = true;
}
if (bChangeRange)
{
bIsInRange = !bIsInRange;
}
if (bIsInRange || bChangeRange)
{
oListItem.checked(bChecked);
}
}
}
}
this.sLastUid = '' === sUid ? '' : sUid;
};
/**
*
* @param {Object} oItem
* @param {Object=} oEvent
*/
Selector.prototype.actionClick = function (oItem, oEvent)
{
if (oItem)
{
var
bClick = true,
sUid = this.getItemUid(oItem)
;
if (oEvent)
{
if (oEvent.shiftKey)
{
bClick = false;
if ('' === this.sLastUid)
{
this.sLastUid = sUid;
}
oItem.checked(!oItem.checked());
this.eventClickFunction(oItem, oEvent);
}
else if (oEvent.ctrlKey)
{
bClick = false;
this.sLastUid = sUid;
oItem.checked(!oItem.checked());
}
}
if (bClick)
{
this.selectedItem(oItem);
this.sLastUid = sUid;
}
}
};
Selector.prototype.on = function (sEventName, fCallback)
{
this.oCallbacks[sEventName] = fCallback;
};