diff --git a/src/cloudinary-image.component.spec.ts b/src/cloudinary-image.component.spec.ts
index f7780d49..5a8dcbbe 100644
--- a/src/cloudinary-image.component.spec.ts
+++ b/src/cloudinary-image.component.spec.ts
@@ -33,6 +33,7 @@ describe('CloudinaryImage', () => {
}).createComponent(TestComponent);
fixture.detectChanges(); // initial binding
+ expect(localCloudinary.responsive).toHaveBeenCalled();
// all elements with an attached CloudinaryImage
des = fixture.debugElement.query(By.directive(CloudinaryImage));
@@ -218,5 +219,41 @@ describe('CloudinaryImage', () => {
expect(img.src).toEqual(jasmine.stringMatching(/image\/upload\/updatedId/));
});
});
-});
+ describe('responsive images with nested transformations using the cld-responsive attribute', () => {
+ @Component({
+ template: `
+
+
+
+ `
+ })
+ class TestComponent { }
+
+ let fixture: ComponentFixture;
+ let des: DebugElement; // the elements w/ the directive
+
+ beforeEach(() => {
+ fixture = TestBed.configureTestingModule({
+ declarations: [CloudinaryTransformationDirective, CloudinaryImage, TestComponent],
+ providers: [{ provide: Cloudinary, useValue: localCloudinary }]
+ }).createComponent(TestComponent);
+
+ fixture.detectChanges(); // initial binding
+ expect(localCloudinary.responsive).toHaveBeenCalled();
+
+ // all elements with an attached CloudinaryImage
+ des = fixture.debugElement.query(By.directive(CloudinaryImage));
+ });
+
+ it('creates an img element which encodes the directive attributes to the URL', () => {
+ const img = des.children[0].nativeElement as HTMLImageElement;
+ expect(img.src).toEqual(jasmine.stringMatching
+ (/c_scale,l_text:roboto_25_bold:SDK,w_300\/e_art:hokusai\/f_auto\/responsive_sample.jpg/));
+ console.log('img', img);
+ expect(img.attributes.getNamedItem('data-src').value).toEqual(jasmine.stringMatching(
+ /c_scale,l_text:roboto_25_bold:SDK,w_300\/e_art:hokusai\/f_auto\/responsive_sample.jpg/));
+ });
+ });
+
+});
diff --git a/src/cloudinary-image.component.ts b/src/cloudinary-image.component.ts
index 4b230cb8..997c4bc4 100644
--- a/src/cloudinary-image.component.ts
+++ b/src/cloudinary-image.component.ts
@@ -64,7 +64,6 @@ export class CloudinaryImage implements AfterViewInit, OnInit, OnChanges, OnDest
const nativeElement = this.el.nativeElement;
const image = nativeElement.children[0];
const options = this.cloudinary.toCloudinaryAttributes(nativeElement.attributes, this.transformations);
-
const imageTag = this.cloudinary.imageTag(this.publicId, options);
this.setElementAttributes(image, imageTag.attributes());
if (options.responsive) {
diff --git a/src/cloudinary.service.spec.ts b/src/cloudinary.service.spec.ts
index cd49b568..67414b67 100644
--- a/src/cloudinary.service.spec.ts
+++ b/src/cloudinary.service.spec.ts
@@ -5,7 +5,7 @@ import {
Cloudinary,
isJsonLikeString,
isNamedNodeMap,
- transformKeyNamesFromKebabToSnakeCase
+ transformKeyNames
} from './cloudinary.service';
import CloudinaryConfiguration from './cloudinary-configuration.class';
@@ -70,9 +70,9 @@ describe('Cloudinary service', () => {
});
});
- describe('transformKeyNamesFromKebabToSnakeCase', () => {
+ describe('transformKeyNames', () => {
it('Transforms property names of json-like strings from kebab-case to snake_case', () => {
- expect(transformKeyNamesFromKebabToSnakeCase('{"aaa-aaa":"1", "bbb-bbb":"2", "cc": "ccc-ccc"}')).toEqual(
+ expect(transformKeyNames('{"aaa-aaa":"1", "bbb-bbb":"2", "cc": "ccc-ccc"}')).toEqual(
{
aaa_aaa: '1',
bbb_bbb: '2',
@@ -81,7 +81,7 @@ describe('Cloudinary service', () => {
);
});
it('Transforms property names of json-like strings spanning multi-lines from kebab-case to snake_case', () => {
- expect(transformKeyNamesFromKebabToSnakeCase(`{"aaa-aaa":"1",
+ expect(transformKeyNames(`{"aaa-aaa":"1",
"bbb-bbb":"2",
"cc": "ccc-ccc"}`)).toEqual(
{
@@ -92,7 +92,7 @@ describe('Cloudinary service', () => {
);
});
it('Transforms property names of objects from kebab-case to snake_case', () => {
- expect(transformKeyNamesFromKebabToSnakeCase({'aaa-aaa': 1, 'bbb-bbb': 2, cc: 'ccc-ccc'})).toEqual(
+ expect(transformKeyNames({'aaa-aaa': 1, 'bbb-bbb': 2, cc: 'ccc-ccc'})).toEqual(
{
aaa_aaa: 1,
bbb_bbb: 2,
@@ -100,14 +100,24 @@ describe('Cloudinary service', () => {
}
);
});
+ it('Transforms property names by stripping cld prefix', () => {
+ // "cld" prefix can be followed by an optional dash or underscore
+ expect(transformKeyNames('{"cldResponsive":"1", "cld-width":"2", "cld_height": "ccc-ccc"}')).toEqual(
+ {
+ responsive: '1',
+ width: '2',
+ height: 'ccc-ccc'
+ }
+ );
+ });
it('does not affect primitive values', () => {
- expect(transformKeyNamesFromKebabToSnakeCase(123)).toEqual(123);
- expect(transformKeyNamesFromKebabToSnakeCase(undefined)).toBeUndefined();
- expect(transformKeyNamesFromKebabToSnakeCase('')).toEqual('');
- expect(transformKeyNamesFromKebabToSnakeCase('a b c')).toEqual('a b c');
+ expect(transformKeyNames(123)).toEqual(123);
+ expect(transformKeyNames(undefined)).toBeUndefined();
+ expect(transformKeyNames('')).toEqual('');
+ expect(transformKeyNames('a b c')).toEqual('a b c');
});
it('iterates over array elements to transform its members', () => {
- expect(transformKeyNamesFromKebabToSnakeCase([{
+ expect(transformKeyNames([{
'aaa-aaa': 'aaa-aaa',
'bbb-bbb': 'bbb-bbb',
'ccc': 'ccc'
@@ -125,7 +135,7 @@ describe('Cloudinary service', () => {
]);
});
it('transforms complex json-like objects into options', () => {
- expect(transformKeyNamesFromKebabToSnakeCase(`{"aaa-aaa":"1",
+ expect(transformKeyNames(`{"aaa-aaa":"1",
"bbb-bbb":"2",
"transform-ation": [{ "effect": "sepia", "fetch_format": "auto"}]
}`)).toEqual(
diff --git a/src/cloudinary.service.ts b/src/cloudinary.service.ts
index 73692179..186bb35b 100644
--- a/src/cloudinary.service.ts
+++ b/src/cloudinary.service.ts
@@ -29,7 +29,7 @@ const namedNodeMapToObject = function (source: NamedNodeMap): any {
return target;
};
-const transformKeyNamesFromKebabToSnakeCase = function (obj: any): any {
+const transformKeyNames = function (obj: any): any {
let _obj = obj;
if (isJsonLikeString(obj)) {
// Given attribute value is in the form of a JSON object -
@@ -42,13 +42,14 @@ const transformKeyNamesFromKebabToSnakeCase = function (obj: any): any {
if (Array.isArray(_obj)) {
// Transform all the array values (e.g. transformation array)
_obj = _obj.map(currentValue => {
- return transformKeyNamesFromKebabToSnakeCase(currentValue);
+ return transformKeyNames(currentValue);
});
} else if (typeof _obj === 'object') {
Object.keys(_obj).forEach(key => {
// Replace the key name with the snake_case
- const kebabKey = key.replace(/-/g, '_').toLocaleLowerCase();
- const kebabValue = transformKeyNamesFromKebabToSnakeCase(_obj[key]);
+ // Then strip cld prefix if it exists (with an optional dash or underscore)
+ const kebabKey = key.replace(/-/g, '_').toLocaleLowerCase().replace(/^cld(-|_)?/, '');
+ const kebabValue = transformKeyNames(_obj[key]);
delete _obj[key];
_obj[kebabKey] = kebabValue;
});
@@ -104,7 +105,7 @@ export class Cloudinary {
*/
toCloudinaryAttributes(attributes: NamedNodeMap,
childTransformations?: QueryList): any {
- const options = transformKeyNamesFromKebabToSnakeCase(attributes);
+ const options = transformKeyNames(attributes);
// Add chained transformations
if (childTransformations && childTransformations.length > 0) {
@@ -126,8 +127,8 @@ export class Cloudinary {
/* Return a provider object that creates our configurable service */
export function provideCloudinary(cloudinaryJsLib: any, configuration: CloudinaryConfiguration) {
- return { provide: Cloudinary, useFactory: () => new Cloudinary(cloudinaryJsLib, configuration) };
+ return { provide: Cloudinary, useFactory: () => new Cloudinary(cloudinaryJsLib, configuration) };
};
// For unit tests
-export { isJsonLikeString, isNamedNodeMap, transformKeyNamesFromKebabToSnakeCase, namedNodeMapToObject };
+export { isJsonLikeString, isNamedNodeMap, transformKeyNames, namedNodeMapToObject };