-
-
Notifications
You must be signed in to change notification settings - Fork 100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce Table of Content Without Intense JS Invasion #1237
base: main
Are you sure you want to change the base?
Changes from all commits
09cd9d6
2143e92
4620a3b
5f74621
a60f676
8e45c85
77a5e56
bdfbffa
3a07518
df59543
f389411
6d4aca1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<RCC> | ||
<qresource prefix="/"> | ||
<file>js/tableofcontent.js</file> | ||
</qresource> | ||
</RCC> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** | ||
* Construct recurseData.str as a JSON Array that contains their header text, | ||
* link, and children headers. | ||
* | ||
* References: | ||
* https://stackoverflow.com/questions/187619/is-there-a-javascript-solution-to-generating-a-table-of-contents-for-a-page | ||
* @param elem DOM element | ||
* @param recurseData Object with fields: { level : int, toc : str, count : int, Set : levelSet } | ||
*/ | ||
function recurseChild(elem, recurseData) | ||
{ | ||
if (elem !== "undefined") | ||
{ | ||
if(elem.nodeName.match(/^H\d+$/) && elem.textContent) | ||
{ | ||
var headerText = elem.textContent.trim(); | ||
var prevLevel = recurseData.level; | ||
var level = elem.nodeName.substr(1); | ||
var anchor = "kiwix-toc-" + recurseData.count; | ||
var anchorLink = window.location.href.replace(location.hash,"") + '#' + anchor; | ||
recurseData.count += 1; | ||
|
||
var anchorElem = document.createElement("a"); | ||
anchorElem.id = anchor; | ||
|
||
/* Wrap header content with something we can reference. */ | ||
elem.insertAdjacentElement("afterbegin", anchorElem); | ||
|
||
if (level < prevLevel) | ||
{ | ||
/* Complete current element and the parent element.*/ | ||
recurseData.toc += ']}]}, '; | ||
} | ||
else if (level == prevLevel) | ||
{ | ||
/* Complete current element*/ | ||
recurseData.toc += ']}, '; | ||
} | ||
|
||
recurseData.level = parseInt(level); | ||
recurseData.levelSet.add(parseInt(level)); | ||
recurseData.toc += '{"text" : "' + headerText.replace(/"/g, '\\"') + '", "anchor": "' + anchorLink + '", ' + '"child" : ['; | ||
} | ||
|
||
var c = elem.children; | ||
for (var i = 0; i < c.length; i++) | ||
recurseChild(c[i], recurseData); | ||
} | ||
} | ||
|
||
function tocJSON() | ||
{ | ||
/* level used to track current header level. | ||
toc used to store constructed list. | ||
count used to uniquely identify each list item in toc. | ||
levelSet used to retrieve the levels disregarding header level value. | ||
*/ | ||
var recurseData = { level: 0, toc: '{ "url" : "' + window.location.href.replace(location.hash,"") + '", "table" : [', count: 0, levelSet: new Set}; | ||
recurseChild(document.body, recurseData); | ||
|
||
var levelArray = Array.from(recurseData.levelSet).sort(); | ||
levelArray.sort(function(a, b){return a - b}); | ||
var level = levelArray.indexOf(recurseData.level) + 1; | ||
|
||
/* End un-closed lists */ | ||
if (level) | ||
recurseData.toc += (new Array(level + 1)).join(']}'); | ||
recurseData.toc += ']}'; | ||
return recurseData.toc; | ||
} | ||
Comment on lines
+10
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that instead of constructing the final JSON string it is better to construct a recursive JS object and then call |
||
|
||
new QWebChannel(qt.webChannelTransport, function(channel) { | ||
var kiwixObj = channel.objects.kiwixChannelObj; | ||
kiwixObj.sendTableOfContent(tocJSON()); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#ifndef KIWIXWEBCHANNELOBJECT_H | ||
#define KIWIXWEBCHANNELOBJECT_H | ||
|
||
#include <QObject> | ||
|
||
class KiwixWebChannelObject : public QObject | ||
{ | ||
Q_OBJECT | ||
|
||
public: | ||
explicit KiwixWebChannelObject(QObject *parent = nullptr) : QObject(parent) {}; | ||
|
||
Q_INVOKABLE void sendTableOfContent(const QString& tableJson) { emit tableOfContentChanged(tableJson); }; | ||
|
||
signals: | ||
void tableOfContentChanged(const QString& tableJson); | ||
}; | ||
|
||
#endif // KIWIXWEBCHANNELOBJECT_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,26 @@ | |
#include <QFileDialog> | ||
#include <QMessageBox> | ||
#include <QWebEngineSettings> | ||
#include <QWebEngineScript> | ||
#include <QWebEngineScriptCollection> | ||
|
||
namespace | ||
{ | ||
|
||
QWebEngineScript getScript(QString filename, | ||
QWebEngineScript::InjectionPoint point = QWebEngineScript::DocumentReady) | ||
{ | ||
QWebEngineScript script; | ||
script.setInjectionPoint(point); | ||
script.setWorldId(QWebEngineScript::UserWorld); | ||
|
||
QFile scriptFile(filename); | ||
scriptFile.open(QIODevice::ReadOnly); | ||
script.setSourceCode(scriptFile.readAll()); | ||
return script; | ||
} | ||
Comment on lines
+13
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hide in unnamed namespace |
||
|
||
} | ||
|
||
QString askForSaveFilePath(const QString& suggestedName) | ||
{ | ||
|
@@ -36,6 +56,10 @@ KProfile::KProfile(QObject *parent) : | |
#else // Qt 5.13 and later | ||
setUrlRequestInterceptor(new ExternalReqInterceptor(this)); | ||
#endif | ||
|
||
scripts()->insert(getScript(":/js/tableofcontent.js")); | ||
scripts()->insert(getScript(":/qtwebchannel/qwebchannel.js", | ||
QWebEngineScript::DocumentCreation)); | ||
} | ||
|
||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it doesn't make sense to include the full URL with every link. The URL of the page can be provided only once at the root level of the returned JSON object while each TOC entry will include only the anchor.