Skip to content

Instantly share code, notes, and snippets.

@Imater
Created September 7, 2015 09:26
Show Gist options
  • Save Imater/708ab0442cb620b7ae8b to your computer and use it in GitHub Desktop.
Save Imater/708ab0442cb620b7ae8b to your computer and use it in GitHub Desktop.
angular.module('fab.components.fabAmountEdit', []).directive('fabAmountEdit', function (hotkeys,$timeout) {
return {
restrict: 'E',
scope: {
ngModel: '=?',
enabled: "@",
error: "@",
help: "@",
unit: "@",
kind: "@",
maxAmount: "@",
minAmount: "@",
id: "@",
caption: "@",
precision: "@",
readOnly: "@",
required: "@",
shortAccessKey: "@",
tabIndex: "@",
useCalc: "@",
visible: "@",
onChange: '&',
onFocus: '&',
onExit: '&',
onKeyDown: '&',
onKeyPressed: '&',
onKeyUp: '&'
},
require: ['ngModel', '^?fabForm'],
templateUrl: "components/fabAmountEdit/fabAmountEdit.tpl.html",
compile: function(element, attrs) {
var utils = {
addKindClass: function(kind, element){
var kindClass;
switch (kind){
case 'small':
kindClass = 'small-input';
break;
case 'big':
kindClass = 'big-input';
break;
default:
kindClass = 'big-input';
}
element.addClass(kindClass);
},
setDefaultValues : function(attrs, vars){
angular.forEach(vars, function(value, key){
attrs[key] = attrs[key] !== undefined ? attrs[key] : value;
});
},
apiGet: function ($scope, fnName, scopeProperty) {
$scope[fnName] = function(){
if (fnName!=="getValue"){
return $scope[scopeProperty];
} else {
var value = $scope[scopeProperty].toString();
value = value.replace(/\s/g,"");
return parseFloat(value);
}
}
},
apiSet: function ($scope, fnName, scopeProperty, permanentValue) {
$scope[fnName] = function(value){
$timeout(function(){
$scope[scopeProperty] = permanentValue === undefined ? (value === undefined ? undefined : value.toString()) : permanentValue.toString();
if (fnName === "setMinAmount" || fnName === "setMaxAmount"){
$scope.validateInput($scope.ngModel);
}
$scope.checkCurrency($scope.unit);
if (fnName === "clearAmountUnit"){
$scope.checkCurrency($scope.unit);
}
if (fnName === "setAmountUnit"){
$scope.checkCurrency($scope.unit);
}
},1);
}
},
addFocusHotkey: function(scope, shortAccessKey){
if(!shortAccessKey){
return angular.noop();
}
hotkeys.bindTo(scope).add({
combo: shortAccessKey,
description: 'set focus to edit',
allowIn: ['INPUT', 'TEXTAREA', 'SELECT'],
callback: function(){
scope.setFocus();
}
});
},
addBlurHotkey: function(scope){
hotkeys.bindTo(scope).add({
combo: 'enter',
description: 'set blur to edit',
allowIn: ['INPUT', 'TEXTAREA', 'SELECT'],
callback: function(){
scope.setBlur();
}
});
},
addNumPadHotkeyEnter: function(scope){
hotkeys.bindTo(scope).add({
combo: 'enter',
callback: function(){
scope.calculate();
}
});
},
addNumPadHotkeySpace: function(scope){
hotkeys.bindTo(scope).add({
combo: 'space',
callback: function(){
scope.execute();
}
});
},
addNumPadHotkeyAdd: function(scope){
hotkeys.bindTo(scope).add({
combo: '+',
callback: function(){
scope.updateOutput('+');
}
});
},
addNumPadHotkeyDivision: function(scope){
hotkeys.bindTo(scope).add({
combo: '/',
callback: function(){
scope.updateOutput('/');
}
});
},
addNumPadHotkeySubtract: function(scope){
hotkeys.bindTo(scope).add({
combo: '-',
callback: function(){
scope.updateOutput('-');
}
});
},
addNumPadHotkeyMultiply: function(scope){
hotkeys.bindTo(scope).add({
combo: '*',
callback: function(){
scope.updateOutput('x');
}
});
},
addNumPadHotkeyNum0: function(scope){
hotkeys.bindTo(scope).add({
combo: '0',
callback: function(){
scope.updateOutput('0');
}
});
},
addNumPadHotkeyNum1: function(scope){
hotkeys.bindTo(scope).add({
combo: '1',
callback: function(){
scope.updateOutput('1');
}
});
},
addNumPadHotkeyNum2: function(scope){
hotkeys.bindTo(scope).add({
combo: '2',
callback: function(){
scope.updateOutput('2');
}
});
},
addNumPadHotkeyNum3: function(scope){
hotkeys.bindTo(scope).add({
combo: '3',
callback: function(){
scope.updateOutput('3');
}
});
},
addNumPadHotkeyNum4: function(scope){
hotkeys.bindTo(scope).add({
combo: '4',
callback: function(){
scope.updateOutput('4');
}
});
},
addNumPadHotkeyNum5: function(scope){
hotkeys.bindTo(scope).add({
combo: '5',
callback: function(){
scope.updateOutput('5');
}
});
},
addNumPadHotkeyNum6: function(scope){
hotkeys.bindTo(scope).add({
combo: '6',
callback: function(){
scope.updateOutput('6');
}
});
},
addNumPadHotkeyNum7: function(scope){
hotkeys.bindTo(scope).add({
combo: '7',
callback: function(){
scope.updateOutput('7');
}
});
},
addNumPadHotkeyNum8: function(scope){
hotkeys.bindTo(scope).add({
combo: '8',
callback: function(){
scope.updateOutput('8');
}
});
},
addNumPadHotkeyNum9: function(scope){
hotkeys.bindTo(scope).add({
combo: '9',
callback: function(){
scope.updateOutput('9');
}
});
},
addNumPadHotkeyPoint: function(scope){
hotkeys.bindTo(scope).add({
combo: '.',
callback: function(){
scope.updateOutput('.');
}
});
},
addNumPadHotkeyComma: function(scope){
hotkeys.bindTo(scope).add({
combo: ',',
callback: function(){
scope.updateOutput('.');
}
});
},
addNumPadHotkeyClose: function(scope){
hotkeys.bindTo(scope).add({
combo: 'escape',
callback: function(){
scope.showCalc();
}
});
},
addNumPadHotkeyRBracket: function(scope){
hotkeys.bindTo(scope).add({
combo: ')',
callback: function(){
scope.updateOutput(')');
}
});
},
addNumPadHotkeyLBracket: function(scope){
hotkeys.bindTo(scope).add({
combo: '(',
callback: function(){
scope.updateOutput('(');
}
});
},
addNumPadHotkeyPercent: function(scope){
hotkeys.bindTo(scope).add({
combo: '%',
callback: function(){
scope.updateOutput('%');
}
});
},
addNumPadHotkeyEval: function(scope){
hotkeys.bindTo(scope).add({
combo: '=',
callback: function(){
scope.calculate();
}
});
},
addNumPadHotkeyDel: function(scope){
hotkeys.bindTo(scope).add({
combo: 'del',
callback: function(){
scope.clear();
}
});
},
addNumPadHotkeyDelChar: function(scope){
hotkeys.bindTo(scope).add({
combo: 'backspace',
callback: function(){
scope.del();
}
});
}
};
utils.setDefaultValues(attrs, {
enabled: "true",
error: '',
help: '',
inputMask: '',
kind: "big",
caption: '',
precision: "2",
readOnly: "false",
required: "false",
visible: "true",
useCalc: "false",
showHelp: "false"
});
//link function
return function ($scope, element, attrs, ctrls) {
element.removeAttr("tabIndex");
$scope.isShowError = false;
$scope.isValid = true;
$scope.isParseError = false;
$scope.showCalculator = false;
$scope.output = "";
if ($scope.readOnly === 'true'){
$scope.setReadOnly(true);
}
$scope.arrayOfCurrency = ["glyphicon-euro","glyphicon-eur","glyphicon-usd","glyphicon-gbp","glyphicon-btc","glyphicon-yen",
"glyphicon-jpy","glyphicon-ruble","glyphicon-rub"];
$scope.checkCurrency = function (currency) {
if ($scope.arrayOfCurrency.indexOf(currency)!==-1){
$scope.unitText = undefined;
$scope.unitChar = $scope.unit;
} else {
$scope.unitText = $scope.unit;
$scope.unitChar = undefined;
}
}
$scope.checkCurrency($scope.unit);
var formCtrl = ctrls[1];
if (attrs.id && formCtrl && formCtrl.registerComponent) {
formCtrl.registerComponent(attrs.id, $scope);
}
//popover
$scope.popover = {
error: {
className: 'popover-error',
content: $scope.error
},
help: {
className: 'popover-help',
content: $scope.help
},
selected: undefined //currently selected popover
};
$scope.closePopover = function(){
var input = element[0].querySelector('.popover-place');
if ($scope.popover.selected == "help"){
$timeout(function(){
angular.element(input).triggerHandler('triggerPopover');
$scope.showHelp = false;
$scope.popover.selected = undefined;
},1);
}
if ($scope.popover.selected == "error"){
$timeout(function(){
angular.element(input).triggerHandler('triggerPopover');
$scope.popover.selected = undefined;
},1);
}
if ($scope.showCalculator == true){
$scope.showCalc();
}
};
$scope.showCalc = function () {
if ($scope.showCalculator === false && ($scope.enabled).toString() === "false"){
return true;
}
$scope.showCalculator = !($scope.showCalculator);
if ($scope.showCalculator == true){
$timeout(function(){
$scope.elInputCalc = element.find("input");
$scope.elInputCalc[1].onkeydown = function (event){
var keyCode = event.keyCode;
switch (keyCode){
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
if (event.shiftKey !== true) {
var value = (event.keyCode - 48).toString();
$scope.updateOutput(value);
event.preventDefault();
event.stopPropagation();
$scope.$digest();
} else {
if ([48,53,56,57].indexOf(keyCode)!==-1){
if (keyCode === 48){
var value = ")";
} else if (keyCode === 53){
var value = "%";
} else if (keyCode === 56){
var value = "x";
} else if (keyCode === 57){
var value = "(";
}
$scope.updateOutput(value);
event.preventDefault();
event.stopPropagation();
$scope.$digest();
}
}
break;
case 96:
case 97:
case 98:
case 99:
case 100:
case 101:
case 102:
case 103:
case 104:
case 105:
var value = (event.keyCode - 96).toString();
$scope.updateOutput(value);
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 13:
case 187:
$scope.calculate();
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 32:
$scope.execute();
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 27:
$scope.showCalc();
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 106:
case 42:
$scope.updateOutput('x');
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 107:
case 43:
$scope.updateOutput('+');
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 109:
case 189:
$scope.updateOutput('-');
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 110:
case 188:
case 190:
$scope.updateOutput('.');
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 111:
$scope.updateOutput('/');
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 37:
$scope.updateOutput('%');
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 40:
$scope.updateOutput('(');
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 41:
$scope.updateOutput(')');
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 46:
$scope.clear();
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 8:
$scope.del();
event.preventDefault();
event.stopPropagation();
$scope.$digest();
break;
case 9:
break;
default:
event.preventDefault();
event.stopPropagation();
}
};
$scope.elInputCalc[1].focus();
},1);
} else {
$scope.isParseError = false;
$scope.output = "";
}
};
$scope.triggerPopover = function(type){
var input = element[0].querySelector('.popover-place');
$timeout(function(){
if(!$scope.popover.selected && $scope.popover[type]) {
//open first time
$scope.popover.selected = type;
if (type == "help"){
$scope.showHelp = true;
}
$timeout(function(){
angular.element(input).triggerHandler('triggerPopover');
},1);
} else if ($scope.popover.selected && $scope.popover.selected !== type){
//switch popover to other type
//close and open to recalculate placement
$timeout(function(){
angular.element(input).triggerHandler('triggerPopover');
},1);
$timeout(function(){
angular.element(input).triggerHandler('triggerPopover');
},1);
$scope.popover.selected = type;
$scope.showHelp = type=="help" ? true : false;
} else if ($scope.popover.selected){
//close
$scope.popover.selected = undefined;
$scope.showHelp = false;
$timeout(function(){
angular.element(input).triggerHandler('triggerPopover');
},1);
}
},1)
};
$scope.showErrorMsg = function(){
if ($scope.popover.selected !== "error" && $scope.isShowError){
$scope.triggerPopover('error');
}
};
$scope.updateOutput = function(btn){
$scope.output += String(btn);
};
$scope.clear = function() {
$scope.output = "";
};
$scope.calculate = function(){
try {
$scope.output = Parser.evaluate($scope.output.replace(/x/g,"*").replace(/(.*\d+.*)\+(\d+)%/g,"$1\+$1\*$2/100").replace(/\*(\d+)%/g,"\*$1/100"))+"";
$scope.isParseError = false;
}
catch(e){
$scope.isParseError = true;
}
};
$scope.del = function(){
$scope.output = $scope.output.substring(0, $scope.output.length-1);
};
$scope.execute = function(){
$scope.calculate();
if ($scope.isParseError == false){
$scope.setValue($scope.output);
$scope.showCalc();
$scope.output = "";
}
};
utils.addKindClass($scope.kind, element);
$scope.setFocus = function () {
$timeout(function(){
element.find('input')[0].focus();
},1);
};
$scope.setBlur = function () {
$timeout(function(){
element.find('input')[0].blur();
$scope.closePopover();
},1);
};
//focus on hotkey, blur on Enter
utils.addFocusHotkey($scope, $scope.shortAccessKey);
utils.addBlurHotkey($scope);
utils.addNumPadHotkeyEnter($scope);
utils.addNumPadHotkeySpace($scope);
utils.addNumPadHotkeyAdd($scope);
utils.addNumPadHotkeyDivision($scope);
utils.addNumPadHotkeySubtract($scope);
utils.addNumPadHotkeyMultiply($scope);
utils.addNumPadHotkeyNum0($scope);
utils.addNumPadHotkeyNum1($scope);
utils.addNumPadHotkeyNum2($scope);
utils.addNumPadHotkeyNum3($scope);
utils.addNumPadHotkeyNum4($scope);
utils.addNumPadHotkeyNum5($scope);
utils.addNumPadHotkeyNum6($scope);
utils.addNumPadHotkeyNum7($scope);
utils.addNumPadHotkeyNum8($scope);
utils.addNumPadHotkeyNum9($scope);
utils.addNumPadHotkeyPoint($scope);
utils.addNumPadHotkeyComma($scope);
utils.addNumPadHotkeyClose($scope);
utils.addNumPadHotkeyRBracket($scope);
utils.addNumPadHotkeyLBracket($scope);
utils.addNumPadHotkeyPercent($scope);
utils.addNumPadHotkeyEval($scope);
utils.addNumPadHotkeyDel($scope);
utils.addNumPadHotkeyDelChar($scope);
var validate = function(value){
if (value == undefined || value == "") return '';
var validateFunctions = [
function(value){
if ($scope.minAmount || $scope.minAmount*1==0){
if (value*1 < $scope.minAmount*1) {
return "Введенное значение не входит в диапазон допустимых значений";
}
}
},
function(value){
if ($scope.maxAmount || $scope.maxAmount*1==0){
if (value*1 > $scope.maxAmount*1) {
return "Введенное значение не входит в диапазон допустимых значений";
}
}
},
function(value){
if(false){
return $scope.error;
}
}
];
var count = validateFunctions.length;
for (var i = 0; i < count; i++) {
var errorMessage = validateFunctions[i](value);
if(errorMessage){
return errorMessage;
}
}
};
$scope.validateInput = function (){
var errorMessage = validate(($scope.ngModel+"").replace(/\s/g,""));
if (errorMessage){
$scope.isValid = false;
$scope.isShowError = true;
$scope.popover.error.content = errorMessage;
if($scope.popover.selected !== 'error'){
$scope.triggerPopover('error');
}
} else {
$scope.isShowError = false;
$scope.isValid = true;
if($scope.popover.selected === 'error'){
$scope.triggerPopover('error');
}
}
};
$scope.$watch('ngModel', function(newValue, oldValue){
newValue = (newValue === undefined) ? "" : newValue.toString();
oldValue = (oldValue === undefined) ? "" : oldValue.toString();
if (newValue.replace(/\s/g,"") === oldValue.replace(/\s/g,"")){
return true;
}
$scope.validateInput(newValue);
});
//javascript api
utils.apiGet($scope, "getAmountUnit", "unit");
utils.apiGet($scope, "getError", "error");
utils.apiGet($scope, "getMaxAmount", "maxAmount");
utils.apiGet($scope, "getMinAmount", "minAmount");
utils.apiGet($scope, "getPrecision", "precision");
utils.apiGet($scope, "getText", "ngModel");
utils.apiGet($scope, "getValue", "ngModel");
utils.apiGet($scope, "isElementValid", "isValid");
utils.apiGet($scope, "isEnabled", "enabled");
utils.apiGet($scope, "isReadOnly", "readOnly");
utils.apiGet($scope, "isRequired", "required");
utils.apiGet($scope, "isVisible", "visible");
utils.apiSet($scope, "clearAmountUnit", "unit", undefined);
utils.apiSet($scope, "disableElement", "enabled", false);
utils.apiSet($scope, "enableElement", "enabled", true);
utils.apiSet($scope, "hideElement", "visible", false);
utils.apiSet($scope, "showElement", "visible", true);
utils.apiSet($scope, "setError", "error");
utils.apiSet($scope, "setAmountUnit", "unit");
utils.apiSet($scope, "setMinAmount", "minAmount");
utils.apiSet($scope, "setMaxAmount", "maxAmount");
utils.apiSet($scope, "setRequired", "required");
utils.apiSet($scope, "setValue", "ngModel");
}
},
controller: function ($scope, $element, $attrs) {
$scope.initMask = function(){
$scope.maskForInput = {
'alias': "decimal",
'groupSeparator': " ",
'autoGroup': true,
'rightAlign': false,
'radixPoint': ".",
'digits': $scope.precision,
'showMaskOnFocus' : true,
'showMaskOnHover' : true,
'clearMaskOnLostFocus' : false,
'skipRadixDance' : true
};
$scope.inputMaskKind = 'mask';
};
$scope.initMask();
$scope.setPrecision = function (value){
$scope.precision = value;
$scope.initMask();
$timeout(function(){
$element.find('input').inputmask('decimal',$scope.maskForInput);
},1);
};
$scope.setReadOnly = function (value){
$scope.readOnly = value;
if (value === true){
$element.find('input').attr('readonly',"readonly");
} else {
$element.find('input').removeAttr("readonly");
}
};
}
}
});
<div click-out="closePopover()" ng-show="visible=='true'" class="relative-container">
<div class="form-group has-icon label-inside-new popover-place"
ng-class="{'has-error': isShowError , 'has-units' : unit}"
popover-trigger="triggerPopover"
popover-placement="top"
popover-template="'components/fabPopover/fabSwitchPopover.tpl.html'"
>
<input class="form-control right-to-left"
type="text"
placeholder="{{ kind==='small' ? caption + (required=='true' ? ' *' : ''): ''}}"
id="{{id}}"
required="{{required}}"
ng-disabled="enabled=='false'"
ng-model="ngModel"
ng-model-options="{debounce: 200}"
tabindex="{{tabIndex}}"
ng-keyup="onKeyUp({keyCode: $event.keyCode})"
ng-keydown="onKeyDown({keyCode: $event.keyCode})"
ng-change="onChange({value: ngModel})"
ng-blur="onExit({value: ngModel})"
ng-focus="onFocus({value: ngModel});showErrorMsg();"
fab-mask=""
input-mask="inputMaskKind"
mask-option="maskForInput"/>
<label class=""
alt="{{caption + (required=='true' ? ' *' : '')}}"
caption="{{caption + (required=='true' ? ' *' : '')}}"
ng-class="{'label-required': required=='true' && !ngModel}">
</label>
<div class="form-control-feedback" ng-class="{'' : !showHelp , 'opaque' : showHelp}">
<span class="glyphicon glyphicon-question-sign help-button" ng-click="triggerPopover('help')" ng-if="help"></span>
<span class="glyphicon glyphicon-info-sign error-button" ng-click="triggerPopover('error')" ng-if="isShowError"></span>
</div>
<div class="form-control-icon" ng-if="useCalc=='true'">
<span ng-click="showCalc()" class="icon icon-calc pointer"></span>
</div>
<div class="form-control-units" ng-class="{'show': (ngModel && (unitText || unitChar))}" ng-if="unit">
{{unitText}}
<span class="glyphicon pointer" ng-class="[unitChar, {'hide': !unitChar}]"></span>
</div>
</div>
<div ng-if="showCalculator" class="popup-panel calculator" style="z-index: 5;">
<div class="display" style="margin-bottom: 20px">
<div class="form-group input-group" ng-class="{'has-error': isParseError}">
<input class="form-control input-lg right-to-left" type="text" ng-model="output">
<span class="input-group-btn">
<button class="btn btn-info btn-lg" type="button" ng-click="del()">←</button>
</span>
</div>
</div>
<div class="pad" style="margin-bottom: 20px">
<div>
<div><button type="button" class="btn btn-default darker" ng-click="clear()">C</button></div><!--
--><div><button type="button" class="btn btn-default darker" ng-click="updateOutput('(')">(</button></div><!--
--><div><button type="button" class="btn btn-default darker" ng-click="updateOutput(')')">)</button></div><!--
--><div><button type="button" class="btn btn-default darker" ng-click="updateOutput('%')">%</button></div>
</div>
<div>
<div><button type="button" class="btn btn-default" ng-click="updateOutput('7')">7</button></div><!--
--><div><button type="button" class="btn btn-default" ng-click="updateOutput('8')">8</button></div><!--
--><div><button type="button" class="btn btn-default" ng-click="updateOutput('9')">9</button></div><!--
--><div><button type="button" class="btn btn-default darker" ng-click="updateOutput('/')">/</button></div>
</div>
<div>
<div><button type="button" class="btn btn-default" ng-click="updateOutput('4')">4</button></div><!--
--><div><button type="button" class="btn btn-default" ng-click="updateOutput('5')">5</button></div><!--
--><div><button type="button" class="btn btn-default" ng-click="updateOutput('6')">6</button></div><!--
--><div><button type="button" class="btn btn-default darker" ng-click="updateOutput('x')">x</button></div>
</div>
<div>
<div><button type="button" class="btn btn-default" ng-click="updateOutput('1')">1</button></div><!--
--><div><button type="button" class="btn btn-default" ng-click="updateOutput('2')">2</button></div><!--
--><div><button type="button" class="btn btn-default" ng-click="updateOutput('3')">3</button></div><!--
--><div><button type="button" class="btn btn-default darker" ng-click="updateOutput('-')">-</button></div>
</div>
<div>
<div><button type="button" class="btn btn-default" ng-click="updateOutput('.')">.</button></div><!--
--><div><button type="button" class="btn btn-default" ng-click="updateOutput('0')">0</button></div><!--
--><div><button type="button" class="btn btn-default darker" ng-click="calculate()">=</button></div><!--
--><div><button type="button" class="btn btn-default darker" ng-click="updateOutput('+')">+</button></div>
</div>
</div>
<button type="button" class="btn btn-success" style="width: 143px; margin-right: 20px" ng-click="execute()">Применить</button>
<button type="button" class="btn btn-info" ng-click="showCalc()">Отмена</button>
</div>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment