sr-npm 1.7.1263 → 1.7.1267
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.
package/backend/data.js
CHANGED
|
@@ -287,7 +287,7 @@ async function saveJobsDescriptionsAndLocationApplyUrlReferencesToCMS() {
|
|
|
287
287
|
const chunkPromises = chunk.map(async job => {
|
|
288
288
|
try {
|
|
289
289
|
const jobDetails = await fetchJobDescription(job._id);
|
|
290
|
-
const richContentDescription=await htmlRichContentConverter(jobDetails.jobAd.sections,richContentConverterToken
|
|
290
|
+
const richContentDescription=await htmlRichContentConverter(jobDetails.jobAd.sections,richContentConverterToken);
|
|
291
291
|
const jobLocation = fetchJobLocation(jobDetails);
|
|
292
292
|
const {applyLink , referFriendLink} = fetchApplyAndReferFriendLink(jobDetails);
|
|
293
293
|
const updatedJob = {
|
|
@@ -114,12 +114,12 @@ async function fetchJobDescription(jobId,testObject=undefined) {
|
|
|
114
114
|
return await makeSmartRecruitersRequest(`/v1/companies/${companyId}/postings/${jobId}`,templateType);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
async function htmlRichContentConverter(sections,richContentConverterToken
|
|
117
|
+
async function htmlRichContentConverter(sections,richContentConverterToken) {
|
|
118
118
|
|
|
119
119
|
const richContentObject = {}
|
|
120
120
|
for (const [sectionTitle, sectionData] of Object.entries(sections)) {
|
|
121
121
|
if (sectionData.text) {
|
|
122
|
-
// Strip span tags but keep their content
|
|
122
|
+
// Strip span tags but keep their content , since <span> tags paragraphs are deleted by the converter
|
|
123
123
|
const cleanedHtml = sectionData.text.replace(/<\/?span[^>]*>/gi, '');
|
|
124
124
|
const raw = JSON.stringify({
|
|
125
125
|
content: cleanedHtml,
|
|
@@ -139,8 +139,10 @@ async function htmlRichContentConverter(sections,richContentConverterToken,jobNa
|
|
|
139
139
|
);
|
|
140
140
|
if (response.ok) {
|
|
141
141
|
const data = await response.json();
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
// Fix list items with nested paragraphs (causes line breaks after bold text)
|
|
143
|
+
const flattenedContent = flattenListItems(data.richContent.richContent);
|
|
144
|
+
// const richContentWithSpacing=addSpacingToRichContent(cleanedHtml,flattenedContent);
|
|
145
|
+
const richContentWithSpacing=addEmptyParagraphsBetweenConsecutive(cleanedHtml,flattenedContent);
|
|
144
146
|
richContentObject[sectionTitle] = richContentWithSpacing
|
|
145
147
|
}
|
|
146
148
|
else {
|
|
@@ -151,163 +153,6 @@ async function htmlRichContentConverter(sections,richContentConverterToken,jobNa
|
|
|
151
153
|
return richContentObject;
|
|
152
154
|
}
|
|
153
155
|
|
|
154
|
-
//Adds empty paragraph nodes between sections in rich content
|
|
155
|
-
// to create visual spacing that the Wix RICOS converter strips out
|
|
156
|
-
function addSpacingToRichContent(html, richContent) {
|
|
157
|
-
if (!richContent || !richContent.nodes) {
|
|
158
|
-
return richContent;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
// Extract paragraph texts from HTML that end with  
|
|
163
|
-
const htmlParagraphsWithSpace = [];
|
|
164
|
-
// Extract paragraphs with <br> tags
|
|
165
|
-
const htmlParagraphsWithBr = new Map(); // text -> array of parts split by <br>
|
|
166
|
-
// Check if HTML has consecutive paragraphs (</p><p> pattern)
|
|
167
|
-
const hasConsecutiveParagraphs = /<\/p>\s*<p/i.test(html);
|
|
168
|
-
// Check if HTML has paragraph followed by list (</p><ul> pattern)
|
|
169
|
-
const hasParagraphBeforeList = /<\/p>\s*<ul/i.test(html);
|
|
170
|
-
|
|
171
|
-
const pTagRegex = /<p>(.*?)<\/p>/gi;
|
|
172
|
-
let match;
|
|
173
|
-
|
|
174
|
-
while ((match = pTagRegex.exec(html)) !== null) {
|
|
175
|
-
const content = match[1];
|
|
176
|
-
|
|
177
|
-
// Check if this paragraph ends with   (before closing tags)
|
|
178
|
-
if (content.includes(' ')) {
|
|
179
|
-
const textOnly = content.replace(/<[^>]+>/g, '').trim();
|
|
180
|
-
htmlParagraphsWithSpace.push(textOnly);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Check if this paragraph contains <br> tags
|
|
184
|
-
if (content.includes('<br>') || content.includes('<br/>') || content.includes('<br />')) {
|
|
185
|
-
// Split by <br> tags and extract text parts
|
|
186
|
-
const parts = content.split(/<br\s*\/?>/i).map(part =>
|
|
187
|
-
part.replace(/<[^>]+>/g, '').trim()
|
|
188
|
-
).filter(part => part.length > 0);
|
|
189
|
-
|
|
190
|
-
if (parts.length > 1) {
|
|
191
|
-
// Store the parts for this paragraph
|
|
192
|
-
const fullText = content.replace(/<[^>]+>/g, '').replace(/\s+/g, '').trim();
|
|
193
|
-
htmlParagraphsWithBr.set(fullText, parts);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const nodes = richContent.nodes;
|
|
199
|
-
const newNodes = [];
|
|
200
|
-
let nodeIdCounter = 0;
|
|
201
|
-
|
|
202
|
-
// Check if a paragraph is bold (has BOLD decoration)
|
|
203
|
-
const isBoldParagraph = (node) => {
|
|
204
|
-
if (node.type !== 'PARAGRAPH') return false;
|
|
205
|
-
const decorations = node.nodes?.[0]?.textData?.decorations || [];
|
|
206
|
-
return decorations.some(d => d.type === 'BOLD');
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
// Check if a paragraph node's text matches one with   in HTML
|
|
210
|
-
const needsSpacingAfter = (node, nextNode) => {
|
|
211
|
-
if (node.type !== 'PARAGRAPH') return false;
|
|
212
|
-
|
|
213
|
-
// Add spacing after bold paragraphs
|
|
214
|
-
if (isBoldParagraph(node)) {
|
|
215
|
-
return true;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// If HTML has consecutive <p> tags and next node is also a paragraph, add spacing
|
|
219
|
-
if (hasConsecutiveParagraphs && nextNode && nextNode.type === 'PARAGRAPH') {
|
|
220
|
-
return true;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// If HTML has </p><ul> and next node is a list, add spacing
|
|
224
|
-
if (hasParagraphBeforeList && nextNode && nextNode.type === 'BULLETED_LIST') {
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const text = node.nodes?.[0]?.textData?.text || '';
|
|
229
|
-
const trimmedText = text.trim();
|
|
230
|
-
|
|
231
|
-
// Check if this text matches any HTML paragraph that had  
|
|
232
|
-
return htmlParagraphsWithSpace.some(htmlText => {
|
|
233
|
-
const cleanHtml = htmlText.replace(/ /g, ' ').trim();
|
|
234
|
-
return trimmedText.includes(cleanHtml) || cleanHtml.includes(trimmedText);
|
|
235
|
-
});
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
// Check if a paragraph contains text that should be split by <br>
|
|
239
|
-
const shouldSplitByBr = (node) => {
|
|
240
|
-
if (node.type !== 'PARAGRAPH') return null;
|
|
241
|
-
|
|
242
|
-
const text = node.nodes?.[0]?.textData?.text || '';
|
|
243
|
-
const normalizedText = text.replace(/\s+/g, '').trim();
|
|
244
|
-
|
|
245
|
-
// Find matching HTML paragraph with <br>
|
|
246
|
-
for (const [htmlText, parts] of htmlParagraphsWithBr.entries()) {
|
|
247
|
-
if (normalizedText.includes(htmlText) || htmlText.includes(normalizedText)) {
|
|
248
|
-
return parts;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return null;
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
// Add spacing after bulleted lists
|
|
255
|
-
const isListEnd = (currentNode, nextNode) => {
|
|
256
|
-
return currentNode.type === 'BULLETED_LIST' &&
|
|
257
|
-
nextNode &&
|
|
258
|
-
nextNode.type === 'PARAGRAPH';
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
262
|
-
const currentNode = nodes[i];
|
|
263
|
-
const nextNode = nodes[i + 1];
|
|
264
|
-
|
|
265
|
-
// Check if this paragraph should be split by <br>
|
|
266
|
-
const brParts = shouldSplitByBr(currentNode);
|
|
267
|
-
if (brParts && brParts.length > 1) {
|
|
268
|
-
// Split into multiple paragraphs and add spacing between them
|
|
269
|
-
const decorations = currentNode.nodes?.[0]?.textData?.decorations || [];
|
|
270
|
-
|
|
271
|
-
brParts.forEach((part, idx) => {
|
|
272
|
-
newNodes.push({
|
|
273
|
-
...currentNode,
|
|
274
|
-
id: `${currentNode.id}_split_${idx}`,
|
|
275
|
-
nodes: [{
|
|
276
|
-
type: "TEXT",
|
|
277
|
-
id: "",
|
|
278
|
-
nodes: [],
|
|
279
|
-
textData: {
|
|
280
|
-
text: part,
|
|
281
|
-
decorations: decorations
|
|
282
|
-
}
|
|
283
|
-
}]
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
// Add empty paragraph after each split part except the last
|
|
287
|
-
if (idx < brParts.length - 1) {
|
|
288
|
-
newNodes.push(createEmptyParagraph(`empty_br_${nodeIdCounter++}`));
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
// Add spacing after the split paragraphs if there's a next node
|
|
293
|
-
if (nextNode) {
|
|
294
|
-
newNodes.push(createEmptyParagraph(`empty_${nodeIdCounter++}`));
|
|
295
|
-
}
|
|
296
|
-
} else {
|
|
297
|
-
newNodes.push(currentNode);
|
|
298
|
-
|
|
299
|
-
// Add empty paragraph after paragraphs with  , consecutive paragraphs, or after lists
|
|
300
|
-
if ((needsSpacingAfter(currentNode, nextNode) || isListEnd(currentNode, nextNode)) && nextNode) {
|
|
301
|
-
newNodes.push(createEmptyParagraph(`empty_${nodeIdCounter++}`));
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return {
|
|
307
|
-
...richContent,
|
|
308
|
-
nodes: newNodes
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
156
|
|
|
312
157
|
function createEmptyParagraph(id) {
|
|
313
158
|
return {
|
|
@@ -332,14 +177,44 @@ function createEmptyParagraph(id) {
|
|
|
332
177
|
};
|
|
333
178
|
}
|
|
334
179
|
|
|
335
|
-
//
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
180
|
+
// Flattens LIST_ITEM nodes by removing nested PARAGRAPH wrappers
|
|
181
|
+
// Fixes Wix converter bug where bold text + regular text in <li> creates line breaks
|
|
182
|
+
function flattenListItems(richContent) {
|
|
183
|
+
if (!richContent || !richContent.nodes) return richContent;
|
|
184
|
+
|
|
185
|
+
const processNode = (node) => {
|
|
186
|
+
if (node.type === 'BULLETED_LIST' || node.type === 'ORDERED_LIST') {
|
|
187
|
+
return {
|
|
188
|
+
...node,
|
|
189
|
+
nodes: node.nodes.map(listItem => {
|
|
190
|
+
if (listItem.type !== 'LIST_ITEM') return listItem;
|
|
191
|
+
|
|
192
|
+
// Flatten: extract TEXT nodes from nested PARAGRAPHs
|
|
193
|
+
const flattenedChildren = [];
|
|
194
|
+
for (const child of listItem.nodes) {
|
|
195
|
+
if (child.type === 'PARAGRAPH' && child.nodes) {
|
|
196
|
+
// Pull TEXT nodes out of the PARAGRAPH
|
|
197
|
+
flattenedChildren.push(...child.nodes);
|
|
198
|
+
} else {
|
|
199
|
+
flattenedChildren.push(child);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return { ...listItem, nodes: flattenedChildren };
|
|
204
|
+
})
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return node;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
...richContent,
|
|
212
|
+
nodes: richContent.nodes.map(processNode)
|
|
213
|
+
};
|
|
342
214
|
}
|
|
215
|
+
|
|
216
|
+
// Adds empty paragraph nodes between consecutive paragraphs and before lists
|
|
217
|
+
function addEmptyParagraphsBetweenConsecutive(html, richContent) {
|
|
343
218
|
if (!richContent || !richContent.nodes) return richContent;
|
|
344
219
|
const hasConsecutiveParagraphs = /<\/p>\s*<p/i.test(html);
|
|
345
220
|
const hasParagraphBeforeList = /<\/p>\s*<ul/i.test(html);
|
package/package.json
CHANGED
|
@@ -75,8 +75,8 @@ async function handleBackAndForth(_$w){
|
|
|
75
75
|
ActivateURLOnchange=false;
|
|
76
76
|
await clearAll(_$w,true);
|
|
77
77
|
await handleUrlParams(_$w,newQueryParams,true);
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
// ActivateURLOnchange=true;
|
|
79
|
+
|
|
80
80
|
}
|
|
81
81
|
else{
|
|
82
82
|
ActivateURLOnchange=true;
|