From edb1c95d31dfcec09b789143fc4c0fb5e06108c4 Mon Sep 17 00:00:00 2001 From: Ciro Nunes Date: Fri, 5 Dec 2014 09:57:05 -0200 Subject: [PATCH 1/3] feat(Angular): add camelcase as a public method convert dash-separated strings to camelCase --- src/.jshintrc | 1 + src/Angular.js | 21 +++++++++++++++++++++ src/AngularPublic.js | 1 + src/jqLite.js | 2 -- test/.jshintrc | 1 + test/AngularSpec.js | 34 +++++++++++++++++++++++++++++++++- 6 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/.jshintrc b/src/.jshintrc index 9500d330d7ab..dd5755a60896 100644 --- a/src/.jshintrc +++ b/src/.jshintrc @@ -26,6 +26,7 @@ "uppercase": false, "manualLowercase": false, "manualUppercase": false, + "camelcase": false, "isArrayLike": false, "forEach": false, "sortedKeys": false, diff --git a/src/Angular.js b/src/Angular.js index 09d7c4619b8d..68926a1758f5 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -19,6 +19,7 @@ uppercase: true, manualLowercase: true, manualUppercase: true, + camelcase: true, nodeName_: true, isArrayLike: true, forEach: true, @@ -108,6 +109,8 @@ */ var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/; +var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; +var MOZ_HACK_REGEXP = /^moz([A-Z])/; // The name of a form control's ValidityState property. // This is used so that it's possible for internal tests to create mock ValidityStates. @@ -161,6 +164,24 @@ if ('i' !== 'I'.toLowerCase()) { uppercase = manualUppercase; } +/** + * @ngdoc function + * @name angular.camelcase + * @module ng + * @kind function + * + * @description Converts the specified string dash-separated to camelcase. + * @param {string} string String to be converted to camelcase. + * @returns {string} Camelcased string. + */ +var camelcase = function(name) { + return name. + replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }). + replace(MOZ_HACK_REGEXP, 'Moz$1'); +}; + var msie, // holds major version number for IE, or NaN if UA is not IE. diff --git a/src/AngularPublic.js b/src/AngularPublic.js index b81257b9fff7..4902120854b9 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -137,6 +137,7 @@ function publishExternalAPI(angular) { 'isDate': isDate, 'lowercase': lowercase, 'uppercase': uppercase, + 'camelcase': camelcase, 'callbacks': {counter: 0}, 'getTestability': getTestability, '$$minErr': minErr, diff --git a/src/jqLite.js b/src/jqLite.js index 1b7f638c2bb6..9bdc68abe0ae 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -122,8 +122,6 @@ JQLite._data = function(node) { function jqNextId() { return ++jqId; } -var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; -var MOZ_HACK_REGEXP = /^moz([A-Z])/; var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"}; var jqLiteMinErr = minErr('jqLite'); diff --git a/test/.jshintrc b/test/.jshintrc index 6bca8382f2d9..993f224e922d 100644 --- a/test/.jshintrc +++ b/test/.jshintrc @@ -24,6 +24,7 @@ "uppercase": false, "manualLowercase": false, "manualUppercase": false, + "camelcase": false, "isArrayLike": false, "forEach": false, "sortedKeys": false, diff --git a/test/AngularSpec.js b/test/AngularSpec.js index e63789100947..9138a9cb04dc 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -7,15 +7,47 @@ describe('angular', function() { dealoc(element); }); - describe('case', function() { + describe('lowercase', function() { it('should change case', function() { expect(lowercase('ABC90')).toEqual('abc90'); expect(manualLowercase('ABC90')).toEqual('abc90'); + }); + }); + + describe('uppercase', function() { + it('should change case', function() { expect(uppercase('abc90')).toEqual('ABC90'); expect(manualUppercase('abc90')).toEqual('ABC90'); }); }); + describe('camelcase', function() { + it('should change case', function() { + expect(camelcase('foo-bar')).toEqual('fooBar'); + expect(camelcase('foo-Bar')).toEqual('fooBar'); + }); + + it('should leave non-dashed strings alone', function() { + expect(camelcase('foo')).toBe('foo'); + expect(camelcase('')).toBe(''); + expect(camelcase('fooBar')).toBe('fooBar'); + }); + + + it('should covert dash-separated strings to camelCase', function() { + expect(camelcase('foo-bar')).toBe('fooBar'); + expect(camelcase('foo-bar-baz')).toBe('fooBarBaz'); + expect(camelcase('foo:bar_baz')).toBe('fooBarBaz'); + }); + + + it('should covert browser specific css properties', function() { + expect(camelcase('-moz-foo-bar')).toBe('MozFooBar'); + expect(camelcase('-webkit-foo-bar')).toBe('webkitFooBar'); + expect(camelcase('-webkit-foo-bar')).toBe('webkitFooBar'); + }); + }); + describe("copy", function() { it("should return same object", function() { var obj = {}; From 3ee771a8a1803036a059f37b3737b274e6b22dae Mon Sep 17 00:00:00 2001 From: Ciro Nunes Date: Fri, 5 Dec 2014 09:59:17 -0200 Subject: [PATCH 2/3] refactor(*): DRY camelcase method remove the camelCase function from jqLite and use the one from the public API --- src/jqLite.js | 15 +-------------- src/ng/compile.js | 2 +- src/ng/sce.js | 6 +++--- test/jqLiteSpec.js | 24 ------------------------ 4 files changed, 5 insertions(+), 42 deletions(-) diff --git a/src/jqLite.js b/src/jqLite.js index 9bdc68abe0ae..b542e9b06267 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -125,19 +125,6 @@ function jqNextId() { return ++jqId; } var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"}; var jqLiteMinErr = minErr('jqLite'); -/** - * Converts snake_case to camelCase. - * Also there is special case for Moz prefix starting with upper case letter. - * @param name Name to normalize - */ -function camelCase(name) { - return name. - replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { - return offset ? letter.toUpperCase() : letter; - }). - replace(MOZ_HACK_REGEXP, 'Moz$1'); -} - var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/; var HTML_REGEXP = /<|&#?\w+;/; var TAG_NAME_REGEXP = /<([\w:]+)/; @@ -575,7 +562,7 @@ forEach({ hasClass: jqLiteHasClass, css: function(element, name, value) { - name = camelCase(name); + name = camelcase(name); if (isDefined(value)) { element.style[name] = value; diff --git a/src/ng/compile.js b/src/ng/compile.js index 4d89e8795e2d..37847026146d 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -2495,7 +2495,7 @@ var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i; * @param name Name to normalize */ function directiveNormalize(name) { - return camelCase(name.replace(PREFIX_REGEXP, '')); + return camelcase(name.replace(PREFIX_REGEXP, '')); } /** diff --git a/src/ng/sce.js b/src/ng/sce.js index 67be4997fb8f..c5e7524d9ad8 100644 --- a/src/ng/sce.js +++ b/src/ng/sce.js @@ -1033,13 +1033,13 @@ function $SceProvider() { forEach(SCE_CONTEXTS, function(enumValue, name) { var lName = lowercase(name); - sce[camelCase("parse_as_" + lName)] = function(expr) { + sce[camelcase("parse_as_" + lName)] = function(expr) { return parse(enumValue, expr); }; - sce[camelCase("get_trusted_" + lName)] = function(value) { + sce[camelcase("get_trusted_" + lName)] = function(value) { return getTrusted(enumValue, value); }; - sce[camelCase("trust_as_" + lName)] = function(value) { + sce[camelcase("trust_as_" + lName)] = function(value) { return trustAs(enumValue, value); }; }); diff --git a/test/jqLiteSpec.js b/test/jqLiteSpec.js index 29848825a41a..764de76b34da 100644 --- a/test/jqLiteSpec.js +++ b/test/jqLiteSpec.js @@ -1965,30 +1965,6 @@ describe('jqLite', function() { }); }); - - describe('camelCase', function() { - it('should leave non-dashed strings alone', function() { - expect(camelCase('foo')).toBe('foo'); - expect(camelCase('')).toBe(''); - expect(camelCase('fooBar')).toBe('fooBar'); - }); - - - it('should covert dash-separated strings to camelCase', function() { - expect(camelCase('foo-bar')).toBe('fooBar'); - expect(camelCase('foo-bar-baz')).toBe('fooBarBaz'); - expect(camelCase('foo:bar_baz')).toBe('fooBarBaz'); - }); - - - it('should covert browser specific css properties', function() { - expect(camelCase('-moz-foo-bar')).toBe('MozFooBar'); - expect(camelCase('-webkit-foo-bar')).toBe('webkitFooBar'); - expect(camelCase('-webkit-foo-bar')).toBe('webkitFooBar'); - }); - }); - - describe('jqLiteDocumentLoaded', function() { function createMockWindow(readyState) { From 28728a9351f89105100340270b1410817c62013a Mon Sep 17 00:00:00 2001 From: Ciro Nunes Date: Fri, 5 Dec 2014 10:00:34 -0200 Subject: [PATCH 3/3] refactor(ngAria): DRY camelcase method remove the camelCase function and use the one from the public API --- src/ngAria/aria.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/ngAria/aria.js b/src/ngAria/aria.js index cc90170989f4..343a402c9af1 100644 --- a/src/ngAria/aria.js +++ b/src/ngAria/aria.js @@ -109,15 +109,8 @@ function $AriaProvider() { config = angular.extend(config, newConfig); }; - function camelCase(input) { - return input.replace(/-./g, function(letter, pos) { - return letter[1].toUpperCase(); - }); - } - - function watchExpr(attrName, ariaAttr, negate) { - var ariaCamelName = camelCase(ariaAttr); + var ariaCamelName = angular.camelcase(ariaAttr); return function(scope, elem, attr) { if (config[ariaCamelName] && !attr[ariaCamelName]) { scope.$watch(attr[attrName], function(boolVal) { @@ -178,7 +171,7 @@ function $AriaProvider() { this.$get = function() { return { config: function(key) { - return config[camelCase(key)]; + return config[angular.camelcase(key)]; }, $$watchExpr: watchExpr };