mirror of
https://github.com/moparisthebest/mail
synced 2024-11-11 03:35:01 -05:00
333 lines
11 KiB
JavaScript
333 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
var ngModule = angular.module('woDirectives');
|
|
|
|
ngModule.directive('woTouch', function($parse) {
|
|
var className = 'wo-touch-active';
|
|
|
|
return function(scope, elm, attrs) {
|
|
var handler = $parse(attrs.woTouch);
|
|
|
|
elm.on('touchstart', function() {
|
|
elm.addClass(className);
|
|
});
|
|
elm.on('touchleave touchcancel touchmove touchend', function() {
|
|
elm.removeClass(className);
|
|
});
|
|
|
|
elm.on('click', function(event) {
|
|
elm.removeClass(className);
|
|
scope.$apply(function() {
|
|
handler(scope, {
|
|
$event: event
|
|
});
|
|
});
|
|
});
|
|
};
|
|
});
|
|
|
|
ngModule.directive('woTooltip', function($document, $timeout) {
|
|
return function(scope, elm, attrs) {
|
|
var selector = attrs.woTooltip;
|
|
var tooltip = $document.find(selector);
|
|
|
|
elm.on('mouseover', function() {
|
|
// Compute tooltip position
|
|
var offsetElm = elm.offset();
|
|
var offsetTooltipParent = tooltip.offsetParent().offset();
|
|
|
|
// Set tooltip position
|
|
tooltip[0].style.top = (offsetElm.top - offsetTooltipParent.top +
|
|
elm[0].offsetHeight / 2 - tooltip[0].offsetHeight / 2) + 'px';
|
|
tooltip[0].style.left = (offsetElm.left - offsetTooltipParent.left +
|
|
elm[0].offsetWidth) + 'px';
|
|
|
|
// Wait till browser repaint
|
|
$timeout(function() {
|
|
tooltip.addClass('tooltip--show');
|
|
});
|
|
});
|
|
|
|
elm.on('mouseout', function() {
|
|
tooltip.removeClass('tooltip--show');
|
|
tooltip[0].style.top = '-9999px';
|
|
tooltip[0].style.left = '-9999px';
|
|
});
|
|
};
|
|
});
|
|
|
|
ngModule.directive('woDropdown', function($document, $timeout) {
|
|
return function(scope, elm, attrs) {
|
|
var selector = attrs.woDropdown;
|
|
var position = (attrs.woDropdownPosition || '').split(' ');
|
|
var dropdown = $document.find(selector);
|
|
|
|
function appear() {
|
|
// Compute dropdown position
|
|
var offsetElm = elm.offset();
|
|
var offsetDropdownParent = dropdown.offsetParent().offset();
|
|
|
|
// Set dropdown position
|
|
switch(position[1]) {
|
|
case 'up':
|
|
dropdown[0].style.top = (offsetElm.top - offsetDropdownParent.top -
|
|
dropdown[0].offsetHeight - 3) + 'px';
|
|
break;
|
|
default:
|
|
dropdown[0].style.top = (offsetElm.top - offsetDropdownParent.top +
|
|
elm[0].offsetHeight + 3) + 'px';
|
|
}
|
|
|
|
switch(position[0]) {
|
|
case 'center':
|
|
dropdown[0].style.left = (offsetElm.left - offsetDropdownParent.left +
|
|
elm[0].offsetWidth / 2 - dropdown[0].offsetWidth / 2) + 'px';
|
|
break;
|
|
case 'right':
|
|
dropdown[0].style.left = (offsetElm.left - offsetDropdownParent.left +
|
|
elm[0].offsetWidth - dropdown[0].offsetWidth) + 'px';
|
|
break;
|
|
default:
|
|
dropdown[0].style.left = (offsetElm.left - offsetDropdownParent.left) + 'px';
|
|
}
|
|
|
|
// Wait till browser repaint
|
|
$timeout(function() {
|
|
dropdown.addClass('dropdown--show');
|
|
});
|
|
|
|
// class on element to change style if drowdown is visible
|
|
elm.addClass('wo-dropdown-active');
|
|
}
|
|
|
|
function disappear() {
|
|
dropdown.removeClass('dropdown--show');
|
|
dropdown[0].style.top = '-9999px';
|
|
dropdown[0].style.left = '-9999px';
|
|
|
|
elm.removeClass('wo-dropdown-active');
|
|
}
|
|
|
|
function toggle() {
|
|
if(dropdown.hasClass('dropdown--show')) {
|
|
disappear();
|
|
}
|
|
else {
|
|
appear();
|
|
}
|
|
}
|
|
|
|
elm.on('click', function(e) {
|
|
e.preventDefault();
|
|
toggle();
|
|
});
|
|
|
|
// close if user clicks button in dropdown list
|
|
dropdown.on('click.woDropdown', 'button', disappear);
|
|
|
|
// close if user clicks outside of dropdown and elm
|
|
$document.on('touchstart.woDropdown click.woDropdown', function(e) {
|
|
var t = angular.element(e.target);
|
|
if(dropdown.hasClass('dropdown--show') &&
|
|
!t.closest(dropdown).length &&
|
|
!t.closest(elm).length) {
|
|
disappear();
|
|
}
|
|
});
|
|
|
|
// remove event listener on document
|
|
scope.$on('$destroy', function() {
|
|
dropdown.off('click.woDropdown');
|
|
$document.off('touchstart.woDropdown click.woDropdown');
|
|
});
|
|
};
|
|
});
|
|
|
|
ngModule.directive('woFocusMe', function($timeout, $parse) {
|
|
return function(scope, element, attrs) {
|
|
var model = $parse(attrs.woFocusMe);
|
|
scope.$watch(model, function(value) {
|
|
if (value === true) {
|
|
$timeout(function() {
|
|
var el = element[0];
|
|
el.focus();
|
|
// set cursor to start of textarea
|
|
if (el.type === 'textarea') {
|
|
el.selectionStart = 0;
|
|
el.selectionEnd = 0;
|
|
el.scrollTop = 0;
|
|
}
|
|
}, 100);
|
|
}
|
|
});
|
|
};
|
|
});
|
|
|
|
ngModule.directive('woClickFileInput', function() {
|
|
return function(scope, elm, attrs) {
|
|
var fileInput = document.querySelector(attrs.woClickFileInput);
|
|
elm.on('click touchstart', function(e) {
|
|
e.preventDefault();
|
|
fileInput.click();
|
|
});
|
|
};
|
|
});
|
|
|
|
ngModule.directive('woInputCode', function() {
|
|
var BLOCK_SIZE = 4;
|
|
var NUM_BLOCKS = 6;
|
|
var BLOCK_DIVIDER = '-';
|
|
|
|
// helpers
|
|
|
|
function getBlockIndex(code, pos) {
|
|
return code.substr(0, pos).replace(new RegExp('[^' + BLOCK_DIVIDER + ']', 'g'), '').length;
|
|
}
|
|
|
|
function getBlockDimensions(code, i) {
|
|
var start = 0;
|
|
var end = code.length;
|
|
var found = 0;
|
|
for(var j = 0; j < code.length; j++) {
|
|
if(code[j] === BLOCK_DIVIDER) {
|
|
found++;
|
|
if(found === i) {
|
|
start = j + 1;
|
|
}
|
|
if(found === i + 1) {
|
|
end = j - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
start: start,
|
|
end: end
|
|
};
|
|
}
|
|
|
|
function getBlock(code, i) {
|
|
var dims = getBlockDimensions(code, i);
|
|
return code.substring(dims.start, dims.end + 1);
|
|
}
|
|
|
|
function setBlock(code, i, val) {
|
|
var dims = getBlockDimensions(code, i);
|
|
return code.substring(0, dims.start) + val + code.substring(dims.end + 1);
|
|
}
|
|
|
|
return {
|
|
restrict: 'A',
|
|
require: 'ngModel',
|
|
link: function(scope, elm, attrs, ngModelCtrl) {
|
|
function format(val) {
|
|
var str = '';
|
|
|
|
// check if value exists
|
|
if (!val) {
|
|
return str;
|
|
}
|
|
|
|
for(var i = 0; i < val.length; i++) {
|
|
if(i > 0 && i % BLOCK_SIZE === 0) {
|
|
str += BLOCK_DIVIDER;
|
|
}
|
|
str += val[i];
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
function parse(val) {
|
|
var parsed = val.replace(new RegExp(BLOCK_DIVIDER, 'g'), '').trim();
|
|
return parsed;
|
|
}
|
|
|
|
ngModelCtrl.$parsers.push(parse);
|
|
ngModelCtrl.$formatters.push(format);
|
|
|
|
var maxlength = NUM_BLOCKS * (BLOCK_SIZE + 1) - 1;
|
|
|
|
function handleInput(el) {
|
|
var start = el.selectionStart;
|
|
var end = el.selectionEnd;
|
|
var val = el.value;
|
|
|
|
var blockIndex = getBlockIndex(val, start);
|
|
var blockDims = getBlockDimensions(val, blockIndex);
|
|
var block = getBlock(val, blockIndex);
|
|
|
|
// add new block to the end
|
|
if(val.length < maxlength && start === val.length && block.length > BLOCK_SIZE) {
|
|
val = setBlock(val, blockIndex, block.substr(0, BLOCK_SIZE) + BLOCK_DIVIDER + block.substr(BLOCK_SIZE));
|
|
start = val.length;
|
|
end = val.length;
|
|
}
|
|
// maxsize in last block
|
|
else if(start === val.length && block.length > BLOCK_SIZE) {
|
|
val = setBlock(val, blockIndex, block.substr(0, BLOCK_SIZE));
|
|
start = val.length;
|
|
end = val.length;
|
|
}
|
|
// overwrite next char if block is full
|
|
else if(block.length > BLOCK_SIZE && start <= blockDims.end) {
|
|
val = val.substring(0, start) + val.substring(start + 1);
|
|
}
|
|
// jump to next block if cursor is at the end of the block and block is full
|
|
else if(block.length > BLOCK_SIZE && start === blockDims.end + 1) {
|
|
var overflow = block.substr(BLOCK_SIZE);
|
|
if(overflow.length > BLOCK_SIZE) {
|
|
overflow = overflow.substr(0, BLOCK_SIZE);
|
|
}
|
|
val = setBlock(val, blockIndex, block.substr(0, BLOCK_SIZE));
|
|
|
|
var nextBlock = getBlock(val, blockIndex + 1);
|
|
val = setBlock(val, blockIndex + 1, (overflow + nextBlock).substr(0, BLOCK_SIZE));
|
|
|
|
start++;
|
|
end++;
|
|
}
|
|
|
|
ngModelCtrl.$setViewValue(val);
|
|
ngModelCtrl.$render();
|
|
|
|
el.setSelectionRange(start, end);
|
|
}
|
|
|
|
function handlePastedInput(el) {
|
|
// reformat whole input value
|
|
el.value = format(parse(el.value).substr(0, BLOCK_SIZE * NUM_BLOCKS));
|
|
|
|
// select first block to have clear predefined selection behavior
|
|
var dims = getBlockDimensions(el.value, 0);
|
|
el.setSelectionRange(dims.start, dims.end + 1);
|
|
}
|
|
|
|
// Events
|
|
|
|
var pasted = false;
|
|
|
|
elm.on('input', function() {
|
|
if(pasted) {
|
|
handlePastedInput(this);
|
|
pasted = false;
|
|
return;
|
|
}
|
|
|
|
handleInput(this);
|
|
});
|
|
|
|
elm.on('paste', function() {
|
|
// next input event will handle pasted input
|
|
pasted = true;
|
|
});
|
|
|
|
elm.on('click', function() {
|
|
var blockIndex = getBlockIndex(this.value, this.selectionStart);
|
|
var dims = getBlockDimensions(this.value, blockIndex);
|
|
|
|
this.setSelectionRange(dims.start, dims.end + 1);
|
|
});
|
|
}
|
|
};
|
|
}); |