Skip to content

Commit

Permalink
Merge pull request #1984 from Sefaria/bug/sc-28448/new-editor-inconsi…
Browse files Browse the repository at this point in the history
…stency-verse-numbers

Bug/sc 28448/new editor inconsistency verse numbers
  • Loading branch information
yonadavGit authored Aug 20, 2024
2 parents d9d2dd8 + afa44ee commit 24cce8a
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 63 deletions.
69 changes: 69 additions & 0 deletions sefaria/sheetsUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Sefaria from "../static/js/sefaria/sefaria";

export async function getSegmentObjs(refs) {
/*
Given an array of ref-strings (could also be ranged refs),
turn each ref to segment object and return an array of all segments
*/
const segments = [];

for (const ref of refs) {
const text = await Sefaria.getText(ref, { stripItags: 1 });
const newSegments = Sefaria.makeSegments(text, false);
segments.push(...newSegments);
}
return segments;
}
export async function getNormalRef(ref) {
/*
Given a ref-string, get he and en normal ref-string
*/
const refObj = await Sefaria.getRef(ref);
return {en: refObj.ref, he: refObj.heRef}
}
function placedSegmentMapper(lang, segmented, includeNumbers, s) {
/*
Map each segment object to a formatted text string
*/
if (!s[lang]) {return ""}

let numStr = "";
if (includeNumbers) {
const num = (lang=="he") ? Sefaria.hebrew.encodeHebrewNumeral(s.number) : s.number;
numStr = "<small>(" + num + ")</small> ";
}
let str = "<span class='segment'>" + numStr + s[lang] + "</span> ";
if (segmented) {
str = "<p>" + str + "</p>";
}
str = str.replace(/(<br\/>)+/g, ' ')
return str;
}
export const segmentsToSourceText = (segments, lan) => {
/*
Turn array of segment objects into one chunk of formatted text
*/
const segmented = shouldBeSegmented(segments[0].ref);
const includeNumbers = shouldIncludeSegmentNums(segments[0].ref);
return(segments.map(placedSegmentMapper.bind(this, lan, segmented, includeNumbers))
.filter(Boolean)
.join(""));
}
function shouldIncludeSegmentNums(ref){
/*
Decide if segment of this ref should have segment numbers when turned into text chunk
*/
const indexTitle = Sefaria.refIndexTitle(ref);
const categories = Sefaria.refCategories(ref);
if (categories.includes("Talmud")) {return false}
if (indexTitle === "Pesach Haggadah") {return false}
if (categories === 1) {return false}
return true;
}
function shouldBeSegmented(ref){
/*
Decide if segment of this ref should be followed by new line when turned into text chunk
*/
const categories = Sefaria.refCategories(ref);
return !(categories[0] in {"Tanakh": 1, "Talmud": 1});
}
59 changes: 56 additions & 3 deletions static/js/AddToSourceSheet.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Component from 'react-class';
import sanitizeHtml from 'sanitize-html';
import { SignUpModalKind } from './sefaria/signupModalContent';
import { GDocAdvertBox } from './Promotions';
import * as sheetsUtils from '../../sefaria/sheetsUtils'



Expand Down Expand Up @@ -89,7 +90,34 @@ class AddToSourceSheetBox extends Component {
}, this.confirmAdd);
}
}
addToSourceSheet() {

//return the initial index of the suffix of string1 which also constitutes a prefix for string2
longestSuffixPrefixIndex(string1, string2) {
let longestSuffixIndex = 0;
for (let i = 0; i < string1.length; i++){
let suffix = string1.slice(i);
if (string2.startsWith(suffix)) {
longestSuffixIndex = i;
}
}
return longestSuffixIndex;
}
//return the final index of the prefix of string1 which also constitutes a suffix for string2
longestPrefixSuffixIndex(string1, string2) {
let longestPrefixIndex = 0;
for (let i = 0; i < string1.length; i++) {
let prefix = string1.slice(0, i + 1);
if (string2.endsWith(prefix)) {
longestPrefixIndex = i + 1;
}
}
return longestPrefixIndex;
}

normalize(text){
return(text.replaceAll(/(<br\/>)+/g, ' ').replace(/\u2009/g, ' ').replace(/<[^>]*>/g, ''));
}
async addToSourceSheet() {
if (!Sefaria._uid) {
this.props.toggleSignUpModal(SignUpModalKind.AddToSheet);
}
Expand All @@ -111,15 +139,40 @@ class AddToSourceSheetBox extends Component {
}
} else if (this.props.srefs) { //regular use - this is currently the case when the component is loaded in the sidepanel or in the modal component via profiles and notes pages
source.refs = this.props.srefs;



const { en, he } = this.props.currVersions ? this.props.currVersions : {"en": null, "he": null}; //the text we are adding may be non-default version
if (he) { source["version-he"] = he; }
if (en) { source["version-en"] = en; }

// If something is highlighted and main panel language is not bilingual:
// Use passed in language to determine which version this highlight covers.
var selectedWords = this.props.selectedWords; //if there was highlighted single panel
let selectedWords = this.props.selectedWords; //if there was highlighted single panel
if (selectedWords && language != "bilingual") {
source[language.slice(0,2)] = selectedWords;
let lan = language.slice(0,2);
let segments = await sheetsUtils.getSegmentObjs(source.refs);
selectedWords = this.normalize(selectedWords);
segments = segments.map(segment => ({
...segment,
[lan]: this.normalize(segment[lan])
}));
for (let iSegment = 0; iSegment < segments.length; iSegment++) {
const segment = segments[iSegment];
if (iSegment == 0){
let criticalIndex = this.longestSuffixPrefixIndex(segment[lan], selectedWords);
const ellipse = criticalIndex == 0 ? "" : "...";
segment[lan] = ellipse + segment[lan].slice(criticalIndex);
}
else if (iSegment == segments.length-1){
let criticalIndex = this.longestPrefixSuffixIndex(segment[lan], selectedWords);
const ellipse = criticalIndex == segment[lan].length-1 ? "" : "...";
const chunk = segment[lan].slice(0, criticalIndex)
segment[lan] = chunk + ellipse;
}
}

source[lan] = sheetsUtils.segmentsToSourceText(segments, lan);
}
}
if (this.checkContentForImages(source.refs)) {
Expand Down
96 changes: 36 additions & 60 deletions static/js/Editor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {Editor, createEditor, Range, Node, Transforms, Path, Text, Point, Elemen
import {Slate, Editable, ReactEditor, withReact, useSlate, useSelected, useFocused} from 'slate-react'
import isHotkey from 'is-hotkey'
import Sefaria from './sefaria/sefaria';
import * as sheetsUtils from '../../sefaria/sheetsUtils'


import {
SheetMetaDataBox,
Expand Down Expand Up @@ -1866,73 +1868,47 @@ const insertMedia = (editor, mediaUrl) => {
Transforms.move(editor);
}


function placed_segment_mapper(lang, segmented, includeNumbers, s) {
if (!s[lang]) {return ""}

let numStr = "";
if (includeNumbers) {
const num = (lang=="he") ? Sefaria.hebrew.encodeHebrewNumeral(s.number) : s.number;
numStr = "<small>(" + num + ")</small> ";
}
let str = "<span class='segment'>" + numStr + s[lang] + "</span> ";
if (segmented) {
str = "<p>" + str + "</p>";
}
str = str.replace(/(<br\/>)+/g, ' ')
return str;
}


const insertSource = (editor, ref) => {
const insertSource = async (editor, ref) => {
const path = editor.selection.anchor.path;

Transforms.setNodes(editor, { loading: true }, {at: path});

const nodeAbove = getNodeAbove(path, editor)
const nodeBelow = getNodeBelow(path, editor)

Sefaria.getText(ref, {stripItags: 1}).then(text => {
let segments = Sefaria.makeSegments(text, false);
segments = Sefaria.stripImagesFromSegments(segments);

let includeNumbers = $.inArray("Talmud", text.categories) == -1;
includeNumbers = text.indexTitle === "Pesach Haggadah" ? false : includeNumbers;
const segmented = !(text.categories[0] in {"Tanakh": 1, "Talmud": 1});

const enText = segments.map(placed_segment_mapper.bind(this, "en", segmented, includeNumbers))
.filter(Boolean)
.join("");
const heText = segments.map(placed_segment_mapper.bind(this, "he", segmented, includeNumbers))
.filter(Boolean)
.join("");

let fragment = [{
type: "SheetSource",
node: editor.children[0].nextNode,
ref: text.ref,
heRef: text.heRef,
heText: parseSheetItemHTML(heText),
enText: parseSheetItemHTML(enText),
title: null,
children: [
{text: ""},
]
}];

if (!(nodeBelow.node && (nodeBelow.node.type == "SheetOutsideText" || nodeBelow.node.type == "paragraph" ) )) {
fragment.push({type: 'spacer', children: [{text: ""}]})
}
Transforms.setNodes(editor, { loading: false }, { at: path });
addItemToSheet(editor, fragment);
checkAndFixDuplicateSheetNodeNumbers(editor)
if (nodeAbove.node && (nodeAbove.node.type == "SheetOutsideText" || nodeAbove.node.type == "paragraph" ) ) {
Transforms.delete(editor, {at: path})
}


Transforms.move(editor, { unit: 'block', distance: 1 })
});
const {en: normalEnRef, he: normalHeRef} = await sheetsUtils.getNormalRef(ref);

let segments = await sheetsUtils.getSegmentObjs([ref])

const enText = sheetsUtils.segmentsToSourceText(segments, 'en');

const heText = sheetsUtils.segmentsToSourceText(segments, 'he');

let fragment = [{
type: "SheetSource",
node: editor.children[0].nextNode,
ref: normalEnRef,
heRef: normalHeRef,
heText: parseSheetItemHTML(heText),
enText: parseSheetItemHTML(enText),
title: null,
children: [
{text: ""},
]
}];

if (!(nodeBelow.node && (nodeBelow.node.type == "SheetOutsideText" || nodeBelow.node.type == "paragraph" ) )) {
fragment.push({type: 'spacer', children: [{text: ""}]})
}
Transforms.setNodes(editor, { loading: false }, { at: path });
addItemToSheet(editor, fragment);
checkAndFixDuplicateSheetNodeNumbers(editor)
if (nodeAbove.node && (nodeAbove.node.type == "SheetOutsideText" || nodeAbove.node.type == "paragraph" ) ) {
Transforms.delete(editor, {at: path})
}


Transforms.move(editor, { unit: 'block', distance: 1 })
};


Expand Down
6 changes: 6 additions & 0 deletions static/js/sefaria/sefaria.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@ Sefaria = extend(Sefaria, {
let index = Sefaria.index(pRef.index);
return index && index.categories ? index.categories : [];
},
refIndexTitle: function(ref) {
let pRef = Sefaria.parseRef(ref);
if ("error" in pRef) { return null; }
let index = Sefaria.index(pRef.index);
return index?.title
},
sectionRef: function(ref, deriveIfNotFound=false) {
// Returns the section level ref for `ref` or null if no data is available
const oref = this.getRefFromCache(ref);
Expand Down

0 comments on commit 24cce8a

Please sign in to comment.