Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions step-web/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
<jsSourceFile>js/backbone/models/passage_model.js</jsSourceFile>
<jsSourceFile>js/backbone/models/model_settings.js</jsSourceFile>
<jsSourceFile>js/backbone/views/view_menu_passage.js</jsSourceFile>
<jsSourceFile>js/backbone/views/view_menu_copy.js</jsSourceFile>
<jsSourceFile>js/state/step.state.js</jsSourceFile>
<jsSourceFile>js/passage/step.passage.js</jsSourceFile>
<jsSourceFile>js/backbone/views/view_feedback.js</jsSourceFile>
Expand Down
1,310 changes: 1,310 additions & 0 deletions step-web/src/main/webapp/js/backbone/views/view_menu_copy.js

Large diffs are not rendered by default.

530 changes: 367 additions & 163 deletions step-web/src/main/webapp/js/copy_text.js

Large diffs are not rendered by default.

154 changes: 114 additions & 40 deletions step-web/src/main/webapp/js/step.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,11 @@ step.util = {
new PassageMenuView({
model: step.util.activePassage()
});
if (typeof PassageCopyMenuView === "function") {
new PassageCopyMenuView({
model: step.util.activePassage()
});
}

Backbone.Events.trigger("columnsChanged", {});
return newPassageId;
Expand Down Expand Up @@ -1831,6 +1836,109 @@ step.util = {
return [verse, version];
},

"getSelectionVerseInfo": function (el) {
var $el = $(el);
var verse = '';
var version = '';

// Standard Bible: <div class="verse"> > <a class="verseLink">
verse = $($el.closest("div.verse").find('a.verseLink')[0]).attr('name');
// Interleaved comparison: <div class="verseGrouping">
if (!verse) verse = $el.closest(".verseGrouping").find(".heading .verseLink").attr("name");
// Interlinear: <span class="interlinear">
if (!verse) verse = $el.closest(".verse, .interlinear").find(".verseLink").attr("name");
// Commentary: <span class="commentaryVerse"> > <a name="...">
if (!verse) {
var commentaryVerse = $el.closest(".commentaryVerse");
if (commentaryVerse.length > 0)
verse = commentaryVerse.find('a[name]').first().attr('name');
}

if (!verse) verse = '';
// Handle space-separated OSIS IDs (e.g., "Gen.1.1 Gen.1.2")
if (verse.indexOf(' ') > -1) verse = verse.split(' ')[0];

// Version detection (same logic as getVerseNumberAndVersion)
version = $el.closest("div.verse").parent().find('span.smallResultKey').attr('data-version') ||
$el.closest(".singleVerse").find('span.smallResultKey').attr('data-version');
if (!version) {
var compareVersionHeader = $('th.comparingVersionName');
if (compareVersionHeader.length > 0) {
var index = $el.closest('td').index();
if (typeof index === 'number' && index > 0)
version = $(compareVersionHeader[index - 1]).text();
}
}
// Fallback: active passage's masterVersion
if (!version || typeof step.keyedVersions[version] !== "object") {
var passageContainer = $el.closest('.passageContainer');
if (passageContainer.length > 0) {
var passageId = passageContainer.attr('passage-id');
var model = step.passages.findWhere({ passageId: parseInt(passageId) });
if (model) version = model.get('masterVersion');
}
}
if (!version) version = '';
return { verse: verse, version: version };
},

"initSelectionTracking": function () {
var debounceTimer;
step.lastPassageSelection = null;
document.addEventListener('selectionchange', function () {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(function () {
var sel = window.getSelection();
if (!sel || sel.rangeCount === 0 || sel.isCollapsed) {
// Copy-dropdown gate: when a dropdown is open, clicks inside
// it collapse the native selection. Don't flip deselectedAt,
// or the 5s grace window will drop us out of selection mode.
if (step.copyDropdown && step.copyDropdown.shouldSuppressCollapseEvent && step.copyDropdown.shouldSuppressCollapseEvent())
return;
if (step.lastPassageSelection && !step.lastPassageSelection.deselectedAt)
step.lastPassageSelection.deselectedAt = Date.now();
return;
}
var range = sel.getRangeAt(0);
var startNode = range.startContainer;
var endNode = range.endContainer;
var startEl = (startNode.nodeType === 3) ? startNode.parentElement : startNode;
var endEl = (endNode.nodeType === 3) ? endNode.parentElement : endNode;
if (!startEl || $(startEl).closest('.passageContentHolder').length === 0) return;
var text = sel.toString().trim();
if (text.length === 0) return;
var startInfo = step.util.ui["getSelectionVerseInfo"](startEl);
var endInfo = step.util.ui["getSelectionVerseInfo"](endEl);
var allVersions = [];
var addVersionIfNeeded = function (version) {
if (version && allVersions.indexOf(version) === -1)
allVersions.push(version);
};
var rangeCommonAncestor = range.commonAncestorContainer;
if (rangeCommonAncestor && rangeCommonAncestor.nodeType === 3)
rangeCommonAncestor = rangeCommonAncestor.parentElement;
if (rangeCommonAncestor) {
$(rangeCommonAncestor).find('.verse, .singleVerse, .interlinear, .commentaryVerse').each(function () {
if (range.intersectsNode && !range.intersectsNode(this)) return;
var verseInfo = step.util.ui["getSelectionVerseInfo"](this);
addVersionIfNeeded(verseInfo.version);
});
}
addVersionIfNeeded(startInfo.version);
addVersionIfNeeded(endInfo.version);
step.lastPassageSelection = {
startVerse: startInfo.verse,
endVerse: endInfo.verse,
version: startInfo.version || endInfo.version,
versions: allVersions,
textLength: text.length,
timestamp: Date.now(),
deselectedAt: null
};
}, 150);
});
},

getWordOrderSuffix: function (el, strongsSelectedByUser) {
var verseClass = $(el).closest('.verse');
if (verseClass.length == 0)
Expand Down Expand Up @@ -2517,45 +2625,11 @@ step.util = {
step.util.blockBackgroundScrolling("passageSelectionModal");
},

copyModal: function () { // Do not shorten name in pom.xml because it is called at start.jsp
var element = document.getElementById('copyModal');
if (element) element.parentNode.removeChild(element);
$("div.modal-backdrop.in").remove();
var modalHTML = '<div id="copyModal" class="modal selectModal" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">' +
'<div class="modal-dialog">' +
'<div class="modal-content stepModalFgBg" style="width:95%;max-width:100%;top:0;right:0;bottom:0;left:0;-webkit-overflow-scrolling:touch">' +
'<div class="modal-header">' +
'<span class="pull-right">' +
step.util.modalCloseBtn("copyModal") +
'<span class="pull-right">&nbsp;&nbsp;&nbsp;</span>' +
'</span>'+
'</div>';
modalHTML +=
'<div id="selectversionstocopy" class="modal-body" style="padding-bottom:0"></div>' +
'<div id="bookchaptermodalbody" class="modal-body"></div>';
modalHTML +=
'<div class="footer" id="copyModalFooter">' +
'<div id="includeNotes" style="display:none">' +
'<span>&nbsp;&nbsp;<b>Include notes</b>&nbsp;</span>' +
'<input type="checkbox" id="selectnotes"/>' +
'</div>' +
'<div id="includeXRefs" style="display:none">' +
'<span>&nbsp;&nbsp;<b>Include cross references</b>&nbsp;</span>' +
'<input type="checkbox" id="selectxref"/>' +
'</div>' +
'<br>' +
'</div>' +
'<script>' +
'$(document).ready(function () {' +
'step.copyText.initVerseSelect();' +
'});' +
'</script>' +
'</div>' +
'</div>' +
'</div>';
$(_.template(modalHTML)()).modal("show");
step.util.blockBackgroundScrolling("copyModal");
},
copyModal: function () { // Do not shorten name in pom.xml — still called from start.jsp hrefs
var activePanelId = step.util.activePassageId();
var view = step.copyDropdown && step.copyDropdown.views[activePanelId];
if (view) view.toggle();
},

lexFeedbackModal: function (strongNum, ref, version) {
var element = document.getElementById('lexFeedbackModal');
Expand Down Expand Up @@ -4238,7 +4312,7 @@ step.util = {
'left', 499, 'step.multiVersionCount');
},
closeModal: function (modalID) {
var modalsRequireUnfreezeOfScroll = " showLongAlertModal showBookOrChapterSummaryModal grammarClrModal passageSelectionModal searchSelectionModal copyModal videoModal fontSettings raiseSupport aboutModal bibleVersions ";
var modalsRequireUnfreezeOfScroll = " showLongAlertModal showBookOrChapterSummaryModal grammarClrModal passageSelectionModal searchSelectionModal videoModal fontSettings raiseSupport aboutModal bibleVersions ";
if ((modalsRequireUnfreezeOfScroll.indexOf( " " + modalID + " ") > -1) && step.touchDevice && !step.touchWideDevice)
$("body").css("overflow-y","auto"); // let the body (web page) scroll
if (modalID === "bibleVersions")
Expand Down
11 changes: 10 additions & 1 deletion step-web/src/main/webapp/js/step_ready.js
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,11 @@
new PassageMenuView({
model: modelZero
});
if (typeof PassageCopyMenuView === "function") {
new PassageCopyMenuView({
model: modelZero
});
}

step.router.handleRenderModel(modelZero, true, $.getUrlVar('q'));

Expand Down Expand Up @@ -540,6 +545,10 @@
initCoreModelsAndRouter();
initSearchDropdown();
initReportDropdownPortal();
if (!step.selectionTrackingInitialized) {
step.selectionTrackingInitialized = true;
step.util.ui.initSelectionTracking();
}

Backbone.history.start({pushState: true, silent: true});

Expand Down Expand Up @@ -667,7 +676,7 @@
var ua = navigator.userAgent.toLowerCase();
if (ua.indexOf('firefox') > -1) $("#panel-icon").hide(); // Firefox has some issues with this.
if ((typeof navigator.clipboard !== "object") || (typeof navigator.clipboard.writeText !== "function"))
$("#copy-icon").hide();
{ $("#copy-icon").hide(); $(".copyDropdown").hide(); }
if ( ((step.appleTouchDevice) && (ua.search(/ cpu os [345678]_/) > -1)) || // older versions of iOS shows a light grey color. Probably similiar issue as Internet Explorer
(false || !!document.documentMode) ) { // Internet Explorer use the wrong css based on the <a> tag, so change it to black
$("#panel-icon").css("color", "black");
Expand Down
Loading