/** * Created by tsufiev on 2/24/15. */ (function() { 'use strict'; angular.module('merlin') .directive('editable', function() { return { restrict: 'E', templateUrl: '/static/merlin/templates/editable.html', require: 'ngModel', scope: true, link: function(scope, element, attrs, ngModelCtrl) { var hiddenSpan = element.find('span.width-detector'), input = element.find('input'), maxWidth = 400; function adjustWidth() { var width; hiddenSpan.html(scope.editableValue); width = hiddenSpan.width(); input.width(width <= maxWidth ? width : maxWidth); } function accept() { ngModelCtrl.$setViewValue(scope.editableValue); scope.isEdited = false; } function reject() { ngModelCtrl.$rollbackViewValue(); scope.isEdited = false; } scope.isEdited = false; scope.$watch('editableValue', function() { adjustWidth(); }); input.on('keyup', function(e) { if ( e.keyCode == 13 ) { accept(); scope.$apply(); } else if (e.keyCode == 27 ) { reject(); scope.$apply(); } }); ngModelCtrl.$render = function() { if ( !ngModelCtrl.$viewValue ) { ngModelCtrl.$viewValue = ngModelCtrl.$modelValue; } scope.editableValue = ngModelCtrl.$viewValue; adjustWidth(); }; scope.accept = accept; scope.reject = reject; } }; }) .directive('showFocus', function($timeout) { return function(scope, element, attrs) { scope.$watch(attrs.showFocus, function(newValue) { $timeout(function() { newValue && element.focus(); }); }); } }) .directive('panel', function($parse) { return { restrict: 'E', templateUrl: '/static/merlin/templates/collapsible-panel.html', transclude: true, scope: { panel: '=content' }, link: function(scope, element, attrs) { scope.removable = $parse(attrs.removable)(); scope.isCollapsed = false; } } }) .directive('collapsibleGroup', function() { return { restrict: 'E', templateUrl: '/static/merlin/templates/collapsible-group.html', transclude: true, scope: { group: '=content', onAdd: '&', onRemove: '&' }, link: function(scope, element, attrs) { scope.isCollapsed = false; if ( attrs.onAdd && attrs.additive !== 'false' ) { scope.additive = true; } if ( attrs.onRemove && attrs.removable !== 'false' ) { scope.removable = true; } } } }) .directive('validatableWith', function($parse) { return { restrict: 'A', require: 'ngModel', link: function(scope, element, attrs, ctrl) { var model; if ( attrs.validatableWith ) { model = $parse(attrs.validatableWith)(scope); scope.error = ''; model.setValidatable && model.setValidatable(true); model.on && model.on('validation', function(result) { var isValid = (result == 'succeeded'), baseMessage = ''; // (FIXME): hack until Barricade supports validation of empty required entries if ( !model.get() && model.isRequired() ) { isValid = false; baseMessage = 'This field is required.' } ctrl.$setValidity('barricade', isValid); scope.error = model.hasError() ? model.getError() : baseMessage; }); ctrl.$formatters.push(function(modelValue) { return modelValue === undefined ? ( ctrl.$isEmpty(ctrl.$viewValue) ? undefined : ctrl.$viewValue ) : modelValue; }); } } } }) .directive('typedField', ['$compile', 'merlin.templates', function($compile, templates) { return { restrict: 'E', scope: { value: '=', type: '@' }, link: function(scope, element) { templates.templateReady(scope.type).then(function(template) { element.replaceWith($compile(template)(scope)); }) } } }]) })();