Merge "JSCS cleanup - horizon/static/framework conf and util"
This commit is contained in:
commit
bf9acb39a5
@ -1,10 +1,10 @@
|
|||||||
/*global angular*/
|
|
||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
angular.module('horizon.framework.conf', [])
|
|
||||||
|
|
||||||
|
angular
|
||||||
|
.module('horizon.framework.conf', [])
|
||||||
.constant('horizon.framework.conf.spinner_options', {
|
.constant('horizon.framework.conf.spinner_options', {
|
||||||
inline: {
|
inline: {
|
||||||
lines: 10,
|
lines: 10,
|
||||||
length: 5,
|
length: 5,
|
||||||
width: 2,
|
width: 2,
|
||||||
@ -14,7 +14,7 @@
|
|||||||
trail: 50,
|
trail: 50,
|
||||||
zIndex: 100
|
zIndex: 100
|
||||||
},
|
},
|
||||||
modal: {
|
modal: {
|
||||||
lines: 10,
|
lines: 10,
|
||||||
length: 15,
|
length: 15,
|
||||||
width: 4,
|
width: 4,
|
||||||
@ -33,4 +33,4 @@
|
|||||||
trail: 50
|
trail: 50
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}());
|
})();
|
||||||
|
@ -1,39 +1,41 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('horizon.framework', [
|
angular
|
||||||
'horizon.framework.conf',
|
.module('horizon.framework', [
|
||||||
'horizon.framework.util',
|
'horizon.framework.conf',
|
||||||
'horizon.framework.widgets'
|
'horizon.framework.util',
|
||||||
])
|
'horizon.framework.widgets'
|
||||||
.constant('horizon.framework.basePath', '/static/framework/')
|
])
|
||||||
|
.constant('horizon.framework.basePath', '/static/framework/')
|
||||||
|
.config([
|
||||||
|
'$interpolateProvider',
|
||||||
|
'$httpProvider',
|
||||||
|
function ($interpolateProvider, $httpProvider) {
|
||||||
|
// Replacing the default angular symbol
|
||||||
|
// allow us to mix angular with django templates
|
||||||
|
$interpolateProvider.startSymbol('{$');
|
||||||
|
$interpolateProvider.endSymbol('$}');
|
||||||
|
|
||||||
.config(['$interpolateProvider', '$httpProvider',
|
// Http global settings for ease of use
|
||||||
function ($interpolateProvider, $httpProvider) {
|
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
|
||||||
|
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
|
||||||
|
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||||
|
$httpProvider.defaults.headers.common['Content-Type'] = 'application/json;charset=utf-8';
|
||||||
|
|
||||||
// Replacing the default angular symbol
|
// Global http error handler
|
||||||
// allow us to mix angular with django templates
|
// if user is not authorized, log user out
|
||||||
$interpolateProvider.startSymbol('{$');
|
// this can happen when session expires
|
||||||
$interpolateProvider.endSymbol('$}');
|
$httpProvider.interceptors.push(function ($q) {
|
||||||
|
return {
|
||||||
// Http global settings for ease of use
|
responseError: function (error) {
|
||||||
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
|
if (error.status === 401) {
|
||||||
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
|
window.location.replace('/auth/logout');
|
||||||
$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
}
|
||||||
$httpProvider.defaults.headers.common['Content-Type'] = 'application/json;charset=utf-8';
|
return $q.reject(error);
|
||||||
|
|
||||||
// Global http error handler
|
|
||||||
// if user is not authorized, log user out
|
|
||||||
// this can happen when session expires
|
|
||||||
$httpProvider.interceptors.push(function ($q) {
|
|
||||||
return {
|
|
||||||
responseError: function (error) {
|
|
||||||
if (error.status === 401) {
|
|
||||||
window.location.replace('/auth/logout');
|
|
||||||
}
|
}
|
||||||
return $q.reject(error);
|
};
|
||||||
}
|
});
|
||||||
};
|
}
|
||||||
});
|
]);
|
||||||
}]);
|
|
||||||
})();
|
})();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
(function() {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,12 +41,12 @@
|
|||||||
* </tr>
|
* </tr>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
.directive('bindScope', function() {
|
.directive('bindScope', function () {
|
||||||
return {
|
return {
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
link: function(scope, element, attrs, ctrl, transclude) {
|
link: function (scope, element, attrs, ctrl, transclude) {
|
||||||
if (transclude) {
|
if (transclude) {
|
||||||
transclude(scope, function(clone) {
|
transclude(scope, function (clone) {
|
||||||
var detailElt = element.find('[bind-scope-target]');
|
var detailElt = element.find('[bind-scope-target]');
|
||||||
if (detailElt.length) {
|
if (detailElt.length) {
|
||||||
detailElt.append(clone);
|
detailElt.append(clone);
|
||||||
@ -56,5 +56,4 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
})();
|
||||||
})();
|
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
(function() {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe('horizon.framework.util.bind-scope module', function() {
|
describe('horizon.framework.util.bind-scope module', function () {
|
||||||
it('should have been defined', function() {
|
it('should have been defined', function () {
|
||||||
expect(angular.module('horizon.framework.util.bind-scope')).toBeDefined();
|
expect(angular.module('horizon.framework.util.bind-scope')).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('bind-scope directive', function() {
|
describe('bind-scope directive', function () {
|
||||||
|
|
||||||
var $scope, $element;
|
var $scope, $element;
|
||||||
|
|
||||||
beforeEach(module('horizon.framework'));
|
beforeEach(module('horizon.framework'));
|
||||||
beforeEach(module('horizon.framework.widgets'));
|
beforeEach(module('horizon.framework.widgets'));
|
||||||
beforeEach(module('horizon.framework.util.bind-scope'));
|
beforeEach(module('horizon.framework.util.bind-scope'));
|
||||||
|
|
||||||
beforeEach(module('horizon.framework.util.bind-scope', function($compileProvider) {
|
beforeEach(module('horizon.framework.util.bind-scope', function ($compileProvider) {
|
||||||
$compileProvider.directive('testBindScope', function() {
|
$compileProvider.directive('testBindScope', function () {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
@ -30,7 +29,7 @@
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(inject(function($injector) {
|
beforeEach(inject(function ($injector) {
|
||||||
var $compile = $injector.get('$compile');
|
var $compile = $injector.get('$compile');
|
||||||
$scope = $injector.get('$rootScope').$new();
|
$scope = $injector.get('$rootScope').$new();
|
||||||
|
|
||||||
@ -48,17 +47,15 @@
|
|||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should have 3 list items', function() {
|
it('should have 3 list items', function () {
|
||||||
expect($element.find('li').length).toBe(3);
|
expect($element.find('li').length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have 3 list items with values "cat", "dog" and "fish"', function() {
|
it('should have 3 list items with values "cat", "dog" and "fish"', function () {
|
||||||
var listItems = $element.find('li');
|
var listItems = $element.find('li');
|
||||||
expect(listItems[0].textContent.trim()).toBe('cat');
|
expect(listItems[0].textContent.trim()).toBe('cat');
|
||||||
expect(listItems[1].textContent.trim()).toBe('dog');
|
expect(listItems[1].textContent.trim()).toBe('dog');
|
||||||
expect(listItems[2].textContent.trim()).toBe('fish');
|
expect(listItems[2].textContent.trim()).toBe('fish');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
})();
|
||||||
})();
|
|
||||||
|
@ -25,128 +25,128 @@
|
|||||||
*/
|
*/
|
||||||
angular.module('horizon.framework.util.filters', ['horizon.framework.util.i18n'])
|
angular.module('horizon.framework.util.filters', ['horizon.framework.util.i18n'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc filter
|
* @ngdoc filter
|
||||||
* @name yesno
|
* @name yesno
|
||||||
* @description
|
* @description
|
||||||
* Evaluates given input for standard truthiness and returns translation
|
* Evaluates given input for standard truthiness and returns translation
|
||||||
* of 'Yes' and 'No' for true/false respectively.
|
* of 'Yes' and 'No' for true/false respectively.
|
||||||
*/
|
*/
|
||||||
.filter('yesno', ['horizon.framework.util.i18n.gettext', function(gettext) {
|
.filter('yesno', ['horizon.framework.util.i18n.gettext', function (gettext) {
|
||||||
return function(input) {
|
return function (input) {
|
||||||
return (input ? gettext("Yes") : gettext("No"));
|
return (input ? gettext("Yes") : gettext("No"));
|
||||||
};
|
};
|
||||||
}])
|
}])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc filter
|
* @ngdoc filter
|
||||||
* @name gb
|
* @name gb
|
||||||
* @description
|
* @description
|
||||||
* Expects numeric value and suffixes translated 'GB' with spacing.
|
* Expects numeric value and suffixes translated 'GB' with spacing.
|
||||||
* Returns empty string if input is not a number or is null.
|
* Returns empty string if input is not a number or is null.
|
||||||
*/
|
*/
|
||||||
.filter('gb', function() {
|
.filter('gb', function () {
|
||||||
return function(input) {
|
return function (input) {
|
||||||
if (isNaN(input) || null === input) {
|
if (isNaN(input) || null === input) {
|
||||||
return '';
|
return '';
|
||||||
} else {
|
} else {
|
||||||
return interpolate(gettext("%s GB"), [input.toString()]);
|
return interpolate(gettext("%s GB"), [input.toString()]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc filter
|
* @ngdoc filter
|
||||||
* @name mb
|
* @name mb
|
||||||
* @description
|
* @description
|
||||||
* Expects numeric value and suffixes translated 'MB' with spacing.
|
* Expects numeric value and suffixes translated 'MB' with spacing.
|
||||||
* Returns empty string if input is not a number or is null.
|
* Returns empty string if input is not a number or is null.
|
||||||
*/
|
*/
|
||||||
.filter('mb', function() {
|
.filter('mb', function () {
|
||||||
return function(input) {
|
return function (input) {
|
||||||
if (isNaN(input) || null === input) {
|
if (isNaN(input) || null === input) {
|
||||||
return '';
|
return '';
|
||||||
} else {
|
} else {
|
||||||
return interpolate(gettext("%s MB"), [input.toString()]);
|
return interpolate(gettext("%s MB"), [input.toString()]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc filter
|
* @ngdoc filter
|
||||||
* @name title
|
* @name title
|
||||||
* @description
|
* @description
|
||||||
* Capitalizes leading characters of individual words.
|
* Capitalizes leading characters of individual words.
|
||||||
*/
|
*/
|
||||||
.filter('title', function() {
|
.filter('title', function () {
|
||||||
return function(input) {
|
return function (input) {
|
||||||
if (typeof input !== 'string') {
|
if (typeof input !== 'string') {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
return input.replace(/(?:^|\s)\S/g, function(a) {
|
return input.replace(/(?:^|\s)\S/g, function (a) {
|
||||||
return a.toUpperCase();
|
return a.toUpperCase();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc filter
|
* @ngdoc filter
|
||||||
* @name noUnderscore
|
* @name noUnderscore
|
||||||
* @description
|
* @description
|
||||||
* Replaces all underscores with spaces.
|
* Replaces all underscores with spaces.
|
||||||
*/
|
*/
|
||||||
.filter('noUnderscore', function() {
|
.filter('noUnderscore', function () {
|
||||||
return function(input) {
|
return function (input) {
|
||||||
if (typeof input !== 'string') {
|
if (typeof input !== 'string') {
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
return input.replace(/_/g, ' ');
|
return input.replace(/_/g, ' ');
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc filter
|
* @ngdoc filter
|
||||||
* @name decode
|
* @name decode
|
||||||
* @description
|
* @description
|
||||||
* Returns values based on key and given mapping. If key doesn't exist
|
* Returns values based on key and given mapping. If key doesn't exist
|
||||||
* in given mapping, return key. This is useful when translations for
|
* in given mapping, return key. This is useful when translations for
|
||||||
* codes are present.
|
* codes are present.
|
||||||
*/
|
*/
|
||||||
.filter('decode', function() {
|
.filter('decode', function () {
|
||||||
return function(input, mapping) {
|
return function (input, mapping) {
|
||||||
var val = mapping[input];
|
var val = mapping[input];
|
||||||
return angular.isDefined(val) ? val : input;
|
return angular.isDefined(val) ? val : input;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc filter
|
* @ngdoc filter
|
||||||
* @name bytes
|
* @name bytes
|
||||||
* @description
|
* @description
|
||||||
* Returns a human-readable approximation of the input of bytes,
|
* Returns a human-readable approximation of the input of bytes,
|
||||||
* converted to a useful unit of measure. Uses 1024-based notation.
|
* converted to a useful unit of measure. Uses 1024-based notation.
|
||||||
*/
|
*/
|
||||||
.filter('bytes', function() {
|
.filter('bytes', function () {
|
||||||
return function(input) {
|
return function (input) {
|
||||||
var kb = 1024;
|
var kb = 1024;
|
||||||
var mb = kb*1024;
|
var mb = kb * 1024;
|
||||||
var gb = mb*1024;
|
var gb = mb * 1024;
|
||||||
var tb = gb*1024;
|
var tb = gb * 1024;
|
||||||
if (isNaN(input) || null === input || input < 0) {
|
if (isNaN(input) || null === input || input < 0) {
|
||||||
return '';
|
return '';
|
||||||
} else if (input >= tb) {
|
} else if (input >= tb) {
|
||||||
return interpolate(gettext("%s TB"), [Number(input/tb).toFixed(2)]);
|
return interpolate(gettext("%s TB"), [Number(input / tb).toFixed(2)]);
|
||||||
} else if (input >= gb) {
|
} else if (input >= gb) {
|
||||||
return interpolate(gettext("%s GB"), [Number(input/gb).toFixed(2)]);
|
return interpolate(gettext("%s GB"), [Number(input / gb).toFixed(2)]);
|
||||||
} else if (input >= mb) {
|
} else if (input >= mb) {
|
||||||
return interpolate(gettext("%s MB"), [Number(input/mb).toFixed(2)]);
|
return interpolate(gettext("%s MB"), [Number(input / mb).toFixed(2)]);
|
||||||
} else if (input >= kb) {
|
} else if (input >= kb) {
|
||||||
return interpolate(gettext("%s KB"), [Number(input/kb).toFixed(2)]);
|
return interpolate(gettext("%s KB"), [Number(input / kb).toFixed(2)]);
|
||||||
} else {
|
} else {
|
||||||
return interpolate(gettext("%s bytes"), [Math.floor(input)]);
|
return interpolate(gettext("%s bytes"), [Math.floor(input)]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc filter
|
* @ngdoc filter
|
||||||
@ -155,11 +155,11 @@
|
|||||||
* Displays translated count in table footer.
|
* Displays translated count in table footer.
|
||||||
* Takes only finite numbers.
|
* Takes only finite numbers.
|
||||||
*/
|
*/
|
||||||
.filter('itemCount', function() {
|
.filter('itemCount', function () {
|
||||||
return function(input) {
|
return function (input) {
|
||||||
var isNumeric = (input !== null && isFinite(input));
|
var isNumeric = (input !== null && isFinite(input));
|
||||||
var number = isNumeric ? Math.round(input): 0;
|
var number = isNumeric ? Math.round(input) : 0;
|
||||||
var count = (number > 0) ? number: 0;
|
var count = (number > 0) ? number : 0;
|
||||||
var format = ngettext('Displaying %s item', 'Displaying %s items', count);
|
var format = ngettext('Displaying %s item', 'Displaying %s items', count);
|
||||||
return interpolate(format, [count]);
|
return interpolate(format, [count]);
|
||||||
};
|
};
|
||||||
@ -171,11 +171,10 @@
|
|||||||
* @description
|
* @description
|
||||||
* Returns translated text.
|
* Returns translated text.
|
||||||
*/
|
*/
|
||||||
.filter('trans', ['horizon.framework.util.i18n.gettext', function(gettextFunc) {
|
.filter('trans', ['horizon.framework.util.i18n.gettext', function (gettextFunc) {
|
||||||
return function(input) {
|
return function (input) {
|
||||||
// NOTE: uses 'gettextFunc' to avoid message collection.
|
// NOTE: uses 'gettextFunc' to avoid message collection.
|
||||||
return gettextFunc(input);
|
return gettextFunc(input);
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -1,220 +1,211 @@
|
|||||||
describe('horizon.framework.util.filters', function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
beforeEach(module('horizon.framework.util.i18n'));
|
describe('horizon.framework.util.filters', function () {
|
||||||
beforeEach(module('horizon.framework.util.filters'));
|
beforeEach(module('horizon.framework.util.i18n'));
|
||||||
|
beforeEach(module('horizon.framework.util.filters'));
|
||||||
|
|
||||||
describe('yesno', function () {
|
describe('yesno', function () {
|
||||||
var yesnoFilter;
|
var yesnoFilter;
|
||||||
beforeEach(inject(function (_yesnoFilter_) {
|
beforeEach(inject(function (_yesnoFilter_) {
|
||||||
yesnoFilter = _yesnoFilter_;
|
yesnoFilter = _yesnoFilter_;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('returns Yes for true', function () {
|
it('returns Yes for true', function () {
|
||||||
expect(yesnoFilter(true)).toBe('Yes');
|
expect(yesnoFilter(true)).toBe('Yes');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns No for false', function () {
|
||||||
|
expect(yesnoFilter(false)).toBe('No');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns No for null', function () {
|
||||||
|
expect(yesnoFilter(null)).toBe('No');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns No for undefined', function () {
|
||||||
|
expect(yesnoFilter(undefined)).toBe('No');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns Yes for other truthy values', function () {
|
||||||
|
expect(yesnoFilter(7)).toBe('Yes');
|
||||||
|
expect(yesnoFilter('C')).toBe('Yes');
|
||||||
|
expect(yesnoFilter('This will be truthy')).toBe('Yes');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns No for other falsy values', function () {
|
||||||
|
expect(yesnoFilter(0)).toBe('No');
|
||||||
|
expect(yesnoFilter('')).toBe('No');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns No for false', function () {
|
describe('gb', function () {
|
||||||
expect(yesnoFilter(false)).toBe('No');
|
var gbFilter;
|
||||||
|
beforeEach(inject(function (_gbFilter_) {
|
||||||
|
gbFilter = _gbFilter_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('returns given numeric value properly', function () {
|
||||||
|
expect(gbFilter(12)).toBe('12 GB');
|
||||||
|
expect(gbFilter(-12)).toBe('-12 GB');
|
||||||
|
expect(gbFilter(12.12)).toBe('12.12 GB');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty string for non-numeric', function () {
|
||||||
|
expect(gbFilter('humbug')).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty string for null', function () {
|
||||||
|
expect(gbFilter(null)).toBe('');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns No for null', function () {
|
describe('mb', function () {
|
||||||
expect(yesnoFilter(null)).toBe('No');
|
var mbFilter;
|
||||||
|
beforeEach(inject(function (_mbFilter_) {
|
||||||
|
mbFilter = _mbFilter_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('returns given numeric value properly', function () {
|
||||||
|
expect(mbFilter(12)).toBe('12 MB');
|
||||||
|
expect(mbFilter(-12)).toBe('-12 MB');
|
||||||
|
expect(mbFilter(12.12)).toBe('12.12 MB');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty string for non-numeric', function () {
|
||||||
|
expect(mbFilter('humbug')).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty string for null', function () {
|
||||||
|
expect(mbFilter(null)).toBe('');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns No for undefined', function () {
|
describe('title', function () {
|
||||||
expect(yesnoFilter(undefined)).toBe('No');
|
var titleFilter;
|
||||||
|
beforeEach(inject(function (_titleFilter_) {
|
||||||
|
titleFilter = _titleFilter_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('capitalizes as expected', function () {
|
||||||
|
expect(titleFilter('title')).toBe('Title');
|
||||||
|
expect(titleFilter('we have several words')).toBe('We Have Several Words');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles non-strings correctly', function () {
|
||||||
|
expect(titleFilter(42)).toBe(42);
|
||||||
|
expect(titleFilter(null)).toBe(null);
|
||||||
|
expect(titleFilter(undefined)).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not mess up properly capitalized strings', function () {
|
||||||
|
expect(titleFilter('I Love OpenStack Horizon!')).toBe('I Love OpenStack Horizon!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles strings beginning with numbers', function () {
|
||||||
|
expect(titleFilter('3abc')).toBe('3abc');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns Yes for other truthy values', function () {
|
describe('noUnderscore', function () {
|
||||||
expect(yesnoFilter(7)).toBe('Yes');
|
var noUnderscoreFilter;
|
||||||
expect(yesnoFilter('C')).toBe('Yes');
|
beforeEach(inject(function (_noUnderscoreFilter_) {
|
||||||
expect(yesnoFilter('This will be truthy')).toBe('Yes');
|
noUnderscoreFilter = _noUnderscoreFilter_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('replaces all underscores with spaces', function () {
|
||||||
|
expect(noUnderscoreFilter('_this_is___a_lot____of_underscores__'))
|
||||||
|
.toBe(' this is a lot of underscores ');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns non-string input', function () {
|
||||||
|
expect(noUnderscoreFilter(null)).toBe(null);
|
||||||
|
expect(noUnderscoreFilter(false)).toBe(false);
|
||||||
|
expect(noUnderscoreFilter(true)).toBe(true);
|
||||||
|
expect(noUnderscoreFilter('')).toBe('');
|
||||||
|
expect(noUnderscoreFilter(21)).toBe(21);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns No for other falsy values', function () {
|
describe("decode", function () {
|
||||||
expect(yesnoFilter(0)).toBe('No');
|
var decodeFilter;
|
||||||
expect(yesnoFilter('')).toBe('No');
|
beforeEach(inject(function (_decodeFilter_) {
|
||||||
|
decodeFilter = _decodeFilter_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("Returns value when key is present", function () {
|
||||||
|
expect(decodeFilter('PRESENT', {'PRESENT': 'Here'})).toBe('Here');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Returns value when key is present and value is falsy", function () {
|
||||||
|
expect(decodeFilter('PRESENT', {'PRESENT': false})).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Returns input when key is not present", function () {
|
||||||
|
expect(decodeFilter('NOT_PRESENT', {'PRESENT': 'Here'})).toBe('NOT_PRESENT');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('bytes', function () {
|
||||||
|
var bytesFilter;
|
||||||
|
beforeEach(inject(function (_bytesFilter_) {
|
||||||
|
bytesFilter = _bytesFilter_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('returns TB values', function () {
|
||||||
|
expect(bytesFilter(1099511627776)).toBe('1.00 TB');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns GB values', function () {
|
||||||
|
expect(bytesFilter(1073741824)).toBe('1.00 GB');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns MB values', function () {
|
||||||
|
expect(bytesFilter(1048576)).toBe('1.00 MB');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns KB values', function () {
|
||||||
|
expect(bytesFilter(1024)).toBe('1.00 KB');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns byte values', function () {
|
||||||
|
expect(bytesFilter(0)).toBe('0 bytes');
|
||||||
|
expect(bytesFilter(1)).toBe('1 bytes');
|
||||||
|
expect(bytesFilter(1023)).toBe('1023 bytes');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles non-numbers correctly', function () {
|
||||||
|
expect(bytesFilter('Yo!')).toBe('');
|
||||||
|
expect(bytesFilter(null)).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('itemCount', function () {
|
||||||
|
it('should return translated text with item count',
|
||||||
|
inject(function (itemCountFilter) {
|
||||||
|
expect(itemCountFilter(null)).toBe('Displaying 0 items');
|
||||||
|
expect(itemCountFilter(undefined)).toBe('Displaying 0 items');
|
||||||
|
expect(itemCountFilter(true)).toBe('Displaying 1 item');
|
||||||
|
expect(itemCountFilter(false)).toBe('Displaying 0 items');
|
||||||
|
expect(itemCountFilter('a')).toBe('Displaying 0 items');
|
||||||
|
expect(itemCountFilter('0')).toBe('Displaying 0 items');
|
||||||
|
expect(itemCountFilter('1')).toBe('Displaying 1 item');
|
||||||
|
expect(itemCountFilter('1e1')).toBe('Displaying 10 items');
|
||||||
|
expect(itemCountFilter('1b1')).toBe('Displaying 0 items');
|
||||||
|
expect(itemCountFilter(0)).toBe('Displaying 0 items');
|
||||||
|
expect(itemCountFilter(1)).toBe('Displaying 1 item');
|
||||||
|
expect(itemCountFilter(1.2)).toBe('Displaying 1 item');
|
||||||
|
expect(itemCountFilter(1.6)).toBe('Displaying 2 items');
|
||||||
|
expect(itemCountFilter(-1)).toBe('Displaying 0 items');
|
||||||
|
expect(itemCountFilter(-1.2)).toBe('Displaying 0 items');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('trans', function () {
|
||||||
|
it('should return translated text', inject(function (transFilter) {
|
||||||
|
expect(transFilter("Howdy")).toBe("Howdy");
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
})();
|
||||||
describe('gb', function () {
|
|
||||||
var gbFilter;
|
|
||||||
beforeEach(inject(function (_gbFilter_) {
|
|
||||||
gbFilter = _gbFilter_;
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('returns given numeric value properly', function () {
|
|
||||||
expect(gbFilter(12)).toBe('12 GB');
|
|
||||||
expect(gbFilter(-12)).toBe('-12 GB');
|
|
||||||
expect(gbFilter(12.12)).toBe('12.12 GB');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty string for non-numeric', function () {
|
|
||||||
expect(gbFilter('humbug')).toBe('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty string for null', function () {
|
|
||||||
expect(gbFilter(null)).toBe('');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('mb', function () {
|
|
||||||
var mbFilter;
|
|
||||||
beforeEach(inject(function (_mbFilter_) {
|
|
||||||
mbFilter = _mbFilter_;
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('returns given numeric value properly', function () {
|
|
||||||
expect(mbFilter(12)).toBe('12 MB');
|
|
||||||
expect(mbFilter(-12)).toBe('-12 MB');
|
|
||||||
expect(mbFilter(12.12)).toBe('12.12 MB');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty string for non-numeric', function () {
|
|
||||||
expect(mbFilter('humbug')).toBe('');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty string for null', function () {
|
|
||||||
expect(mbFilter(null)).toBe('');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('title', function () {
|
|
||||||
var titleFilter;
|
|
||||||
beforeEach(inject(function (_titleFilter_) {
|
|
||||||
titleFilter = _titleFilter_;
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('capitalizes as expected', function () {
|
|
||||||
expect(titleFilter('title')).toBe('Title');
|
|
||||||
expect(titleFilter('we have several words')).toBe('We Have Several Words');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles non-strings correctly', function () {
|
|
||||||
expect(titleFilter(42)).toBe(42);
|
|
||||||
expect(titleFilter(null)).toBe(null);
|
|
||||||
expect(titleFilter(undefined)).toBe(undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not mess up properly capitalized strings', function () {
|
|
||||||
expect(titleFilter('I Love OpenStack Horizon!')).toBe('I Love OpenStack Horizon!');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles strings beginning with numbers', function () {
|
|
||||||
expect(titleFilter('3abc')).toBe('3abc');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('noUnderscore', function () {
|
|
||||||
var noUnderscoreFilter;
|
|
||||||
beforeEach(inject(function (_noUnderscoreFilter_) {
|
|
||||||
noUnderscoreFilter = _noUnderscoreFilter_;
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('replaces all underscores with spaces', function () {
|
|
||||||
expect(noUnderscoreFilter('_this_is___a_lot____of_underscores__'))
|
|
||||||
.toBe(' this is a lot of underscores ');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns non-string input', function () {
|
|
||||||
expect(noUnderscoreFilter(null)).toBe(null);
|
|
||||||
expect(noUnderscoreFilter(false)).toBe(false);
|
|
||||||
expect(noUnderscoreFilter(true)).toBe(true);
|
|
||||||
expect(noUnderscoreFilter('')).toBe('');
|
|
||||||
expect(noUnderscoreFilter(21)).toBe(21);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("decode", function () {
|
|
||||||
var decodeFilter;
|
|
||||||
beforeEach(inject(function (_decodeFilter_) {
|
|
||||||
decodeFilter = _decodeFilter_;
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("Returns value when key is present", function () {
|
|
||||||
expect(decodeFilter('PRESENT', {'PRESENT': 'Here'})).toBe('Here');
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Returns value when key is present and value is falsy", function () {
|
|
||||||
expect(decodeFilter('PRESENT', {'PRESENT': false})).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Returns input when key is not present", function () {
|
|
||||||
expect(decodeFilter('NOT_PRESENT', {'PRESENT': 'Here'})).toBe('NOT_PRESENT');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('bytes', function () {
|
|
||||||
var bytesFilter;
|
|
||||||
beforeEach(inject(function (_bytesFilter_) {
|
|
||||||
bytesFilter = _bytesFilter_;
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('returns TB values', function () {
|
|
||||||
expect(bytesFilter(1099511627776)).toBe('1.00 TB');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns GB values', function () {
|
|
||||||
expect(bytesFilter(1073741824)).toBe('1.00 GB');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns MB values', function () {
|
|
||||||
expect(bytesFilter(1048576)).toBe('1.00 MB');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns KB values', function () {
|
|
||||||
expect(bytesFilter(1024)).toBe('1.00 KB');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns byte values', function () {
|
|
||||||
expect(bytesFilter(0)).toBe('0 bytes');
|
|
||||||
expect(bytesFilter(1)).toBe('1 bytes');
|
|
||||||
expect(bytesFilter(1023)).toBe('1023 bytes');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles non-numbers correctly', function () {
|
|
||||||
expect(bytesFilter('Yo!')).toBe('');
|
|
||||||
expect(bytesFilter(null)).toBe('');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('itemCount', function () {
|
|
||||||
|
|
||||||
it('should return translated text with item count',
|
|
||||||
inject(function (itemCountFilter) {
|
|
||||||
expect(itemCountFilter(null)).toBe('Displaying 0 items');
|
|
||||||
expect(itemCountFilter(undefined)).toBe('Displaying 0 items');
|
|
||||||
expect(itemCountFilter(true)).toBe('Displaying 1 item');
|
|
||||||
expect(itemCountFilter(false)).toBe('Displaying 0 items');
|
|
||||||
expect(itemCountFilter('a')).toBe('Displaying 0 items');
|
|
||||||
expect(itemCountFilter('0')).toBe('Displaying 0 items');
|
|
||||||
expect(itemCountFilter('1')).toBe('Displaying 1 item');
|
|
||||||
expect(itemCountFilter('1e1')).toBe('Displaying 10 items');
|
|
||||||
expect(itemCountFilter('1b1')).toBe('Displaying 0 items');
|
|
||||||
expect(itemCountFilter(0)).toBe('Displaying 0 items');
|
|
||||||
expect(itemCountFilter(1)).toBe('Displaying 1 item');
|
|
||||||
expect(itemCountFilter(1.2)).toBe('Displaying 1 item');
|
|
||||||
expect(itemCountFilter(1.6)).toBe('Displaying 2 items');
|
|
||||||
expect(itemCountFilter(-1)).toBe('Displaying 0 items');
|
|
||||||
expect(itemCountFilter(-1.2)).toBe('Displaying 0 items');
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('trans', function() {
|
|
||||||
|
|
||||||
it('should return translated text', inject(function(transFilter) {
|
|
||||||
expect(transFilter("Howdy")).toBe("Howdy");
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
@ -13,43 +13,37 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
(function () {
|
||||||
(function() {
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module("horizon.framework.util.i18n", [])
|
angular.module('horizon.framework.util.i18n', [])
|
||||||
|
|
||||||
/*
|
|
||||||
* @name horizon.framework.util.i18n.gettext
|
|
||||||
* @description
|
|
||||||
* Provides a wrapper for translation, using the global 'gettext'
|
|
||||||
* function if it is present. Provides a method that
|
|
||||||
* simply returns the input if the expected global 'gettext' is
|
|
||||||
* not provided.
|
|
||||||
*
|
|
||||||
* Ideally once gettext is no longer needed on a global scope,
|
|
||||||
* the global ref can be deleted here. For now that is not possible.
|
|
||||||
* Also, if future alternate means were provided, we could put that
|
|
||||||
* logic here.
|
|
||||||
*
|
|
||||||
* This could also be done in the context of the filter, but
|
|
||||||
* the approach taken here was to separate business logic
|
|
||||||
* (translation) from filters, which are arguably more
|
|
||||||
* presentation-oriented.
|
|
||||||
*/
|
|
||||||
.factory("horizon.framework.util.i18n.gettext", ['$window', function($window) {
|
|
||||||
|
|
||||||
// If no global function, revert to just returning given text.
|
|
||||||
var gettextFunc = $window.gettext || function(x) { return x; };
|
|
||||||
|
|
||||||
// Eventually, could delete the window gettext references here,
|
|
||||||
// or provide an appropriate method.
|
|
||||||
|
|
||||||
return function() {
|
|
||||||
return gettextFunc.apply(this, arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
}]);
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @name horizon.framework.util.i18n.gettext
|
||||||
|
* @description
|
||||||
|
* Provides a wrapper for translation, using the global 'gettext'
|
||||||
|
* function if it is present. Provides a method that
|
||||||
|
* simply returns the input if the expected global 'gettext' is
|
||||||
|
* not provided.
|
||||||
|
*
|
||||||
|
* Ideally once gettext is no longer needed on a global scope,
|
||||||
|
* the global ref can be deleted here. For now that is not possible.
|
||||||
|
* Also, if future alternate means were provided, we could put that
|
||||||
|
* logic here.
|
||||||
|
*
|
||||||
|
* This could also be done in the context of the filter, but
|
||||||
|
* the approach taken here was to separate business logic
|
||||||
|
* (translation) from filters, which are arguably more
|
||||||
|
* presentation-oriented.
|
||||||
|
*/
|
||||||
|
.factory('horizon.framework.util.i18n.gettext', ['$window', function ($window) {
|
||||||
|
// If no global function, revert to just returning given text.
|
||||||
|
var gettextFunc = $window.gettext || function (x) { return x; };
|
||||||
|
|
||||||
|
// Eventually, could delete the window gettext references here,
|
||||||
|
// or provide an appropriate method.
|
||||||
|
return function () {
|
||||||
|
return gettextFunc.apply(this, arguments);
|
||||||
|
};
|
||||||
|
}]);
|
||||||
})();
|
})();
|
||||||
|
@ -13,52 +13,46 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
(function () {
|
||||||
(function() {
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe('horizon.framework.util.i18n', function() {
|
describe('horizon.framework.util.i18n', function () {
|
||||||
|
|
||||||
beforeEach(module('horizon.framework.util.i18n'));
|
beforeEach(module('horizon.framework.util.i18n'));
|
||||||
|
|
||||||
describe('gettext', function() {
|
describe('gettext', function () {
|
||||||
var factory;
|
var factory;
|
||||||
|
|
||||||
describe('Normal operation', function() {
|
describe('Normal operation', function () {
|
||||||
|
beforeEach(inject(function ($injector) {
|
||||||
beforeEach(inject(function($injector) {
|
|
||||||
factory = $injector.get("horizon.framework.util.i18n.gettext");
|
factory = $injector.get("horizon.framework.util.i18n.gettext");
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('defines the factory', function() {
|
it('defines the factory', function () {
|
||||||
expect(factory).toBeDefined();
|
expect(factory).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('function returns what is given', function() {
|
it('function returns what is given', function () {
|
||||||
expect(factory("Hello")).toBe('Hello');
|
expect(factory("Hello")).toBe('Hello');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("injected window.gettext", function() {
|
describe("injected window.gettext", function () {
|
||||||
|
beforeEach(module(function ($provide) {
|
||||||
beforeEach(module(function($provide) {
|
var $window = { gettext: function (x) { return x.replace(/good/, 'bad'); } };
|
||||||
var $window = {gettext: function(x) { return x.replace(/good/, 'bad'); }};
|
|
||||||
$provide.value('$window', $window);
|
$provide.value('$window', $window);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Get the factory by name.
|
// Get the factory by name.
|
||||||
beforeEach(inject(function($injector) {
|
beforeEach(inject(function ($injector) {
|
||||||
factory = $injector.get("horizon.framework.util.i18n.gettext");
|
factory = $injector.get("horizon.framework.util.i18n.gettext");
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('uses the window gettext when available', function() {
|
it('uses the window gettext when available', function () {
|
||||||
// we can't spy on the window gettext due to (appropriate)
|
// we can't spy on the window gettext due to (appropriate)
|
||||||
// indirection. But we can make sure it was called.
|
// indirection. But we can make sure it was called.
|
||||||
expect(factory("good cop")).toBe("bad cop");
|
expect(factory("good cop")).toBe("bad cop");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -11,5 +11,4 @@
|
|||||||
'horizon.framework.util.validators'
|
'horizon.framework.util.validators'
|
||||||
])
|
])
|
||||||
.constant('horizon.framework.util.basePath', '/static/framework/util/');
|
.constant('horizon.framework.util.basePath', '/static/framework/util/');
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
* |---------------------------------------------------------------------------------|
|
* |---------------------------------------------------------------------------------|
|
||||||
* | {@link horizon.framework.util.validators.directive:validateNumberMax `validateNumberMax`} |
|
* | {@link horizon.framework.util.validators.directive:validateNumberMax `validateNumberMax`} |
|
||||||
* | {@link horizon.framework.util.validators.directive:validateNumberMin `validateNumberMin`} |
|
* | {@link horizon.framework.util.validators.directive:validateNumberMin `validateNumberMin`} |
|
||||||
|
* | {@link horizon.framework.util.validators.directive:notBlank `notBlank`} |
|
||||||
* | {@link horizon.framework.util.validators.directive:hzPasswordMatch `hzPasswordMatch`} |
|
* | {@link horizon.framework.util.validators.directive:hzPasswordMatch `hzPasswordMatch`} |
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -47,38 +48,40 @@
|
|||||||
* validate-number-max="{$ maxNumber $}">
|
* validate-number-max="{$ maxNumber $}">
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
.directive('validateNumberMax', [function () {
|
.directive('validateNumberMax', [function () {
|
||||||
return {
|
return {
|
||||||
require: 'ngModel',
|
require: 'ngModel',
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
link: function (scope, element, attrs, ctrl) {
|
link: function (scope, element, attrs, ctrl) {
|
||||||
if (!ctrl) {
|
if (!ctrl) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxValidator = function (value) {
|
||||||
|
var max = scope.$eval(attrs.validateNumberMax);
|
||||||
|
if (angular.isDefined(max) && !ctrl.$isEmpty(value) && value > max) {
|
||||||
|
ctrl.$setValidity('validateNumberMax', false);
|
||||||
|
} else {
|
||||||
|
ctrl.$setValidity('validateNumberMax', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxValidator = function (value) {
|
// Return the value rather than undefined if invalid
|
||||||
var max = scope.$eval(attrs.validateNumberMax);
|
return value;
|
||||||
if (angular.isDefined(max) && !ctrl.$isEmpty(value) && value > max) {
|
};
|
||||||
ctrl.$setValidity('validateNumberMax', false);
|
|
||||||
} else {
|
|
||||||
ctrl.$setValidity('validateNumberMax', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the value rather than undefined if invalid
|
/**
|
||||||
return value;
|
* Re-validate if value is changed through the UI
|
||||||
};
|
* or model (programmatically)
|
||||||
|
*/
|
||||||
|
ctrl.$parsers.push(maxValidator);
|
||||||
|
ctrl.$formatters.push(maxValidator);
|
||||||
|
|
||||||
// Re-validate if value is changed through the UI
|
attrs.$observe('validateNumberMax', function () {
|
||||||
// or model (programmatically)
|
maxValidator(ctrl.$modelValue);
|
||||||
ctrl.$parsers.push(maxValidator);
|
});
|
||||||
ctrl.$formatters.push(maxValidator);
|
}
|
||||||
|
};
|
||||||
attrs.$observe('validateNumberMax', function () {
|
}])
|
||||||
maxValidator(ctrl.$modelValue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}])
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ngdoc directive
|
* @ngdoc directive
|
||||||
@ -107,107 +110,115 @@
|
|||||||
* validate-number-min="{$ minNumber $}">
|
* validate-number-min="{$ minNumber $}">
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
.directive('validateNumberMin', [function () {
|
.directive('validateNumberMin', [function () {
|
||||||
return {
|
return {
|
||||||
require: 'ngModel',
|
require: 'ngModel',
|
||||||
restrict: 'A',
|
restrict: 'A',
|
||||||
link: function (scope, element, attrs, ctrl) {
|
link: function (scope, element, attrs, ctrl) {
|
||||||
if (!ctrl) {
|
if (!ctrl) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var minValidator = function (value) {
|
||||||
|
var min = scope.$eval(attrs.validateNumberMin);
|
||||||
|
if (angular.isDefined(min) && !ctrl.$isEmpty(value) && value < min) {
|
||||||
|
ctrl.$setValidity('validateNumberMin', false);
|
||||||
|
} else {
|
||||||
|
ctrl.$setValidity('validateNumberMin', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var minValidator = function (value) {
|
// Return the value rather than undefined if invalid
|
||||||
var min = scope.$eval(attrs.validateNumberMin);
|
return value;
|
||||||
if (angular.isDefined(min) && !ctrl.$isEmpty(value) && value < min) {
|
};
|
||||||
ctrl.$setValidity('validateNumberMin', false);
|
|
||||||
} else {
|
|
||||||
ctrl.$setValidity('validateNumberMin', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the value rather than undefined if invalid
|
/**
|
||||||
return value;
|
* Re-validate if value is changed through the UI
|
||||||
};
|
* or model (programmatically)
|
||||||
|
*/
|
||||||
|
ctrl.$parsers.push(minValidator);
|
||||||
|
ctrl.$formatters.push(minValidator);
|
||||||
|
|
||||||
// Re-validate if value is changed through the UI
|
attrs.$observe('validateNumberMin', function () {
|
||||||
// or model (programmatically)
|
minValidator(ctrl.$modelValue);
|
||||||
ctrl.$parsers.push(minValidator);
|
});
|
||||||
ctrl.$formatters.push(minValidator);
|
}
|
||||||
|
};
|
||||||
|
}])
|
||||||
|
|
||||||
attrs.$observe('validateNumberMin', function () {
|
/**
|
||||||
minValidator(ctrl.$modelValue);
|
* @ngdoc directive
|
||||||
});
|
* @name notBlank
|
||||||
}
|
* @element ng-model
|
||||||
};
|
* @description Ensure that the value is not blank
|
||||||
}])
|
*/
|
||||||
|
.directive('notBlank', function () {
|
||||||
.directive('notBlank', function () {
|
return {
|
||||||
return {
|
require: 'ngModel',
|
||||||
require: 'ngModel',
|
link: function (scope, elm, attrs, ctrl) {
|
||||||
link: function (scope, elm, attrs, ctrl) {
|
ctrl.$parsers.unshift(function (viewValue) {
|
||||||
ctrl.$parsers.unshift(function (viewValue) {
|
if (viewValue.length) {
|
||||||
if (viewValue.length) {
|
// it is valid
|
||||||
// it is valid
|
ctrl.$setValidity('notBlank', true);
|
||||||
ctrl.$setValidity('notBlank', true);
|
return viewValue;
|
||||||
return viewValue;
|
|
||||||
}
|
|
||||||
// it is invalid, return undefined (no model update)
|
|
||||||
ctrl.$setValidity('notBlank', false);
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ngdoc directive
|
|
||||||
* @name hzPasswordMatch
|
|
||||||
* @element ng-model
|
|
||||||
*
|
|
||||||
* @description
|
|
||||||
* A directive to ensure that password matches.
|
|
||||||
* Changing the password or confirmation password triggers a validation check.
|
|
||||||
* However, only the confirmation password will show an error if match is false.
|
|
||||||
* The goal is to check that confirmation password matches the password,
|
|
||||||
* not whether the password matches the confirmation password.
|
|
||||||
* The behavior here is NOT bi-directional.
|
|
||||||
*
|
|
||||||
* @restrict A
|
|
||||||
*
|
|
||||||
* @scope
|
|
||||||
* hzPasswordMatch - form model to validate against
|
|
||||||
*
|
|
||||||
* @example:
|
|
||||||
* <form name="form">
|
|
||||||
* <input type='password' id="psw" ng-model="user.psw" name="psw">
|
|
||||||
* <input type='password' ng-model="user.cnf" hz-password-match="form.psw">
|
|
||||||
* </form>
|
|
||||||
*
|
|
||||||
* Note that id and name are required for the password input.
|
|
||||||
* This directive uses the form model and id for validation check.
|
|
||||||
*/
|
|
||||||
.directive('hzPasswordMatch', function(){
|
|
||||||
return {
|
|
||||||
restrict: 'A',
|
|
||||||
require: 'ngModel',
|
|
||||||
scope: { pw: '=hzPasswordMatch' },
|
|
||||||
link: function(scope, element, attr, ctrl){
|
|
||||||
|
|
||||||
// helper function to check that password matches
|
|
||||||
function passwordCheck(){
|
|
||||||
scope.$apply(function(){
|
|
||||||
var match = (ctrl.$modelValue === scope.pw.$modelValue);
|
|
||||||
ctrl.$setValidity('match', match);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
// it is invalid, return undefined (no model update)
|
||||||
|
ctrl.$setValidity('notBlank', false);
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
// this ensures that typing in either input
|
/**
|
||||||
// will trigger the password match
|
* @ngdoc directive
|
||||||
var pwElement = $('#'+scope.pw.$name);
|
* @name hzPasswordMatch
|
||||||
pwElement.on('keyup change', passwordCheck);
|
* @element ng-model
|
||||||
element.on('keyup change', passwordCheck);
|
*
|
||||||
|
* @description
|
||||||
|
* A directive to ensure that password matches.
|
||||||
|
* Changing the password or confirmation password triggers a validation check.
|
||||||
|
* However, only the confirmation password will show an error if match is false.
|
||||||
|
* The goal is to check that confirmation password matches the password,
|
||||||
|
* not whether the password matches the confirmation password.
|
||||||
|
* The behavior here is NOT bi-directional.
|
||||||
|
*
|
||||||
|
* @restrict A
|
||||||
|
*
|
||||||
|
* @scope
|
||||||
|
* hzPasswordMatch - form model to validate against
|
||||||
|
*
|
||||||
|
* @example:
|
||||||
|
* <form name="form">
|
||||||
|
* <input type='password' id="psw" ng-model="user.psw" name="psw">
|
||||||
|
* <input type='password' ng-model="user.cnf" hz-password-match="form.psw">
|
||||||
|
* </form>
|
||||||
|
*
|
||||||
|
* Note that id and name are required for the password input.
|
||||||
|
* This directive uses the form model and id for validation check.
|
||||||
|
*/
|
||||||
|
.directive('hzPasswordMatch', function () {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
require: 'ngModel',
|
||||||
|
scope: { pw: '=hzPasswordMatch' },
|
||||||
|
link: function (scope, element, attr, ctrl) {
|
||||||
|
|
||||||
} // end of link
|
// helper function to check that password matches
|
||||||
}; // end of return
|
function passwordCheck() {
|
||||||
}); // end of directive
|
scope.$apply(function () {
|
||||||
|
var match = (ctrl.$modelValue === scope.pw.$modelValue);
|
||||||
|
ctrl.$setValidity('match', match);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}());
|
/**
|
||||||
|
* this ensures that typing in either input
|
||||||
|
* will trigger the password match
|
||||||
|
*/
|
||||||
|
var pwElement = $('#' + scope.pw.$name);
|
||||||
|
pwElement.on('keyup change', passwordCheck);
|
||||||
|
element.on('keyup change', passwordCheck);
|
||||||
|
} // end of link
|
||||||
|
}; // end of return
|
||||||
|
}); // end of directive
|
||||||
|
})();
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
(function() {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe('horizon.framework.util.validators module', function() {
|
describe('horizon.framework.util.validators module', function () {
|
||||||
it('should have been defined', function() {
|
it('should have been defined', function () {
|
||||||
expect(angular.module('horizon.framework.util.validators')).toBeDefined();
|
expect(angular.module('horizon.framework.util.validators')).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('validators directive', function() {
|
describe('validators directive', function () {
|
||||||
|
|
||||||
beforeEach(module('horizon.framework.widgets'));
|
beforeEach(module('horizon.framework.widgets'));
|
||||||
beforeEach(module('horizon.framework.util.validators'));
|
beforeEach(module('horizon.framework.util.validators'));
|
||||||
|
|
||||||
describe('validateNumberMax directive', function() {
|
describe('validateNumberMax directive', function () {
|
||||||
|
|
||||||
var $scope, $form;
|
var $scope, $form;
|
||||||
|
|
||||||
beforeEach(inject(function($injector) {
|
beforeEach(inject(function ($injector) {
|
||||||
var $compile = $injector.get('$compile');
|
var $compile = $injector.get('$compile');
|
||||||
$scope = $injector.get('$rootScope').$new();
|
$scope = $injector.get('$rootScope').$new();
|
||||||
|
|
||||||
@ -33,32 +31,30 @@
|
|||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should pass validation initially when count is 0 and max is 1', function() {
|
it('should pass validation initially when count is 0 and max is 1', function () {
|
||||||
expect($form.count.$valid).toBe(true);
|
expect($form.count.$valid).toBe(true);
|
||||||
expect($form.$valid).toBe(true);
|
expect($form.$valid).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not pass validation if count increased to 2 and max is 1', function() {
|
it('should not pass validation if count increased to 2 and max is 1', function () {
|
||||||
$form.count.$setViewValue(2);
|
$form.count.$setViewValue(2);
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
expect($form.count.$valid).toBe(false);
|
expect($form.count.$valid).toBe(false);
|
||||||
expect($form.$valid).toBe(false);
|
expect($form.$valid).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass validation if count is empty', function() {
|
it('should pass validation if count is empty', function () {
|
||||||
$form.count.$setViewValue('');
|
$form.count.$setViewValue('');
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
expect($form.count.$valid).toBe(true);
|
expect($form.count.$valid).toBe(true);
|
||||||
expect($form.$valid).toBe(true);
|
expect($form.$valid).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('validateNumberMin directive', function() {
|
describe('validateNumberMin directive', function () {
|
||||||
|
|
||||||
var $scope, $form;
|
var $scope, $form;
|
||||||
|
|
||||||
beforeEach(inject(function($injector) {
|
beforeEach(inject(function ($injector) {
|
||||||
var $compile = $injector.get('$compile');
|
var $compile = $injector.get('$compile');
|
||||||
$scope = $injector.get('$rootScope').$new();
|
$scope = $injector.get('$rootScope').$new();
|
||||||
|
|
||||||
@ -75,12 +71,12 @@
|
|||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should not pass validation initially when count is 0 and min is 1', function() {
|
it('should not pass validation initially when count is 0 and min is 1', function () {
|
||||||
expect($form.count.$valid).toBe(false);
|
expect($form.count.$valid).toBe(false);
|
||||||
expect($form.$valid).toBe(false);
|
expect($form.$valid).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass validation if count increased to 2 and min is 1', function() {
|
it('should pass validation if count increased to 2 and min is 1', function () {
|
||||||
$form.count.$setViewValue(2);
|
$form.count.$setViewValue(2);
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
expect($scope.count).toBe(2);
|
expect($scope.count).toBe(2);
|
||||||
@ -88,16 +84,15 @@
|
|||||||
expect($form.$valid).toBe(true);
|
expect($form.$valid).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass validation if count is empty', function() {
|
it('should pass validation if count is empty', function () {
|
||||||
$form.count.$setViewValue('');
|
$form.count.$setViewValue('');
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
expect($form.count.$valid).toBe(true);
|
expect($form.count.$valid).toBe(true);
|
||||||
expect($form.$valid).toBe(true);
|
expect($form.$valid).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hzPasswordMatch directive', function() {
|
describe('hzPasswordMatch directive', function () {
|
||||||
|
|
||||||
var $compile, $rootScope;
|
var $compile, $rootScope;
|
||||||
var element, password, cpassword;
|
var element, password, cpassword;
|
||||||
@ -108,7 +103,7 @@
|
|||||||
'hz-password-match="form.password">' +
|
'hz-password-match="form.password">' +
|
||||||
'</form>';
|
'</form>';
|
||||||
|
|
||||||
beforeEach(inject(function($injector){
|
beforeEach(inject(function ($injector) {
|
||||||
$compile = $injector.get('$compile');
|
$compile = $injector.get('$compile');
|
||||||
$rootScope = $injector.get('$rootScope').$new();
|
$rootScope = $injector.get('$rootScope').$new();
|
||||||
|
|
||||||
@ -122,60 +117,57 @@
|
|||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should be initially empty', function() {
|
it('should be initially empty', function () {
|
||||||
expect(password.val()).toEqual('');
|
expect(password.val()).toEqual('');
|
||||||
expect(password.val()).toEqual(cpassword.val());
|
expect(password.val()).toEqual(cpassword.val());
|
||||||
expect(cpassword.hasClass('ng-valid')).toBe(true);
|
expect(cpassword.hasClass('ng-valid')).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not match if user changes only password', function(done) {
|
it('should not match if user changes only password', function (done) {
|
||||||
$rootScope.user.password = 'password';
|
$rootScope.user.password = 'password';
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
cpassword.change();
|
cpassword.change();
|
||||||
setTimeout(function(){
|
setTimeout(function () {
|
||||||
expect(cpassword.val()).not.toEqual(password.val());
|
expect(cpassword.val()).not.toEqual(password.val());
|
||||||
expect(cpassword.hasClass('ng-invalid')).toBe(true);
|
expect(cpassword.hasClass('ng-invalid')).toBe(true);
|
||||||
done();
|
done();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not match if user changes only confirmation password', function(done) {
|
it('should not match if user changes only confirmation password', function (done) {
|
||||||
$rootScope.user.cpassword = 'password';
|
$rootScope.user.cpassword = 'password';
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
cpassword.change();
|
cpassword.change();
|
||||||
setTimeout(function(){
|
setTimeout(function () {
|
||||||
expect(cpassword.val()).not.toEqual(password.val());
|
expect(cpassword.val()).not.toEqual(password.val());
|
||||||
expect(cpassword.hasClass('ng-invalid')).toBe(true);
|
expect(cpassword.hasClass('ng-invalid')).toBe(true);
|
||||||
done();
|
done();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should match if both passwords are the same', function(done) {
|
it('should match if both passwords are the same', function (done) {
|
||||||
$rootScope.user.password = 'password';
|
$rootScope.user.password = 'password';
|
||||||
$rootScope.user.cpassword = 'password';
|
$rootScope.user.cpassword = 'password';
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
cpassword.change();
|
cpassword.change();
|
||||||
setTimeout(function(){
|
setTimeout(function () {
|
||||||
expect(cpassword.val()).toEqual(password.val());
|
expect(cpassword.val()).toEqual(password.val());
|
||||||
expect(cpassword.hasClass('ng-valid')).toBe(true);
|
expect(cpassword.hasClass('ng-valid')).toBe(true);
|
||||||
done();
|
done();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not match if both passwords are different', function(done) {
|
it('should not match if both passwords are different', function (done) {
|
||||||
$rootScope.user.password = 'password123';
|
$rootScope.user.password = 'password123';
|
||||||
$rootScope.user.cpassword = 'password345';
|
$rootScope.user.cpassword = 'password345';
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
cpassword.change();
|
cpassword.change();
|
||||||
setTimeout(function(){
|
setTimeout(function () {
|
||||||
expect(cpassword.val()).not.toEqual(password.val());
|
expect(cpassword.val()).not.toEqual(password.val());
|
||||||
expect(cpassword.hasClass('ng-invalid')).toBe(true);
|
expect(cpassword.hasClass('ng-invalid')).toBe(true);
|
||||||
done();
|
done();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
}); // end of hzPasswordMatch directive
|
}); // end of hzPasswordMatch directive
|
||||||
|
|
||||||
});
|
});
|
||||||
|
})();
|
||||||
})();
|
|
||||||
|
@ -97,5 +97,4 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -8,18 +8,16 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('workflow factory', function () {
|
describe('workflow factory', function () {
|
||||||
|
var workflow, spec;
|
||||||
var workflow,
|
var decorators = [
|
||||||
spec,
|
function (spec) {
|
||||||
decorators = [
|
angular.forEach(spec.steps, function (step) {
|
||||||
function (spec) {
|
if (step.requireSomeServices) {
|
||||||
angular.forEach(spec.steps, function (step) {
|
step.checkReadiness = function () {};
|
||||||
if (step.requireSomeServices) {
|
|
||||||
step.checkReadiness = function () {};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
];
|
});
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
beforeEach(module('horizon.framework.util.workflow'));
|
beforeEach(module('horizon.framework.util.workflow'));
|
||||||
|
|
||||||
@ -55,5 +53,4 @@
|
|||||||
expect(angular.isFunction(steps[2].checkReadiness)).toBe(true);
|
expect(angular.isFunction(steps[2].checkReadiness)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('horizon.framework.widgets', [
|
angular.module('horizon.framework.widgets', [
|
||||||
'horizon.framework.widgets.help-panel',
|
'horizon.framework.widgets.help-panel',
|
||||||
|
Loading…
Reference in New Issue
Block a user