Merge "JSCS cleanup - horizon/static/framework conf and util"

This commit is contained in:
Jenkins 2015-06-19 14:51:04 +00:00 committed by Gerrit Code Review
commit bf9acb39a5
14 changed files with 581 additions and 607 deletions

View File

@ -1,10 +1,10 @@
/*global angular*/
(function () {
'use strict';
angular.module('horizon.framework.conf', [])
angular
.module('horizon.framework.conf', [])
.constant('horizon.framework.conf.spinner_options', {
inline: {
inline: {
lines: 10,
length: 5,
width: 2,
@ -14,7 +14,7 @@
trail: 50,
zIndex: 100
},
modal: {
modal: {
lines: 10,
length: 15,
width: 4,
@ -33,4 +33,4 @@
trail: 50
}
});
}());
})();

View File

@ -1,39 +1,41 @@
(function () {
'use strict';
angular.module('horizon.framework', [
'horizon.framework.conf',
'horizon.framework.util',
'horizon.framework.widgets'
])
.constant('horizon.framework.basePath', '/static/framework/')
angular
.module('horizon.framework', [
'horizon.framework.conf',
'horizon.framework.util',
'horizon.framework.widgets'
])
.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',
function ($interpolateProvider, $httpProvider) {
// Http global settings for ease of use
$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
// allow us to mix angular with django templates
$interpolateProvider.startSymbol('{$');
$interpolateProvider.endSymbol('$}');
// Http global settings for ease of use
$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';
// 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');
// 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);
}
return $q.reject(error);
}
};
});
}]);
};
});
}
]);
})();

View File

@ -1,4 +1,4 @@
(function() {
(function () {
'use strict';
/**
@ -41,12 +41,12 @@
* </tr>
* ```
*/
.directive('bindScope', function() {
.directive('bindScope', function () {
return {
restrict: 'A',
link: function(scope, element, attrs, ctrl, transclude) {
link: function (scope, element, attrs, ctrl, transclude) {
if (transclude) {
transclude(scope, function(clone) {
transclude(scope, function (clone) {
var detailElt = element.find('[bind-scope-target]');
if (detailElt.length) {
detailElt.append(clone);
@ -56,5 +56,4 @@
}
};
});
})();
})();

View File

@ -1,22 +1,21 @@
(function() {
(function () {
'use strict';
describe('horizon.framework.util.bind-scope module', function() {
it('should have been defined', function() {
describe('horizon.framework.util.bind-scope module', function () {
it('should have been defined', function () {
expect(angular.module('horizon.framework.util.bind-scope')).toBeDefined();
});
});
describe('bind-scope directive', function() {
describe('bind-scope directive', function () {
var $scope, $element;
beforeEach(module('horizon.framework'));
beforeEach(module('horizon.framework.widgets'));
beforeEach(module('horizon.framework.util.bind-scope'));
beforeEach(module('horizon.framework.util.bind-scope', function($compileProvider) {
$compileProvider.directive('testBindScope', function() {
beforeEach(module('horizon.framework.util.bind-scope', function ($compileProvider) {
$compileProvider.directive('testBindScope', function () {
return {
restrict: 'E',
scope: {
@ -30,7 +29,7 @@
});
}));
beforeEach(inject(function($injector) {
beforeEach(inject(function ($injector) {
var $compile = $injector.get('$compile');
$scope = $injector.get('$rootScope').$new();
@ -48,17 +47,15 @@
$scope.$digest();
}));
it('should have 3 list items', function() {
it('should have 3 list items', function () {
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');
expect(listItems[0].textContent.trim()).toBe('cat');
expect(listItems[1].textContent.trim()).toBe('dog');
expect(listItems[2].textContent.trim()).toBe('fish');
});
});
})();
})();

View File

@ -25,128 +25,128 @@
*/
angular.module('horizon.framework.util.filters', ['horizon.framework.util.i18n'])
/**
* @ngdoc filter
* @name yesno
* @description
* Evaluates given input for standard truthiness and returns translation
* of 'Yes' and 'No' for true/false respectively.
*/
.filter('yesno', ['horizon.framework.util.i18n.gettext', function(gettext) {
return function(input) {
return (input ? gettext("Yes") : gettext("No"));
};
}])
/**
* @ngdoc filter
* @name yesno
* @description
* Evaluates given input for standard truthiness and returns translation
* of 'Yes' and 'No' for true/false respectively.
*/
.filter('yesno', ['horizon.framework.util.i18n.gettext', function (gettext) {
return function (input) {
return (input ? gettext("Yes") : gettext("No"));
};
}])
/**
* @ngdoc filter
* @name gb
* @description
* Expects numeric value and suffixes translated 'GB' with spacing.
* Returns empty string if input is not a number or is null.
*/
.filter('gb', function() {
return function(input) {
if (isNaN(input) || null === input) {
return '';
} else {
return interpolate(gettext("%s GB"), [input.toString()]);
}
};
})
/**
* @ngdoc filter
* @name gb
* @description
* Expects numeric value and suffixes translated 'GB' with spacing.
* Returns empty string if input is not a number or is null.
*/
.filter('gb', function () {
return function (input) {
if (isNaN(input) || null === input) {
return '';
} else {
return interpolate(gettext("%s GB"), [input.toString()]);
}
};
})
/**
* @ngdoc filter
* @name mb
* @description
* Expects numeric value and suffixes translated 'MB' with spacing.
* Returns empty string if input is not a number or is null.
*/
.filter('mb', function() {
return function(input) {
if (isNaN(input) || null === input) {
return '';
} else {
return interpolate(gettext("%s MB"), [input.toString()]);
}
};
})
/**
* @ngdoc filter
* @name mb
* @description
* Expects numeric value and suffixes translated 'MB' with spacing.
* Returns empty string if input is not a number or is null.
*/
.filter('mb', function () {
return function (input) {
if (isNaN(input) || null === input) {
return '';
} else {
return interpolate(gettext("%s MB"), [input.toString()]);
}
};
})
/**
* @ngdoc filter
* @name title
* @description
* Capitalizes leading characters of individual words.
*/
.filter('title', function() {
return function(input) {
if (typeof input !== 'string') {
return input;
}
return input.replace(/(?:^|\s)\S/g, function(a) {
return a.toUpperCase();
});
};
})
/**
* @ngdoc filter
* @name title
* @description
* Capitalizes leading characters of individual words.
*/
.filter('title', function () {
return function (input) {
if (typeof input !== 'string') {
return input;
}
return input.replace(/(?:^|\s)\S/g, function (a) {
return a.toUpperCase();
});
};
})
/**
* @ngdoc filter
* @name noUnderscore
* @description
* Replaces all underscores with spaces.
*/
.filter('noUnderscore', function() {
return function(input) {
if (typeof input !== 'string') {
return input;
}
return input.replace(/_/g, ' ');
};
})
/**
* @ngdoc filter
* @name noUnderscore
* @description
* Replaces all underscores with spaces.
*/
.filter('noUnderscore', function () {
return function (input) {
if (typeof input !== 'string') {
return input;
}
return input.replace(/_/g, ' ');
};
})
/**
* @ngdoc filter
* @name decode
* @description
* Returns values based on key and given mapping. If key doesn't exist
* in given mapping, return key. This is useful when translations for
* codes are present.
*/
.filter('decode', function() {
return function(input, mapping) {
var val = mapping[input];
return angular.isDefined(val) ? val : input;
};
})
/**
* @ngdoc filter
* @name decode
* @description
* Returns values based on key and given mapping. If key doesn't exist
* in given mapping, return key. This is useful when translations for
* codes are present.
*/
.filter('decode', function () {
return function (input, mapping) {
var val = mapping[input];
return angular.isDefined(val) ? val : input;
};
})
/**
* @ngdoc filter
* @name bytes
* @description
* Returns a human-readable approximation of the input of bytes,
* converted to a useful unit of measure. Uses 1024-based notation.
*/
.filter('bytes', function() {
return function(input) {
var kb = 1024;
var mb = kb*1024;
var gb = mb*1024;
var tb = gb*1024;
if (isNaN(input) || null === input || input < 0) {
return '';
} else if (input >= tb) {
return interpolate(gettext("%s TB"), [Number(input/tb).toFixed(2)]);
} else if (input >= gb) {
return interpolate(gettext("%s GB"), [Number(input/gb).toFixed(2)]);
} else if (input >= mb) {
return interpolate(gettext("%s MB"), [Number(input/mb).toFixed(2)]);
} else if (input >= kb) {
return interpolate(gettext("%s KB"), [Number(input/kb).toFixed(2)]);
} else {
return interpolate(gettext("%s bytes"), [Math.floor(input)]);
}
};
})
/**
* @ngdoc filter
* @name bytes
* @description
* Returns a human-readable approximation of the input of bytes,
* converted to a useful unit of measure. Uses 1024-based notation.
*/
.filter('bytes', function () {
return function (input) {
var kb = 1024;
var mb = kb * 1024;
var gb = mb * 1024;
var tb = gb * 1024;
if (isNaN(input) || null === input || input < 0) {
return '';
} else if (input >= tb) {
return interpolate(gettext("%s TB"), [Number(input / tb).toFixed(2)]);
} else if (input >= gb) {
return interpolate(gettext("%s GB"), [Number(input / gb).toFixed(2)]);
} else if (input >= mb) {
return interpolate(gettext("%s MB"), [Number(input / mb).toFixed(2)]);
} else if (input >= kb) {
return interpolate(gettext("%s KB"), [Number(input / kb).toFixed(2)]);
} else {
return interpolate(gettext("%s bytes"), [Math.floor(input)]);
}
};
})
/**
* @ngdoc filter
@ -155,11 +155,11 @@
* Displays translated count in table footer.
* Takes only finite numbers.
*/
.filter('itemCount', function() {
return function(input) {
.filter('itemCount', function () {
return function (input) {
var isNumeric = (input !== null && isFinite(input));
var number = isNumeric ? Math.round(input): 0;
var count = (number > 0) ? number: 0;
var number = isNumeric ? Math.round(input) : 0;
var count = (number > 0) ? number : 0;
var format = ngettext('Displaying %s item', 'Displaying %s items', count);
return interpolate(format, [count]);
};
@ -171,11 +171,10 @@
* @description
* Returns translated text.
*/
.filter('trans', ['horizon.framework.util.i18n.gettext', function(gettextFunc) {
return function(input) {
.filter('trans', ['horizon.framework.util.i18n.gettext', function (gettextFunc) {
return function (input) {
// NOTE: uses 'gettextFunc' to avoid message collection.
return gettextFunc(input);
};
}]);
}());

View File

@ -1,220 +1,211 @@
describe('horizon.framework.util.filters', function () {
(function () {
'use strict';
beforeEach(module('horizon.framework.util.i18n'));
beforeEach(module('horizon.framework.util.filters'));
describe('horizon.framework.util.filters', function () {
beforeEach(module('horizon.framework.util.i18n'));
beforeEach(module('horizon.framework.util.filters'));
describe('yesno', function () {
var yesnoFilter;
beforeEach(inject(function (_yesnoFilter_) {
yesnoFilter = _yesnoFilter_;
}));
describe('yesno', function () {
var yesnoFilter;
beforeEach(inject(function (_yesnoFilter_) {
yesnoFilter = _yesnoFilter_;
}));
it('returns Yes for true', function () {
expect(yesnoFilter(true)).toBe('Yes');
it('returns Yes for true', function () {
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 () {
expect(yesnoFilter(false)).toBe('No');
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('');
});
});
it('returns No for null', function () {
expect(yesnoFilter(null)).toBe('No');
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('');
});
});
it('returns No for undefined', function () {
expect(yesnoFilter(undefined)).toBe('No');
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');
});
});
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');
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);
});
});
it('returns No for other falsy values', function () {
expect(yesnoFilter(0)).toBe('No');
expect(yesnoFilter('')).toBe('No');
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");
}));
});
});
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");
}));
});
});
})();

View File

@ -13,43 +13,37 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
(function () {
'use strict';
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);
};
}]);
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);
};
}]);
})();

View File

@ -13,52 +13,46 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
(function () {
'use strict';
describe('horizon.framework.util.i18n', function() {
describe('horizon.framework.util.i18n', function () {
beforeEach(module('horizon.framework.util.i18n'));
describe('gettext', function() {
describe('gettext', function () {
var factory;
describe('Normal operation', function() {
beforeEach(inject(function($injector) {
describe('Normal operation', function () {
beforeEach(inject(function ($injector) {
factory = $injector.get("horizon.framework.util.i18n.gettext");
}));
it('defines the factory', function() {
it('defines the factory', function () {
expect(factory).toBeDefined();
});
it('function returns what is given', function() {
it('function returns what is given', function () {
expect(factory("Hello")).toBe('Hello');
});
});
describe("injected window.gettext", function() {
beforeEach(module(function($provide) {
var $window = {gettext: function(x) { return x.replace(/good/, 'bad'); }};
describe("injected window.gettext", function () {
beforeEach(module(function ($provide) {
var $window = { gettext: function (x) { return x.replace(/good/, 'bad'); } };
$provide.value('$window', $window);
}));
// Get the factory by name.
beforeEach(inject(function($injector) {
beforeEach(inject(function ($injector) {
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)
// indirection. But we can make sure it was called.
expect(factory("good cop")).toBe("bad cop");
});
});
});
});
})();

View File

@ -11,5 +11,4 @@
'horizon.framework.util.validators'
])
.constant('horizon.framework.util.basePath', '/static/framework/util/');
})();

View File

@ -15,6 +15,7 @@
* |---------------------------------------------------------------------------------|
* | {@link horizon.framework.util.validators.directive:validateNumberMax `validateNumberMax`} |
* | {@link horizon.framework.util.validators.directive:validateNumberMin `validateNumberMin`} |
* | {@link horizon.framework.util.validators.directive:notBlank `notBlank`} |
* | {@link horizon.framework.util.validators.directive:hzPasswordMatch `hzPasswordMatch`} |
*
*/
@ -47,38 +48,40 @@
* validate-number-max="{$ maxNumber $}">
* ```
*/
.directive('validateNumberMax', [function () {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
if (!ctrl) {
return;
.directive('validateNumberMax', [function () {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
if (!ctrl) {
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) {
var max = scope.$eval(attrs.validateNumberMax);
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;
};
// 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
// or model (programmatically)
ctrl.$parsers.push(maxValidator);
ctrl.$formatters.push(maxValidator);
attrs.$observe('validateNumberMax', function () {
maxValidator(ctrl.$modelValue);
});
}
};
}])
attrs.$observe('validateNumberMax', function () {
maxValidator(ctrl.$modelValue);
});
}
};
}])
/**
* @ngdoc directive
@ -107,107 +110,115 @@
* validate-number-min="{$ minNumber $}">
* ```
*/
.directive('validateNumberMin', [function () {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
if (!ctrl) {
return;
.directive('validateNumberMin', [function () {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
if (!ctrl) {
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) {
var min = scope.$eval(attrs.validateNumberMin);
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;
};
// 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
// or model (programmatically)
ctrl.$parsers.push(minValidator);
ctrl.$formatters.push(minValidator);
attrs.$observe('validateNumberMin', function () {
minValidator(ctrl.$modelValue);
});
}
};
}])
attrs.$observe('validateNumberMin', function () {
minValidator(ctrl.$modelValue);
});
}
};
}])
.directive('notBlank', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
if (viewValue.length) {
// it is valid
ctrl.$setValidity('notBlank', true);
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);
});
/**
* @ngdoc directive
* @name notBlank
* @element ng-model
* @description Ensure that the value is not blank
*/
.directive('notBlank', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
if (viewValue.length) {
// it is valid
ctrl.$setValidity('notBlank', true);
return viewValue;
}
// 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
var pwElement = $('#'+scope.pw.$name);
pwElement.on('keyup change', passwordCheck);
element.on('keyup change', passwordCheck);
/**
* @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) {
} // end of link
}; // end of return
}); // end of directive
// helper function to check that password matches
function passwordCheck() {
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
})();

View File

@ -1,22 +1,20 @@
(function() {
(function () {
'use strict';
describe('horizon.framework.util.validators module', function() {
it('should have been defined', function() {
describe('horizon.framework.util.validators module', function () {
it('should have been defined', function () {
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.util.validators'));
describe('validateNumberMax directive', function() {
describe('validateNumberMax directive', function () {
var $scope, $form;
beforeEach(inject(function($injector) {
beforeEach(inject(function ($injector) {
var $compile = $injector.get('$compile');
$scope = $injector.get('$rootScope').$new();
@ -33,32 +31,30 @@
$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.$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);
$scope.$digest();
expect($form.count.$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('');
$scope.$digest();
expect($form.count.$valid).toBe(true);
expect($form.$valid).toBe(true);
});
});
describe('validateNumberMin directive', function() {
describe('validateNumberMin directive', function () {
var $scope, $form;
beforeEach(inject(function($injector) {
beforeEach(inject(function ($injector) {
var $compile = $injector.get('$compile');
$scope = $injector.get('$rootScope').$new();
@ -75,12 +71,12 @@
$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.$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);
$scope.$digest();
expect($scope.count).toBe(2);
@ -88,16 +84,15 @@
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('');
$scope.$digest();
expect($form.count.$valid).toBe(true);
expect($form.$valid).toBe(true);
});
});
describe('hzPasswordMatch directive', function() {
describe('hzPasswordMatch directive', function () {
var $compile, $rootScope;
var element, password, cpassword;
@ -108,7 +103,7 @@
'hz-password-match="form.password">' +
'</form>';
beforeEach(inject(function($injector){
beforeEach(inject(function ($injector) {
$compile = $injector.get('$compile');
$rootScope = $injector.get('$rootScope').$new();
@ -122,60 +117,57 @@
$rootScope.$digest();
}));
it('should be initially empty', function() {
it('should be initially empty', function () {
expect(password.val()).toEqual('');
expect(password.val()).toEqual(cpassword.val());
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.$digest();
cpassword.change();
setTimeout(function(){
setTimeout(function () {
expect(cpassword.val()).not.toEqual(password.val());
expect(cpassword.hasClass('ng-invalid')).toBe(true);
done();
}, 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.$digest();
cpassword.change();
setTimeout(function(){
setTimeout(function () {
expect(cpassword.val()).not.toEqual(password.val());
expect(cpassword.hasClass('ng-invalid')).toBe(true);
done();
}, 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.cpassword = 'password';
$rootScope.$digest();
cpassword.change();
setTimeout(function(){
setTimeout(function () {
expect(cpassword.val()).toEqual(password.val());
expect(cpassword.hasClass('ng-valid')).toBe(true);
done();
}, 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.cpassword = 'password345';
$rootScope.$digest();
cpassword.change();
setTimeout(function(){
setTimeout(function () {
expect(cpassword.val()).not.toEqual(password.val());
expect(cpassword.hasClass('ng-invalid')).toBe(true);
done();
}, 1000);
});
}); // end of hzPasswordMatch directive
});
})();
})();

View File

@ -97,5 +97,4 @@
};
}
]);
})();

View File

@ -8,18 +8,16 @@
});
describe('workflow factory', function () {
var workflow,
spec,
decorators = [
function (spec) {
angular.forEach(spec.steps, function (step) {
if (step.requireSomeServices) {
step.checkReadiness = function () {};
}
});
var workflow, spec;
var decorators = [
function (spec) {
angular.forEach(spec.steps, function (step) {
if (step.requireSomeServices) {
step.checkReadiness = function () {};
}
];
});
}
];
beforeEach(module('horizon.framework.util.workflow'));
@ -55,5 +53,4 @@
expect(angular.isFunction(steps[2].checkReadiness)).toBe(true);
});
});
})();

View File

@ -1,5 +1,5 @@
(function () {
'use strict';
'use strict';
angular.module('horizon.framework.widgets', [
'horizon.framework.widgets.help-panel',