@capillarytech/creatives-library 7.12.108 → 7.12.109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/package.json +4 -2
  2. package/routes.js +1 -0
  3. package/services/api.js +6 -1
  4. package/utils/cdnTransformation.js +316 -0
  5. package/utils/tests/__snapshots__/cdnTransformation.test.js.snap +298 -0
  6. package/utils/tests/cdnTransformation.mockdata.js +301 -0
  7. package/utils/tests/cdnTransformation.test.js +245 -0
  8. package/v2Containers/Cap/actions.js +0 -1
  9. package/v2Containers/Cap/constants.js +0 -1
  10. package/v2Containers/Cap/sagas.js +3 -0
  11. package/v2Containers/CreativesContainer/actions.js +2 -1
  12. package/v2Containers/CreativesContainer/constants.js +1 -1
  13. package/v2Containers/CreativesContainer/index.js +6 -1
  14. package/v2Containers/CreativesContainer/tests/index.test.js +4 -0
  15. package/v2Containers/Email/index.js +4 -1
  16. package/v2Containers/Facebook/Advertisement/index.js +4 -4
  17. package/v2Containers/Line/Container/Image/index.js +5 -4
  18. package/v2Containers/Line/Container/ImageCarousel/index.js +6 -5
  19. package/v2Containers/Line/Container/ImageMap/index.js +2 -1
  20. package/v2Containers/MobilePush/Create/index.js +5 -7
  21. package/v2Containers/MobilePush/Edit/index.js +4 -3
  22. package/v2Containers/Rcs/index.js +2 -1
  23. package/v2Containers/Templates/actions.js +6 -0
  24. package/v2Containers/Templates/constants.js +4 -0
  25. package/v2Containers/Templates/sagas.js +23 -0
  26. package/v2Containers/TemplatesV2/index.js +5 -0
  27. package/v2Containers/Viber/index.js +11 -2
  28. package/v2Containers/WeChat/RichmediaTemplates/Create/index.js +3 -1
  29. package/v2Containers/Whatsapp/index.js +4 -2
  30. package/tests/integration/CreateTemplate/CreateTemplate.integration.test.js +0 -18
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/creatives-library",
3
3
  "author": "meharaj",
4
- "version": "7.12.108",
4
+ "version": "7.12.109",
5
5
  "description": "Capillary creatives ui",
6
6
  "main": "./index.js",
7
7
  "module": "./index.es.js",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "license": "LicenseRef-LICENSE",
14
14
  "dependencies": {
15
- "@babel/polyfill": "^7.4.3",
15
+ "@babel/polyfill": "7.4.3",
16
16
  "@bugsnag/js": "^7.2.1",
17
17
  "@bugsnag/plugin-react": "^7.2.1",
18
18
  "@capillarytech/cap-ui-utils": "1.4.2",
@@ -24,8 +24,10 @@
24
24
  "jest-date-mock": "^1.0.8",
25
25
  "jquery": "^3.3.1",
26
26
  "load-script": "^1.0.0",
27
+ "node-html-parser": "^5.4.2-0",
27
28
  "normalizr": "^3.2.3",
28
29
  "papaparse": "^5.3.1",
30
+ "pre-push": "^0.1.2",
29
31
  "react-datepicker": "^0.46.0",
30
32
  "react-dropzone": "^11.3.4",
31
33
  "react-phone-input-2": "^2.15.0",
package/routes.js CHANGED
@@ -13,6 +13,7 @@ import * as emailSagas from 'containers/Email/sagas';
13
13
  import * as templateSagas from 'containers/Templates/sagas';
14
14
  import * as lineSagas from 'containers/Line/Create/sagas';
15
15
  import * as languageSaga from './v2Containers/LanguageProvider/sagas';
16
+
16
17
  import pathConfig from './config/path';
17
18
 
18
19
  import {updateCharCount} from './utils/smsCharCountV2';
package/services/api.js CHANGED
@@ -484,4 +484,9 @@ export const createWhatsappTemplate = ({ payload }) => {
484
484
  export const getSenderDetails = (channel, orgUnitId) => {
485
485
  const url = `${CAMPAIGNS_API_ORG_ENDPOINT}/meta/domainProperties?channel=${channel}`;
486
486
  return request(url, getAPICallObject('GET', null, false, true, orgUnitId));
487
- };
487
+ };
488
+
489
+ export const getCdnTransformationConfig = () => {
490
+ const url = `${API_ENDPOINT}/common/getCdnTransformationConfig`;
491
+ return request(url, getAPICallObject('GET'));
492
+ }
@@ -0,0 +1,316 @@
1
+ import cloneDeep from "lodash/cloneDeep";
2
+ import isEmpty from "lodash/isEmpty";
3
+ import forOwn from "lodash/forOwn";
4
+ import isNumber from "lodash/isNumber";
5
+ /* eslint-disable no-unused-expressions */
6
+ import { parse } from "node-html-parser";
7
+
8
+ // https://storage.crm.n.content-cdn.io/cdn-cgi/image/width=100,height=100,quality=75,fit=cover,g=top,format=auto/intouch_creative_assets/005710a7-6412-44fe-a19f-e72456b1.jpg
9
+
10
+ // const regex = /https:\/\/.*intouch_creative_assets.*(?:jpg|png|jpeg|gif)$/;
11
+ const regex = /https:\/\/.*intouch_creative_assets.*$/;
12
+ const INTOUCH_CREATIVE_ASSETS = "intouch_creative_assets";
13
+ const SPECIFIED = "specified";
14
+ const FORMAT_TYPES = { AUTO: "auto" };
15
+ const QUALITY_TYPE = {
16
+ DECREASE: "decrease",
17
+ PRESERVE: "preserve",
18
+ };
19
+
20
+ const CHANNEL_CONFIGS = {
21
+ EMAIL: {
22
+ transformations: {
23
+ width: SPECIFIED,
24
+ height: SPECIFIED,
25
+ format: FORMAT_TYPES.AUTO,
26
+ quality: QUALITY_TYPE.DECREASE,
27
+ },
28
+ },
29
+ RCS: {
30
+ transformations: {
31
+ width: 1440,
32
+ height: 720,
33
+ quality: QUALITY_TYPE.DECREASE,
34
+ },
35
+ },
36
+ VIBER: {
37
+ transformations: {
38
+ width: 300,
39
+ height: 400,
40
+ quality: QUALITY_TYPE.DECREASE,
41
+ },
42
+ },
43
+ WHATSAPP: {
44
+ transformations: {
45
+ quality: QUALITY_TYPE.DECREASE, //TODO whatsapp itself might reduce quality. To be checked
46
+ },
47
+ },
48
+ MOBILE_PUSH: {
49
+ transformations: {
50
+ quality: QUALITY_TYPE.DECREASE,
51
+ },
52
+ },
53
+ FACEBOOK: {
54
+ IMAGE: {
55
+ transformations: {
56
+ width: 1080,
57
+ height: 1080,
58
+ quality: QUALITY_TYPE.DECREASE,
59
+ },
60
+ }
61
+ },
62
+ LINE: {
63
+ IMAGE: {
64
+ transformations: {
65
+ width: 300,
66
+ height: 400,
67
+ quality: QUALITY_TYPE.DECREASE,
68
+ },
69
+ },
70
+ CARD: {
71
+ transformations: {
72
+ width: 1024,
73
+ height: 1024,
74
+ quality: QUALITY_TYPE.DECREASE,
75
+ },
76
+ },
77
+ RICH_MESSAGE: {
78
+ //Rich Message in Line has Square and Custom templates.
79
+ //Width (1040px) is same in both whereas height varies. Hence including width only.
80
+ transformations: {
81
+ width: 1040,
82
+ quality: QUALITY_TYPE.DECREASE,
83
+ },
84
+ }
85
+ },
86
+ WECHAT: {
87
+ transformations: {
88
+ width: 360,
89
+ height: 200,
90
+ quality: QUALITY_TYPE.DECREASE,
91
+
92
+ }
93
+ }
94
+ };
95
+
96
+ const CREATIVES_CDN_BASE_URL_KEY = 'CREATIVES_CDN_BASE_URL';
97
+ const CREATIVES_CDN_QUALITY_CONFIG_KEY = 'CREATIVES_CDN_QUALITY_CONFIG';
98
+ const CREATIVES_CDN_TRANSFORMATION_URL_SUFFIX_KEY = 'CREATIVES_CDN_TRANSFORMATION_URL_SUFFIX';
99
+ /**
100
+ * getCdnUrl : utility function to replace and return s3/cdn url with cdn url.
101
+ */
102
+ export const getCdnUrl = ({
103
+ url,
104
+ height,
105
+ width,
106
+ channelName: receivedChannelName,
107
+ channelSubType: receivedChannelSubType,
108
+ }) => {
109
+ try{
110
+ const channelName = receivedChannelName?.toUpperCase();
111
+ const channelSubType = receivedChannelSubType?.toUpperCase();
112
+
113
+ const CREATIVES_CDN_BASE_URL = getLocalStorageItem(CREATIVES_CDN_BASE_URL_KEY);
114
+ const CREATIVES_CDN_QUALITY_CONFIG = getLocalStorageItem(CREATIVES_CDN_QUALITY_CONFIG_KEY, true);
115
+ const CREATIVES_CDN_TRANSFORMATION_URL_SUFFIX = getLocalStorageItem(CREATIVES_CDN_TRANSFORMATION_URL_SUFFIX_KEY);
116
+
117
+ //Basic validations on data stored in local storage.
118
+ if (
119
+ typeof CREATIVES_CDN_QUALITY_CONFIG !== "object" ||
120
+ Array.isArray(CREATIVES_CDN_QUALITY_CONFIG) ||
121
+ CREATIVES_CDN_QUALITY_CONFIG === null ||
122
+ typeof CREATIVES_CDN_BASE_URL !== "string" ||
123
+ typeof CREATIVES_CDN_TRANSFORMATION_URL_SUFFIX !== "string"
124
+ )
125
+ return url;
126
+
127
+ const QUALITY = CREATIVES_CDN_QUALITY_CONFIG?.[channelName] || CREATIVES_CDN_QUALITY_CONFIG?.DEFAULT;
128
+
129
+ if (
130
+ !regex.test(url) ||
131
+ !CHANNEL_CONFIGS?.[channelName] ||
132
+ (channelSubType && !CHANNEL_CONFIGS?.[channelName]?.[channelSubType]) ||
133
+ !CREATIVES_CDN_BASE_URL ||
134
+ !CREATIVES_CDN_TRANSFORMATION_URL_SUFFIX ||
135
+ !QUALITY
136
+ )
137
+ return url;
138
+
139
+ const [, assetKey] = url.split(INTOUCH_CREATIVE_ASSETS);
140
+
141
+ let newUrl = `${CREATIVES_CDN_BASE_URL}`;
142
+
143
+ let applicableTransformations;
144
+
145
+ if (channelSubType) {
146
+ applicableTransformations =
147
+ CHANNEL_CONFIGS?.[channelName]?.[channelSubType]?.transformations;
148
+ } else {
149
+ applicableTransformations = CHANNEL_CONFIGS?.[channelName]?.transformations;
150
+ }
151
+
152
+ if (!isEmpty(applicableTransformations)) {
153
+
154
+ newUrl += `/${CREATIVES_CDN_TRANSFORMATION_URL_SUFFIX}/`;
155
+
156
+ // Object.keys(applicableTransformations)?.forEach((transformationParam) => {
157
+ forOwn(applicableTransformations, (transformationParamVal, transformationParam)=>{
158
+ try {
159
+ switch (transformationParam) {
160
+ case "height":
161
+ if (transformationParamVal === SPECIFIED && height) {
162
+ newUrl += `${transformationParam}=${height},`;
163
+ } else if(transformationParamVal !== SPECIFIED && isNumber(transformationParamVal)) {
164
+ newUrl += `${transformationParam}=${transformationParamVal},`;
165
+ }
166
+ break;
167
+ case "width":
168
+ if (transformationParamVal === SPECIFIED && width) {
169
+ newUrl += `${transformationParam}=${width},`;
170
+ } else if(transformationParamVal !== SPECIFIED && isNumber(transformationParamVal)) {
171
+ newUrl += `${transformationParam}=${transformationParamVal},`;
172
+ }
173
+ break;
174
+ case "format":
175
+ if(transformationParamVal){
176
+ newUrl += `${transformationParam}=${transformationParamVal},`;
177
+ }
178
+ break;
179
+ case "quality":
180
+ if (transformationParamVal === QUALITY_TYPE?.DECREASE && QUALITY && typeof QUALITY === "number") {
181
+ newUrl += `${transformationParam}=${QUALITY},`;
182
+ } else {
183
+ newUrl += `${transformationParam}=100,`;
184
+ }
185
+ break;
186
+ default:
187
+ break;
188
+ }
189
+ } catch (e) {
190
+ console.log("some error occured while applying transformation",e);
191
+ }
192
+ })
193
+ }
194
+ newUrl += `/${INTOUCH_CREATIVE_ASSETS}${assetKey}`;
195
+ return newUrl;
196
+
197
+ }catch(e){
198
+ console.log('Some exception occurred in getCdnUrl',e);
199
+ return url;
200
+ }
201
+ };
202
+
203
+ /**
204
+ *
205
+ * @param {*} htmlContents
206
+ * @returns htmlContents with updated urls in image tag
207
+ */
208
+ export const updateImagesInHtml = (htmlContents) => {
209
+ const copiedHtmlContents = htmlContents;
210
+ try {
211
+ const root = parse(htmlContents);
212
+ root.querySelectorAll("img").forEach((element) => {
213
+ const imageSrc = element.getAttribute("src");
214
+ const isAsset = regex.test(imageSrc);
215
+ if (isAsset) {
216
+ const height = element.getAttribute("height");
217
+ const width = element.getAttribute("width");
218
+ const newUrl = getCdnUrl({
219
+ url: imageSrc,
220
+ height,
221
+ width,
222
+ channelName: "EMAIL",
223
+ });
224
+ element.setAttribute("src", newUrl);
225
+ }
226
+ });
227
+ return root.toString();
228
+ } catch (e) {
229
+ console.log("An error occured while updating images in html", e);
230
+ return copiedHtmlContents; //return received input directly in case an exception is thrown
231
+ }
232
+ };
233
+
234
+ /**
235
+ *
236
+ * @param {*} contents
237
+ * @returns
238
+ */
239
+ export const transformEmailTemplates = (contents) => {
240
+ const contentsCopy = cloneDeep(contents);
241
+ try {
242
+ const base = contentsCopy?.versions?.base;
243
+ const languages = base?.selectedLanguages;
244
+ languages?.forEach((lang) => {
245
+ const htmlContents = base?.[lang]?.["template-content"];
246
+ const newHtmlContents = updateImagesInHtml(htmlContents);
247
+ base[lang]["template-content"] = newHtmlContents;
248
+ });
249
+ return contentsCopy;
250
+ } catch (e) {
251
+ console.log("Some error has occured while transforming email templates", e);
252
+ return contents; //return received input directly in case an exception is thrown
253
+ }
254
+ };
255
+
256
+
257
+
258
+ export function getLocalStorageItem(key, parse = false){
259
+ const val = localStorage.getItem(key);
260
+ if(parse){
261
+ return JSON.parse(val);
262
+ }
263
+ return val;
264
+ }
265
+
266
+ export function removeLocalStorageItem(key) {
267
+ localStorage.removeItem(key);
268
+ return;
269
+ }
270
+
271
+ export function removeAllCdnLocalStorageItems() {
272
+ try{
273
+ removeLocalStorageItem(CREATIVES_CDN_BASE_URL_KEY);
274
+ removeLocalStorageItem(CREATIVES_CDN_QUALITY_CONFIG_KEY);
275
+ removeLocalStorageItem(CREATIVES_CDN_TRANSFORMATION_URL_SUFFIX_KEY);
276
+ }catch(e){
277
+ console.log('some error occured while deleting all keys for cdn', e);
278
+ }
279
+ }
280
+
281
+ export function saveCdnConfigs(configsResponse) {
282
+ try {
283
+ //If no response from api, then remove localStorage items if they were set previously
284
+ if (isEmpty(configsResponse)) {
285
+ removeAllCdnLocalStorageItems();
286
+ return;
287
+ }
288
+
289
+ if (configsResponse?.hostname) {
290
+ localStorage.setItem(
291
+ CREATIVES_CDN_BASE_URL_KEY,
292
+ configsResponse?.hostname
293
+ );
294
+ } else {
295
+ removeLocalStorageItem(CREATIVES_CDN_BASE_URL_KEY);
296
+ }
297
+
298
+ if (!isEmpty(configsResponse?.qualityCfg)) {
299
+ const qualityString = JSON.stringify(configsResponse?.qualityCfg);
300
+ localStorage.setItem(CREATIVES_CDN_QUALITY_CONFIG_KEY, qualityString);
301
+ } else {
302
+ removeLocalStorageItem(CREATIVES_CDN_QUALITY_CONFIG_KEY);
303
+ }
304
+
305
+ if (configsResponse?.transformationUrlSuffix) {
306
+ localStorage.setItem(
307
+ CREATIVES_CDN_TRANSFORMATION_URL_SUFFIX_KEY,
308
+ configsResponse?.transformationUrlSuffix
309
+ );
310
+ } else {
311
+ removeLocalStorageItem(CREATIVES_CDN_TRANSFORMATION_URL_SUFFIX_KEY);
312
+ }
313
+ } catch (e) {
314
+ console.log("some error while setting localstorage cdn configs");
315
+ }
316
+ }
@@ -0,0 +1,298 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`cdnTransformationTests transformEmailTemplate() should transform email template 1`] = `
4
+ Object {
5
+ "name": "test template1",
6
+ "type": "EMAIL",
7
+ "versions": Object {
8
+ "base": Object {
9
+ "activeTab": "en",
10
+ "base": true,
11
+ "en": Object {
12
+ "drag_drop_id": "",
13
+ "id": "5f7b61605f09ebe595ff404e",
14
+ "is_drag_drop": false,
15
+ "iso_code": "en",
16
+ "lang_id": 69,
17
+ "language": "English",
18
+ "tabKey": "6",
19
+ "template-content": "<!DOCTYPE html PUBLIC \\"-//W3C//DTD XHTML 1.0 Transitional //EN\\" \\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\\">
20
+ <html xmlns=\\"http://www.w3.org/1999/xhtml\\" xmlns:o=\\"urn:schemas-microsoft-com:office:office\\" xmlns:v=\\"urn:schemas-microsoft-com:vml\\">
21
+ <head><meta http-equiv=\\"Content-Type\\" content=\\"text/html; charset=utf-8\\"><meta name=\\"viewport\\" content=\\"width=device-width\\">
22
+ <title></title>
23
+
24
+ <style type=\\"text/css\\">body {
25
+ margin: 0;
26
+ padding: 0; }
27
+
28
+ table,
29
+ td,
30
+ tr {
31
+ vertical-align: top;
32
+ border-collapse: collapse; }
33
+
34
+ * {
35
+ line-height: inherit; }
36
+
37
+ a[x-apple-data-detectors=true] {
38
+ color: inherit !important;
39
+ text-decoration: none !important; }
40
+ </style>
41
+ <style id=\\"media-query\\" type=\\"text/css\\">@media (max-width: 520px) {
42
+ .block-grid,
43
+ .col {
44
+ min-width: 320px !important;
45
+ max-width: 100% !important;
46
+ display: block !important;
47
+ }
48
+
49
+ .block-grid {
50
+ width: 100% !important;
51
+ }
52
+
53
+ .col {
54
+ width: 100% !important;
55
+ }
56
+
57
+ .col > div {
58
+ margin: 0 auto;
59
+ }
60
+
61
+ img.fullwidth,
62
+ img.fullwidthOnMobile {
63
+ max-width: 100% !important;
64
+ }
65
+
66
+ .no-stack .col {
67
+ min-width: 0 !important;
68
+ display: table-cell !important;
69
+ }
70
+
71
+ .no-stack.two-up .col {
72
+ width: 50% !important;
73
+ }
74
+
75
+ .no-stack .col.num4 {
76
+ width: 33% !important;
77
+ }
78
+
79
+ .no-stack .col.num8 {
80
+ width: 66% !important;
81
+ }
82
+
83
+ .no-stack .col.num4 {
84
+ width: 33% !important;
85
+ }
86
+
87
+ .no-stack .col.num3 {
88
+ width: 25% !important;
89
+ }
90
+
91
+ .no-stack .col.num6 {
92
+ width: 50% !important;
93
+ }
94
+
95
+ .no-stack .col.num9 {
96
+ width: 75% !important;
97
+ }
98
+
99
+ .video-block {
100
+ max-width: none !important;
101
+ }
102
+
103
+ .mobile_hide {
104
+ min-height: 0px;
105
+ max-height: 0px;
106
+ max-width: 0px;
107
+ display: none;
108
+ overflow: hidden;
109
+ font-size: 0px;
110
+ }
111
+
112
+ .desktop_hide {
113
+ display: block !important;
114
+ max-height: none !important;
115
+ }
116
+ }
117
+ </style>
118
+ </head>
119
+ <body class=\\"clean-body\\" style=\\"margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: #FFFFFF;\\">
120
+ <p>hello world</p>
121
+
122
+ <p><img alt=\\"Capillary Image\\" height=\\"171\\" src=\\"https://storage.crm.n.content-cdn.io/cdn-cgi/image/width=257,height=171,format=auto,quality=75,/intouch_creative_assets/b4b63b22-8a81-41ee-a8df-6db967fc.jpg\\" width=\\"257\\"></p>
123
+
124
+ <table bgcolor=\\"#FFFFFF\\" cellpadding=\\"0\\" cellspacing=\\"0\\" class=\\"nl-container\\" role=\\"presentation\\" style=\\"background-color:#ffffff; border-collapse:collapse; border-spacing:0; margin:0 auto; min-width:320px; mso-table-lspace:0pt; mso-table-rspace:0pt; table-layout:fixed; vertical-align:top; width:100%\\" valign=\\"top\\">
125
+ <tbody>
126
+ <tr style=\\"vertical-align:top\\" valign=\\"top\\">
127
+ <td style=\\"vertical-align:top; word-break:break-word\\">
128
+ <div style=\\"background-color:transparent\\">
129
+ <div class=\\"block-grid\\" style=\\"background-color:transparent; margin-bottom:0; margin-left:auto; margin-right:auto; margin-top:0; max-width:500px; min-width:320px; overflow-wrap:break-word; word-break:break-word; word-wrap:break-word\\">
130
+ <div style=\\"background-color:transparent; border-collapse:collapse; display:table; width:100%\\">
131
+ <div class=\\"col num12\\" style=\\"display:table-cell; max-width:500px; min-width:320px; vertical-align:top; width:500px\\">
132
+ <div style=\\"width:100% !important\\"></div>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ </div>
137
+ </td>
138
+ </tr>
139
+ </tbody>
140
+ </table>
141
+ </body>
142
+ </html>
143
+ ",
144
+ },
145
+ "selectedLanguages": Array [
146
+ "en",
147
+ ],
148
+ "subject": "hello subject",
149
+ "tabKey": "6",
150
+ },
151
+ "history": Array [
152
+ Object {
153
+ "activeTab": "en",
154
+ "base": true,
155
+ "en": Object {
156
+ "drag_drop_id": "",
157
+ "id": "5f7b61605f09ebe595ff404e",
158
+ "is_drag_drop": false,
159
+ "iso_code": "en",
160
+ "lang_id": 69,
161
+ "language": "English",
162
+ "tabKey": "6",
163
+ "template-content": "<!DOCTYPE html PUBLIC \\"-//W3C//DTD XHTML 1.0 Transitional //EN\\" \\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\\">
164
+ <html xmlns=\\"http://www.w3.org/1999/xhtml\\" xmlns:o=\\"urn:schemas-microsoft-com:office:office\\" xmlns:v=\\"urn:schemas-microsoft-com:vml\\">
165
+ <head><!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]--><meta http-equiv=\\"Content-Type\\" content=\\"text/html; charset=utf-8\\"><meta name=\\"viewport\\" content=\\"width=device-width\\"><!--[if !mso]><meta http-equiv=\\"X-UA-Compatible\\" content=\\"IE=edge\\"><!--<![endif]-->
166
+ <title></title>
167
+ <!--[if !mso]><!--<![endif]-->
168
+ <style type=\\"text/css\\">body {
169
+ margin: 0;
170
+ padding: 0; }
171
+
172
+ table,
173
+ td,
174
+ tr {
175
+ vertical-align: top;
176
+ border-collapse: collapse; }
177
+
178
+ * {
179
+ line-height: inherit; }
180
+
181
+ a[x-apple-data-detectors=true] {
182
+ color: inherit !important;
183
+ text-decoration: none !important; }
184
+ </style>
185
+ <style id=\\"media-query\\" type=\\"text/css\\">@media (max-width: 520px) {
186
+ .block-grid,
187
+ .col {
188
+ min-width: 320px !important;
189
+ max-width: 100% !important;
190
+ display: block !important;
191
+ }
192
+
193
+ .block-grid {
194
+ width: 100% !important;
195
+ }
196
+
197
+ .col {
198
+ width: 100% !important;
199
+ }
200
+
201
+ .col > div {
202
+ margin: 0 auto;
203
+ }
204
+
205
+ img.fullwidth,
206
+ img.fullwidthOnMobile {
207
+ max-width: 100% !important;
208
+ }
209
+
210
+ .no-stack .col {
211
+ min-width: 0 !important;
212
+ display: table-cell !important;
213
+ }
214
+
215
+ .no-stack.two-up .col {
216
+ width: 50% !important;
217
+ }
218
+
219
+ .no-stack .col.num4 {
220
+ width: 33% !important;
221
+ }
222
+
223
+ .no-stack .col.num8 {
224
+ width: 66% !important;
225
+ }
226
+
227
+ .no-stack .col.num4 {
228
+ width: 33% !important;
229
+ }
230
+
231
+ .no-stack .col.num3 {
232
+ width: 25% !important;
233
+ }
234
+
235
+ .no-stack .col.num6 {
236
+ width: 50% !important;
237
+ }
238
+
239
+ .no-stack .col.num9 {
240
+ width: 75% !important;
241
+ }
242
+
243
+ .video-block {
244
+ max-width: none !important;
245
+ }
246
+
247
+ .mobile_hide {
248
+ min-height: 0px;
249
+ max-height: 0px;
250
+ max-width: 0px;
251
+ display: none;
252
+ overflow: hidden;
253
+ font-size: 0px;
254
+ }
255
+
256
+ .desktop_hide {
257
+ display: block !important;
258
+ max-height: none !important;
259
+ }
260
+ }
261
+ </style>
262
+ </head>
263
+ <body class=\\"clean-body\\" style=\\"margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: #FFFFFF;\\">
264
+ <p><!--[if IE]><div class=\\"ie-browser\\"><![endif]-->hello world</p>
265
+
266
+ <p><img alt=\\"Capillary Image\\" height=\\"171\\" src=\\"https://crm-nightly-new-fileservice.s3.amazonaws.com/intouch_creative_assets/b4b63b22-8a81-41ee-a8df-6db967fc.jpg\\" width=\\"257\\" /></p>
267
+
268
+ <table bgcolor=\\"#FFFFFF\\" cellpadding=\\"0\\" cellspacing=\\"0\\" class=\\"nl-container\\" role=\\"presentation\\" style=\\"background-color:#ffffff; border-collapse:collapse; border-spacing:0; margin:0 auto; min-width:320px; mso-table-lspace:0pt; mso-table-rspace:0pt; table-layout:fixed; vertical-align:top; width:100%\\" valign=\\"top\\">
269
+ <tbody>
270
+ <tr style=\\"vertical-align:top\\" valign=\\"top\\">
271
+ <td style=\\"vertical-align:top; word-break:break-word\\"><!--[if (mso)|(IE)]><table width=\\"100%\\" cellpadding=\\"0\\" cellspacing=\\"0\\" border=\\"0\\"><tr><td align=\\"center\\" style=\\"background-color:#FFFFFF\\"><![endif]-->
272
+ <div style=\\"background-color:transparent\\">
273
+ <div class=\\"block-grid\\" style=\\"background-color:transparent; margin-bottom:0; margin-left:auto; margin-right:auto; margin-top:0; max-width:500px; min-width:320px; overflow-wrap:break-word; word-break:break-word; word-wrap:break-word\\">
274
+ <div style=\\"background-color:transparent; border-collapse:collapse; display:table; width:100%\\"><!--[if (mso)|(IE)]><table width=\\"100%\\" cellpadding=\\"0\\" cellspacing=\\"0\\" border=\\"0\\" style=\\"background-color:transparent;\\"><tr><td align=\\"center\\"><table cellpadding=\\"0\\" cellspacing=\\"0\\" border=\\"0\\" style=\\"width:500px\\"><tr class=\\"layout-full-width\\" style=\\"background-color:transparent\\"><![endif]--><!--[if (mso)|(IE)]><td align=\\"center\\" width=\\"500\\" style=\\"background-color:transparent;width:500px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;\\" valign=\\"top\\"><table width=\\"100%\\" cellpadding=\\"0\\" cellspacing=\\"0\\" border=\\"0\\"><tr><td style=\\"padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;\\"><![endif]-->
275
+ <div class=\\"col num12\\" style=\\"display:table-cell; max-width:500px; min-width:320px; vertical-align:top; width:500px\\">
276
+ <div style=\\"width:100% !important\\"><!--[if (!mso)&(!IE)]><div style=\\"border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;\\"><!--<![endif]--><!--[if (!mso)&(!IE)]></div><!--<![endif]--></div>
277
+ </div>
278
+ <!--[if (mso)|(IE)]></td></tr></table><![endif]--><!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]--></div>
279
+ </div>
280
+ </div>
281
+ <!--[if (mso)|(IE)]></td></tr></table><![endif]--></td>
282
+ </tr>
283
+ </tbody>
284
+ </table>
285
+ <!--[if (IE)]></div><![endif]--></body>
286
+ </html>
287
+ ",
288
+ },
289
+ "selectedLanguages": Array [
290
+ "en",
291
+ ],
292
+ "subject": "hello subject",
293
+ "tabKey": "6",
294
+ },
295
+ ],
296
+ },
297
+ }
298
+ `;