diff --git a/Makefile b/Makefile
index 9556f80..f00c9c6 100644
--- a/Makefile
+++ b/Makefile
@@ -11,8 +11,8 @@ endif
EXTENSION=KnowledgeGraph
# docker images
-MW_VERSION?=1.39
-PHP_VERSION?=8.1
+MW_VERSION?=1.43
+PHP_VERSION?=8.3
DB_TYPE?=mysql
DB_IMAGE?="mariadb:10"
diff --git a/extension.json b/extension.json
index d683382..e933188 100644
--- a/extension.json
+++ b/extension.json
@@ -36,7 +36,6 @@
"KnowledgeGraphDesigner": "SpecialKnowledgeGraphDesigner"
},
"Hooks":{
- "LoadExtensionSchemaUpdates": "KnowledgeGraph::onLoadExtensionSchemaUpdates",
"BeforePageDisplay":"KnowledgeGraph::onBeforePageDisplay",
"ParserFirstCallInit": "KnowledgeGraph::onParserFirstCallInit",
"OutputPageParserOutput": "KnowledgeGraph::onOutputPageParserOutput",
@@ -91,6 +90,8 @@
"knowledgegraph-dialog-cancel",
"knowledgegraph-dialog-delete",
"knowledgegraph-dialog-select-article",
+ "knowledgegraph-dialog-select-namespace",
+ "knowledgegraph-dialog-main-namespace",
"knowledgegraph-dialog-edit-depth",
"knowledgegraph-dialog-edit-limit",
"knowledgegraph-dialog-edit-offset",
diff --git a/i18n/de.json b/i18n/de.json
index 810be16..58bc0ee 100644
--- a/i18n/de.json
+++ b/i18n/de.json
@@ -13,7 +13,7 @@
"knowledgegraph-knowledgegraphdesigner-label": "KnowledgeGraph Designer",
"knowledgegraph-toolbar-info": "Info",
"knowledgegraph-toolbar-help": "Hilfe",
- "knowledgegraph-graph-options-message": "Klicken Sie auf die Schaltfläche unten und kopieren Sie den Inhalt der Tasten \"nodes\" und \"edges\" in einen Artikel wie diesen. Dann setzen Sie ihn als Wert der Parameter \"graph-options\" oder \"property-options?[Property label] der Parser-Funktion des KnowledgeGraphs.",
+ "knowledgegraph-graph-options-message": "{{#FORMAL:Klicke|Klicken Sie}} auf die Schaltfläche unten und {{#FORMAL:kopiere|kopieren Sie}} den Inhalt der Tasten \"nodes\" und \"edges\" in einen Artikel wie diesen. Dann {{#FORMAL:setze|setzen Sie}} ihn als Wert der Parameter \"graph-options\" oder \"property-options?[Property label] der Parser-Funktion des KnowledgeGraphs.",
"knowledgegraph-menu-open-article": "Seite öffnen",
"knowledgegraph-menu-delete-node": "Knoten entfernen",
"knowledgegraph-delete-node-confirm": "Diesen Knoten sicher entfernen?",
@@ -22,7 +22,9 @@
"knowledgegraph-dialog-done": "Fertig",
"knowledgegraph-dialog-cancel": "Abbrechen",
"knowledgegraph-dialog-delete": "Löschen",
- "knowledgegraph-dialog-select-article": "Wählen Sie einen Artikel mit semantischen Attributen",
+ "knowledgegraph-dialog-select-article": "{{#FORMAL:Wähle|Wählen Sie}} einen Artikel mit semantischen Attributen",
+ "knowledgegraph-dialog-select-namespace": "{{#FORMAL:Wähle|Wählen Sie}} einen Namensraum",
+ "knowledgegraph-dialog-main-namespace": "(Main)",
"knowledgegraph-dialog-edit-depth": "Tiefe",
"knowledgegraph-dialog-edit-limit": "Limit",
"knowledgegraph-dialog-edit-offset": "Versatz",
@@ -42,10 +44,10 @@
"knowledgegraph-dialog-results-importing-nodes": "importiert Knoten:",
"knowledgegraph-copied-to-clipboard": "In die Zwischenablage kopiert!",
"knowledgegraph-toolbar-reset-network": "zurücksetzen",
- "knowledgegraph-toolbar-reset-network-confirm": "Bist du sicher, dass du das Netzwerk neu initialisieren willst?",
+ "knowledgegraph-toolbar-reset-network-confirm": "{{#FORMAL:Bist du|Sind Sie}} sicher, dass {{#FORMAL:du|Sie}} das Netzwerk neu initialisieren {{#FORMAL:willst|wollen}}?",
"knowledgegraph-toolbar-add-node": "Knoten hinzufügen",
"knowledgegraph-toolbar-toggle-config": "Konfiguration umschalten",
"knowledgegraph-toolbar-export-graph": "exportiere Wikitext",
"knowledgegraph-credits": "Info:",
- "knowledgegraph-credits-list": "
'
+ '
'
. wfMessage( 'knowledge-graph-wrapper-loading' )->text() . '
',
'noparse' => true,
'isHTML' => true
@@ -358,6 +379,75 @@ public static function getSubjectsByProperty( $propertyText, $limit = 100, $offs
return $ret;
}
+ /**
+ * Get all properties for a given node.
+ * @param string $nodeTitleText
+ * @return array
+ */
+ public static function getAllPropertiesForNode( string $nodeTitleText ): array {
+ $ret = [];
+
+ $title = Title::newFromText( $nodeTitleText );
+ if ( !$title || !$title->isKnown() ) {
+ wfDebugLog( 'KnowledgeGraph', "Invalid or unknown node: '$nodeTitleText'" );
+ return [];
+ }
+
+ $apiParams = [
+ 'action' => 'smwbrowse',
+ 'format' => 'json',
+ 'browse' => 'subject',
+ 'params' => json_encode( [
+ 'subject' => $nodeTitleText,
+ 'ns' => $title->getNamespace(),
+ ] ),
+ ];
+
+ $request = new \FauxRequest( $apiParams, false );
+ $api = new \ApiMain( $request );
+ $api->execute();
+ $data = $api->getResult()->getResultData();
+
+ if ( empty( $data[ 'query' ][ 'data' ] ) ) {
+ wfDebugLog( 'KnowledgeGraph', "No properties returned from smwbrowse for '$nodeTitleText'" );
+ return [];
+ }
+
+ foreach ( $data['query']['data'] as $propertyEntry ) {
+ $propKey = $propertyEntry['property'] ?? null;
+ $direction = $propertyEntry['direction'] ?? 'direct';
+
+ if ( !$propKey ) {
+ continue;
+ }
+
+ if (
+ ( isset( self::$exclude ) && in_array( $propKey, self::$exclude ) ) ||
+ str_starts_with( $propKey, '_' ) ||
+ str_starts_with( $propKey, '___' ) ||
+ ctype_upper( str_replace( '_', '', $propKey ) )
+ ) {
+ continue;
+ }
+
+ $propKey = str_replace( '_', ' ', $propKey );
+
+ if ( $direction === 'inverse' ) {
+ $propKey = '-' . $propKey;
+ }
+
+ $ret[] = $propKey;
+ }
+
+ wfDebugLog( 'KnowledgeGraph', sprintf(
+ "getAllPropertiesForNode (smwbrowse): node=%s, properties=%d",
+ $nodeTitleText,
+ count( $ret )
+ ) );
+
+ return array_unique( $ret );
+ }
+
/**
* @param Title|MediaWiki\Title\Title $title $title
* @return string|null
@@ -403,6 +493,9 @@ public static function onOutputPageParserOutput( OutputPage $out, ParserOutput $
$out->addJsConfigVars( [
'knowledgegraphs' => json_encode( $data )
] );
+
+ // add the required JavaScript module if graphs are present
+ $out->addModules( 'ext.KnowledgeGraph' );
}
}
@@ -534,7 +627,16 @@ public static function setSemanticDataFromApi( Title $title, $onlyProperties, $d
return;
}
- if ( $depth > $maxDepth ) {
+ // If maxDepth is 0, only create the root node without loading SMW data
+ if ( $maxDepth === 0 ) {
+ self::$data[$titleText] = [
+ 'properties' => [],
+ 'categories' => [],
+ ];
+ return;
+ }
+
+ if ( $depth >= $maxDepth ) {
return;
}
@@ -640,8 +742,19 @@ public static function setSemanticDataFromApi( Title $title, $onlyProperties, $d
foreach ( $entry['dataitem'] ?? [] as $item ) {
if ( $item['type'] === 9 ) {
- $linkedTitle = explode( '#', $item['item'] )[0];
- $linkedTitle = $linkedTitle ? str_replace( '_', ' ', $linkedTitle ) : null;
+ $parts = explode( '#', $item['item'] );
+ $dbkey = $parts[0] ?? '';
+ $nsId = isset( $parts[1] ) && is_numeric( $parts[1] ) ? (int)$parts[1] : 0;
+
+ $namespaceInfo = MediaWiki\MediaWikiServices::getInstance()->getNamespaceInfo();
+ $nsName = $namespaceInfo->getCanonicalName( $nsId );
+
+ $linkedTitle = $dbkey;
+ if ( $nsName !== '' && $nsName !== false ) {
+ $linkedTitle = $nsName . ':' . $dbkey;
+ }
+
+ $linkedTitle = str_replace( '_', ' ', $linkedTitle );
if ( !$linkedTitle ) {
continue;
}
diff --git a/includes/api/KnowledgeGraphApiLoadNodes.php b/includes/api/KnowledgeGraphApiLoadNodes.php
index c3c846d..f3f2ca7 100644
--- a/includes/api/KnowledgeGraphApiLoadNodes.php
+++ b/includes/api/KnowledgeGraphApiLoadNodes.php
@@ -8,7 +8,6 @@
*/
use MediaWiki\Extension\KnowledgeGraph\Aliases\Title as TitleClass;
-use MediaWiki\MediaWikiServices;
class KnowledgeGraphApiLoadNodes extends ApiBase {
@@ -91,7 +90,7 @@ public function mustBePosted(): bool {
"_INST",
"_PPGR",
"_SUBP",
- "_SUBC"
+ "_SUBC",
];
/**
@@ -101,91 +100,27 @@ public function execute() {
$result = $this->getResult();
$params = $this->extractRequestParams();
$context = $this->getContext();
- $output = $context->getOutput();
\KnowledgeGraph::initSMW();
self::$SMWStore = \SMW\StoreFactory::getStore();
self::$SMWDataValueFactory = SMW\DataValueFactory::getInstance();
- $services = MediaWikiServices::getInstance();
- $urlUtils = $services->getUrlUtils();
- $httpRequestFactory = $services->getHttpRequestFactory();
-
- $scriptPath = $services->getMainConfig()->get( 'ScriptPath' );
- $server = $services->getMainConfig()->get( 'Server' );
- $apiUrl = $server . $scriptPath . '/api.php';
-
- $queryParams = [
- 'action' => 'query',
- 'list' => 'allpages',
- 'apnamespace' => 102,
- 'aplimit' => 'max',
- 'format' => 'json'
- ];
-
- $query = http_build_query( $queryParams );
- $response = $httpRequestFactory->get( "$apiUrl?$query", [], __METHOD__ );
- $data = json_decode( $response, true );
-
- $propertyTitles = array_column( $data['query']['allpages'], 'title' );
- $propertyNames = array_map( static function ( $title ) {
- return substr( $title, strrpos( $title, ':' ) + 1 );
- }, $propertyTitles );
-
- $params['properties'] = ( !empty( $params['properties'] ) ?
- json_decode( $params['properties'], true ) : [] );
-
$titles = explode( '|', $params['titles'] );
foreach ( $titles as $titleText ) {
$title_ = TitleClass::newFromText( $titleText );
-
- foreach ( $propertyNames as $propertyName ) {
- $propertyDI = \SMW\DIProperty::newFromUserLabel( $propertyName );
- $results = \KnowledgeGraph::getSubjectsByProperty( $propertyDI, $limit, 0, $titleText );
- if ( count( $results ) > 0 ) {
- $params['properties'][] = $propertyName;
- }
+ if ( !$title_ || !$title_->isKnown() ) {
+ continue;
}
- $subject = new \SMW\DIWikiPage( $title_->getDbKey(), $title_->getNamespace() );
- $semanticData = self::$SMWStore->getSemanticData( $subject );
-
- foreach ( $semanticData->getProperties() as $property ) {
- $key = $property->getKey();
-
- $typeID = $property->findPropertyTypeID();
-
- if ( in_array( $key, self::$exclude ) ) {
- continue;
- }
-
- $propertyDv = self::$SMWDataValueFactory->newDataValueByItem( $property, null );
- if ( !$property->isUserAnnotable() || !$propertyDv->isVisible() ) {
- continue;
- }
-
- $key = str_replace( '_', ' ', $property->getKey() );
-
- $params['properties'][] = $key;
-
- }
+ $listOfProps = \KnowledgeGraph::getAllPropertiesForNode( $titleText );
- $params['properties'] = array_unique( $params['properties'] );
-
- $params['properties'] = array_unique(
- array_merge(
- $params['properties'],
- array_map(
- fn ( $prop ) => '-' . $prop,
- $params['properties']
- )
- )
- );
-
- if ( $title_ && $title_->isKnown() ) {
- if ( !isset( self::$data[$title_->getFullText()] ) ) {
- \KnowledgeGraph::setSemanticDataFromApi( $title_, $params['properties'], 0, $params['depth'] );
- }
+ if ( !isset( self::$data[$title_->getFullText()] ) ) {
+ \KnowledgeGraph::setSemanticDataFromApi(
+ $title_,
+ $listOfProps,
+ 0,
+ $params['depth']
+ );
}
}
diff --git a/includes/specials/SpecialKnowledgeGraphDesigner.php b/includes/specials/SpecialKnowledgeGraphDesigner.php
index 42e615c..edfe734 100644
--- a/includes/specials/SpecialKnowledgeGraphDesigner.php
+++ b/includes/specials/SpecialKnowledgeGraphDesigner.php
@@ -68,7 +68,8 @@ public function execute( $par ) {
'knowledgegraphs' => json_encode( \KnowledgeGraph::$graphs ),
'KnowledgeGraphShowImages' => $GLOBALS['wgKnowledgeGraphShowImages'],
'KnowledgeGraphDisableCredits' => $GLOBALS['wgKnowledgeGraphDisableCredits'],
- 'wgKnowledgeGraphColorPalette' => $colors
+ 'wgKnowledgeGraphColorPalette' => $colors,
+ 'wgExtraNamespaces' => $GLOBALS['wgExtraNamespaces']
] );
$out->addHTML(
diff --git a/resources/KnowledgeGraph.css b/resources/KnowledgeGraph.css
index a3d3dce..ba25f79 100644
--- a/resources/KnowledgeGraph.css
+++ b/resources/KnowledgeGraph.css
@@ -10,7 +10,7 @@
border: 1px solid #ccc;
height: auto;
background: white;
- box-shadow: 0px 2px 18px 0px rgba(0,0,0,0.12);
+ box-shadow: 0px 2px 18px 0px rgba(0, 0, 0, 0.12);
}
.KnowledgeGraphPopupMenu ul {
@@ -24,14 +24,14 @@
padding: 8px;
list-style: none;
cursor: pointer;
-
}
+
.KnowledgeGraphPopupMenu li span {
margin-right: 8px;
}
.KnowledgeGraphPopupMenu li:hover {
- background: #e8e8e9;
+ background: #e8e8e9;
}
.KnowledgeGraphTable .vis-configuration-wrapper {
@@ -48,12 +48,12 @@
.OOUI-dialogs-non-modal .oo-ui-window {
border: 1px solid #a2a9b1;
- box-shadow: 0 0 4px 0 rgba( 0, 0, 0, 0.25 );
- position: relative;
+ box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
+ position: relative;
margin: 0 32px;
}
-.KnowledgeGraphTable .oo-ui-toolbar-position-top > .oo-ui-toolbar-bar {
+.KnowledgeGraphTable .oo-ui-toolbar-position-top>.oo-ui-toolbar-bar {
border-bottom: none;
box-shadow: none;
}
@@ -66,26 +66,26 @@
.kg-node-properties-menu li.kg-node-properties-menu-link-entry:hover,
.kg-node-properties-menu li.kg-node-properties-menu-property-entry:hover,
.kg-node-properties-menu li.kg-node-properties-menu-edge-entry:hover {
- background-color: #e0f0ff;
- cursor: pointer;
+ background-color: #e0f0ff;
+ cursor: pointer;
}
.kg-node-properties-menu {
- position: absolute;
- background: #fff;
- border: 1px solid #ccc;
- padding: 5px;
- list-style: none;
- z-index: 10000;
- max-height: 300px;
- overflow-y: auto;
- margin: 0;
- box-shadow: 0 4px 10px rgba(0,0,0,0.1);
- cursor: pointer;
+ position: absolute;
+ background: #fff;
+ border: 1px solid #ccc;
+ padding: 5px;
+ list-style: none;
+ z-index: 10000;
+ max-height: 300px;
+ overflow-y: auto;
+ margin: 0;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+ cursor: pointer;
}
.kg-node-properties-menu-property-entry-selected {
- font-style: italic;
- font-weight: bold;
- color: #2B7CE9;
-}
+ font-style: italic;
+ font-weight: bold;
+ color: #2B7CE9;
+}
\ No newline at end of file
diff --git a/resources/KnowledgeGraph.js b/resources/KnowledgeGraph.js
index 6c0dbbc..511c240 100644
--- a/resources/KnowledgeGraph.js
+++ b/resources/KnowledgeGraph.js
@@ -7,50 +7,70 @@
*/
KnowledgeGraph = function () {
- var Nodes;
- var Edges;
- var Data = {};
- var maxPropValueLength = 20;
- var Config;
- var Container;
- var Properties = {};
- // var ModelProperties = {};
- var SelectedNode = null;
- var TmpData;
- var Network;
- var PopupMenuId = 'knowledgegraphp-popup-menu';
- var InitialData;
- var ContainerOptions;
- var WindowManagerNonModal;
- var DialogCredits = 'dialog-credits';
- var PropColors = {};
- var Categories = {};
- var LegendDiv;
- var PropIdPropLabelMap = {};
- var nodePropertiesCache = {};
- const colors = mw.config.get('wgKnowledgeGraphColorPalette');
+ // instance bag
+ const self = {};
+
+ // instance state (defaults)
+ self.Nodes = null;
+ self.Edges = null;
+ self.Data = {};
+ self.maxPropValueLength = 20;
+ self.Config = null;
+ self.Container = null;
+ self.Properties = {};
+ self.SelectedNode = null;
+ self.TmpData = null;
+ self.Network = null;
+ self.PopupMenuId = 'knowledgegraphp-popup-menu';
+ self.InitialData = null;
+ self.ContainerOptions = null;
+ self.WindowManagerNonModal = null;
+ self.DialogCredits = 'dialog-credits';
+ self.PropColors = {};
+ self.Categories = {};
+ self.LegendDiv = null;
+ self.PropIdPropLabelMap = {};
+ self.nodePropertiesCache = {};
+ self.id = null;
+ self.colors = mw.config.get('wgKnowledgeGraphColorPalette');
function addLegendEntry(id, label, color) {
- if (!LegendDiv) return;
- if ($(LegendDiv).find('#' + id.replace(/ /g, '_')).length) {
+ if (!self.LegendDiv) return;
+
+ const safeId = id.replace(/ /g, '_');
+ const uniqueId = `${self.id}-${safeId}`;
+
+ if (self.LegendDiv.querySelector(`#${CSS.escape(uniqueId)}`)) {
return;
}
- let fontColor = KnowledgeGraphFunctions.getContrastColor(color); // koristi istu WCAG logiku
+
+ let fontColor = KnowledgeGraphFunctions.getContrastColor(color);
if (!fontColor) fontColor = '#000000';
-
- var container = document.createElement('button');
- container.className = 'legend-element-container';
- container.classList.add('btn', 'btn-outline-light');
- container.id = id.replace(/ /g, '_');
+
+ const container = document.createElement('button');
+ container.className = 'legend-element-container btn btn-outline-light';
+ container.id = uniqueId;
container.style.color = fontColor;
container.style.background = color;
container.innerHTML = label;
- container.innerHTML = id;
+ container.innerHTML = id;
container.dataset.active = true;
container.dataset.active_color = color;
- LegendDiv.append(container);
+ self.LegendDiv.append(container);
+ }
+
+ function removeLegendEntry(property) {
+ if (!self.LegendDiv) return;
+ // use instance-specific ID
+ const safeId = `${this.id}-${property.replace(/ /g, '_')}`;
+ const entry = this.LegendDiv.querySelector(`#${CSS.escape(safeId)}`);
+
+ if (entry) {
+ entry.remove();
+ console.debug(`Legend entry removed for ${property} in ${this.id}`);
+ }
}
function checkAndToogleId(id) {
@@ -58,46 +78,45 @@ KnowledgeGraph = function () {
}
function dispatchEvent_LegendClick(event, id) {
- var container = $(LegendDiv).find('#' + id.replace(/ /g, '_'))[0];
- if (container.dataset.active === 'true') {
- container.dataset.active = false;
+ if (!this.LegendDiv) return;
+
+ const safeId = `${this.id}-${id.replace(/ /g, '_')}`;
+ const container = this.LegendDiv.querySelector(`#${CSS.escape(safeId)}`);
+ if (!container) return;
+
+ const isActive = container.dataset.active === 'true';
+ container.dataset.active = (!isActive).toString();
+
+ if (isActive) {
container.style.background = '#FFFFFF';
-
- let bgColor = container.style.background;
- let fontColor = KnowledgeGraphFunctions.getContrastColor(bgColor);
- if (!fontColor) fontColor = '#000000';
+ const fontColor = KnowledgeGraphFunctions.getContrastColor(container.style.background) || '#000000';
container.style.color = fontColor;
-
} else {
- container.dataset.active = true;
container.style.background = container.dataset.active_color;
-
- let bgColor = container.style.background;
- let fontColor = KnowledgeGraphFunctions.getContrastColor(bgColor);
- if (!fontColor) fontColor = '#000000';
+ const fontColor = KnowledgeGraphFunctions.getContrastColor(container.style.background) || '#000000';
container.style.color = fontColor;
}
- var updateNodes = [];
- var visited = [];
+
+ const updateNodes = [];
+ const visited = [];
+ const self = this;
function toggleConnectedNodes(nodeId) {
- if (visited.indexOf(nodeId) !== -1) {
- return;
- }
+ if (visited.includes(nodeId)) return;
visited.push(nodeId);
- var connectedNodes = Network.getConnectedNodes(nodeId);
-
- for (var nodeId_ of connectedNodes) {
- var connectedEdgesIds = Network.getConnectedEdges(nodeId_);
- var connectedEdges = Edges.get(connectedEdgesIds);
+ const connectedNodes = self.Network.getConnectedNodes(nodeId);
+ for (const nodeId_ of connectedNodes) {
+ const connectedEdgesIds = self.Network.getConnectedEdges(nodeId_);
+ const connectedEdges = self.Edges.get(connectedEdgesIds);
- var found = false;
- connectedEdges.forEach((edge) => {
+ let found = false;
+ for (const edge of connectedEdges) {
if (edge.to === nodeId || edge.from === nodeId) {
found = true;
+ break;
}
- });
+ }
if (!found) {
updateNodes.push({
@@ -109,12 +128,17 @@ KnowledgeGraph = function () {
}
}
- Nodes.forEach((node) => {
- var idValue = checkAndToogleId(node.id);
- if (PropIdPropLabelMap[id] === undefined) {
- PropIdPropLabelMap[id] = [];
+ this.Nodes.forEach((node) => {
+ const idValue = checkAndToogleId(node.id);
+
+ if (this.PropIdPropLabelMap[id] === undefined) {
+ this.PropIdPropLabelMap[id] = [];
}
- if (PropIdPropLabelMap[id].indexOf(idValue) !== -1 || PropIdPropLabelMap[id].indexOf(node.id) !== -1) {
+
+ if (
+ this.PropIdPropLabelMap[id].includes(idValue) ||
+ this.PropIdPropLabelMap[id].includes(node.id)
+ ) {
updateNodes.push({
id: node.id,
hidden: container.dataset.active === 'true' ? false : true,
@@ -123,22 +147,21 @@ KnowledgeGraph = function () {
}
});
- Nodes.update(updateNodes);
+ this.Nodes.update(updateNodes);
}
function deleteNode(nodeId) {
- var children = Network.getConnectedNodes(nodeId);
- children = children.filter(
- (x) => !(x in Data) || Network.getConnectedNodes(x).length === 1
+ const children = self.Network.getConnectedNodes(nodeId).filter(
+ (x) => !(x in self.Data) || self.Network.getConnectedNodes(x).length === 1
);
children.push(nodeId);
- for (var nodeId of children) {
- Edges.remove(Network.getConnectedEdges(nodeId));
+ for (const nid of children) {
+ self.Edges.remove(self.Network.getConnectedEdges(nid));
}
- Nodes.remove(children);
- for (var nodeId of children) {
- delete Data[nodeId];
+ self.Nodes.remove(children);
+ for (const nid of children) {
+ delete self.Data[nid];
}
}
@@ -148,7 +171,7 @@ KnowledgeGraph = function () {
action: 'knowledgegraph-load-nodes',
titles: obj.title,
depth: obj.depth,
- properties: JSON.stringify(Config['properties']),
+ properties: JSON.stringify(self.Config['properties']),
};
} else if (obj.properties !== null) {
if (obj.properties === undefined) {
@@ -161,7 +184,7 @@ KnowledgeGraph = function () {
depth: obj.depth,
limit: obj.limit,
offset: obj.offset,
- inversePropsIncluded: inversePropsIncluded
+ inversePropsIncluded: inversePropsIncluded,
};
} else if (obj.categories !== null) {
var payload = {
@@ -248,27 +271,25 @@ KnowledgeGraph = function () {
}
function addArticleNode(data, label, options, typeID) {
- if (Nodes.get(label) !== null) {
+ if (self.Nodes.get(label) !== null) {
return;
}
let cleanLabel = label.split('#')[0];
- var nodeConfig = jQuery.extend(
- JSON.parse(JSON.stringify(Config.graphOptions.nodes)),
- label in Config.propertyOptions ? Config.propertyOptions[label] : {},
+ const nodeConfig = jQuery.extend(
+ JSON.parse(JSON.stringify(self.Config.graphOptions.nodes)),
+ label in self.Config.propertyOptions ? self.Config.propertyOptions[label] : {},
{
id: label,
label:
- cleanLabel.length <= maxPropValueLength
+ cleanLabel.length <= self.maxPropValueLength
? cleanLabel
: wrapLabel(cleanLabel, 20),
shape: 'box',
- font: jQuery.extend(
- {},
- Config.graphOptions.nodes.font,
- { size: Config.graphOptions.nodes.font.size || 30 }
- ),
+ font: jQuery.extend({}, self.Config.graphOptions.nodes.font, {
+ size: self.Config.graphOptions.nodes.font.size || 30,
+ }),
typeID: typeID || 9,
// https://visjs.github.io/vis-network/examples/network/other/popups.html
@@ -285,6 +306,7 @@ KnowledgeGraph = function () {
if (data[label] === null) {
nodeConfig.opacity = 0.5;
+ nodeConfig.shapeProperties = nodeConfig.shapeProperties || {};
nodeConfig.shapeProperties.borderDashes = [5, 5];
}
@@ -297,12 +319,12 @@ KnowledgeGraph = function () {
nodeConfig.image = data[label].src;
}
- Nodes.add(nodeConfig);
+ self.Nodes.add(nodeConfig);
}
function createNodes(data) {
- for (var label in data) {
- if (label in Data && Data[label] !== null) {
+ for (const label in data) {
+ if (label in self.Data && self.Data[label] !== null) {
continue;
}
@@ -312,34 +334,34 @@ KnowledgeGraph = function () {
continue;
}
- if (!(label in Categories)) {
- Categories[label] = [];
+ if (!(label in self.Categories)) {
+ self.Categories[label] = [];
}
- for (var i in data[label].categories) {
- var category = data[label].categories[i];
- if (Categories[label].indexOf(category) === -1) {
- Categories[label].push(category);
+ for (const i in data[label].categories) {
+ const category = data[label].categories[i];
+ if (self.Categories[label].indexOf(category) === -1) {
+ self.Categories[label].push(category);
}
}
- for (var i in data[label].properties) {
- var property = data[label].properties[i];
+ for (const i in data[label].properties) {
+ const property = data[label].properties[i];
- if (!(property.canonicalLabel in PropColors)) {
- if (colors && colors.length > 0) {
+ if (!(property.canonicalLabel in self.PropColors)) {
+ if (self.colors && self.colors.length > 0) {
// use d3 palette colors defined in wgKnowledgeGraphColorPalette
- PropColors[property.canonicalLabel] = KnowledgeGraphFunctions.colorForPropertyLabel(
+ self.PropColors[property.canonicalLabel] = KnowledgeGraphFunctions.colorForPropertyLabel(
property.canonicalLabel,
- colors,
- PropColors
+ self.colors,
+ self.PropColors
);
} else {
// use random HSL colors if no palette defined
let color_;
function colorExists() {
- for (let j in PropColors) {
- if (PropColors[j] === color_) {
+ for (const j in self.PropColors) {
+ if (self.PropColors[j] === color_) {
return true;
}
}
@@ -348,22 +370,22 @@ KnowledgeGraph = function () {
do {
color_ = KnowledgeGraphFunctions.randomHSL();
} while (colorExists());
- PropColors[property.canonicalLabel] = color_;
+ self.PropColors[property.canonicalLabel] = color_;
}
}
- var options =
- property.preferredLabel in Config.propertyOptions
- ? Config.propertyOptions[property.preferredLabel]
- : property.canonicalLabel in Config.propertyOptions
- ? Config.propertyOptions[property.canonicalLabel]
+ let options =
+ property.preferredLabel in self.Config.propertyOptions
+ ? self.Config.propertyOptions[property.preferredLabel]
+ : property.canonicalLabel in self.Config.propertyOptions
+ ? self.Config.propertyOptions[property.canonicalLabel]
: {};
if ('nodes' in options) {
options = options.nodes;
}
if (!('color' in options)) {
- const nodeColor = PropColors[property.canonicalLabel];
+ const nodeColor = self.PropColors[property.canonicalLabel];
const textColor = KnowledgeGraphFunctions.getContrastColor(nodeColor);
options.color = {
@@ -371,71 +393,58 @@ KnowledgeGraph = function () {
border: '#333',
highlight: {
background: nodeColor,
- border: '#000'
- }
+ border: '#000',
+ },
};
- // ensure readable font color when node background is dark
+ // readable font color when background dark
options.font = Object.assign({}, options.font, {
- color: textColor
+ color: textColor,
});
}
- var legendLabel =
+ const legendLabel =
property.preferredLabel !== ''
? property.preferredLabel
: property.canonicalLabel;
- if (!(legendLabel in PropIdPropLabelMap)) {
- PropIdPropLabelMap[legendLabel] = [];
+ if (!(legendLabel in self.PropIdPropLabelMap)) {
+ self.PropIdPropLabelMap[legendLabel] = [];
}
- var propLabel =
- legendLabel +
- (!Config['show-property-type']
- ? ''
- : ' (' + property.typeLabel + ')');
-
- if (Config['properties-panel']) {
- addLegendEntry(
- property.canonicalLabel,
- legendLabel,
- PropColors[property.canonicalLabel]
- );
+ const propLabel =
+ legendLabel + (!self.Config['show-property-type'] ? '' : ' (' + property.typeLabel + ')');
+
+ if (self.Config['properties-panel']) {
+ addLegendEntry(property.canonicalLabel, legendLabel, self.PropColors[property.canonicalLabel]);
}
switch (property.typeId) {
case '_wpg':
- for (var ii in property.values) {
- var targetLabel = property.values[ii].value;
- PropIdPropLabelMap[legendLabel].push(targetLabel);
+ for (const ii in property.values) {
+ const targetLabel = property.values[ii].value;
+ self.PropIdPropLabelMap[legendLabel].push(targetLabel);
- var from = property.inverse ? targetLabel : label;
- var to = property.inverse ? label : targetLabel;
+ const from = property.inverse ? targetLabel : label;
+ const to = property.inverse ? label : targetLabel;
- let edgeId = KnowledgeGraphFunctions.makeEdgeId(from, to, property.canonicalLabel, 9, Nodes);
+ const edgeId = KnowledgeGraphFunctions.makeEdgeId(from, to, property.canonicalLabel, 9, self.Nodes);
- var edgeConfig = jQuery.extend(
- JSON.parse(JSON.stringify(Config.graphOptions.edges)),
+ const edgeConfig = jQuery.extend(
+ JSON.parse(JSON.stringify(self.Config.graphOptions.edges)),
{
id: edgeId,
from: from,
to: to,
label: propLabel,
group: label,
- arrows: {
- to: { enabled: true }
- }
+ arrows: { to: { enabled: true } },
}
);
- // Edges.add(edgeConfig);
- graphModel.addEdge(edgeConfig);
+ self.graphModel.addEdge(edgeConfig);
- if (
- property.values[ii].src &&
- mw.config.get('KnowledgeGraphShowImages') === true
- ) {
+ if (property.values[ii].src && mw.config.get('KnowledgeGraphShowImages') === true) {
options.shape = 'image';
options.image = property.values[ii].src;
}
@@ -445,66 +454,67 @@ KnowledgeGraph = function () {
break;
default:
- const seen = new Set();
- for (const { value: targetLabel } of property.values) {
- if (seen.has(targetLabel)) continue;
- seen.add(targetLabel);
-
- const typeId = property.typeId === '_txt' ? 2 : property.typeId;
- const valueId = KnowledgeGraphFunctions.makeNodeId(targetLabel, typeId);
- const edgeLabel = property.canonicalLabel || propLabel;
-
- PropIdPropLabelMap[legendLabel].push(valueId);
-
- const edgeId = KnowledgeGraphFunctions.makeEdgeId(label, valueId, edgeLabel);
- Edges.add({
- id: edgeId,
- from: label,
- to: valueId,
- label: propLabel,
- group: label,
- });
+ {
+ const seen = new Set();
+ for (const { value: targetLabel } of property.values) {
+ if (seen.has(targetLabel)) continue;
+ seen.add(targetLabel);
+
+ const typeId = property.typeId === '_txt' ? 2 : property.typeId;
+ const valueId = KnowledgeGraphFunctions.makeNodeId(targetLabel, typeId);
+ const edgeLabel = property.canonicalLabel || propLabel;
+
+ self.PropIdPropLabelMap[legendLabel].push(valueId);
+
+ const edgeId = KnowledgeGraphFunctions.makeEdgeId(label, valueId, edgeLabel);
+ self.Edges.add({
+ id: edgeId,
+ from: label,
+ to: valueId,
+ label: propLabel,
+ group: label,
+ });
- if (!Nodes.get(valueId)) {
- const displayLabel = targetLabel.length <= maxPropValueLength
- ? targetLabel
- : wrapLabel(targetLabel, 20);
-
- Nodes.add(
- jQuery.extend({}, options, {
- id: valueId,
- label: displayLabel,
- typeID: typeId,
- })
- );
+ if (!self.Nodes.get(valueId)) {
+ const displayLabel = targetLabel.length <= self.maxPropValueLength
+ ? targetLabel
+ : wrapLabel(targetLabel, 20);
+
+ self.Nodes.add(
+ jQuery.extend({}, options, {
+ id: valueId,
+ label: displayLabel,
+ typeID: typeId,
+ })
+ );
+ }
}
}
- }
}
}
- Data = jQuery.extend(Data, data);
+ }
+ self.Data = jQuery.extend(self.Data, data);
}
function HideNodesRec(nodeId) {
- var children = Network.getConnectedNodes(nodeId);
- // children = children.filter((x) => excludedIds.indexOf(x) === -1);
- var updateNodes = [];
- for (var nodeId_ of children) {
- if (!(nodeId_ in Data)) {
+ const children = self.Network.getConnectedNodes(nodeId);
+ const updateNodes = [];
+ for (const nodeId_ of children) {
+ if (!(nodeId_ in self.Data)) {
updateNodes.push({
id: nodeId_,
- hidden: !Nodes.get(nodeId_).hidden,
+ hidden: !self.Nodes.get(nodeId_).hidden,
});
}
}
- Nodes.update(updateNodes);
+ self.Nodes.update(updateNodes);
}
function getDialogActionProcessCallback(thisDialog, getActionProcess, action) {
switch (action) {
case 'delete':
if (confirm(mw.msg('knowledgegraph-delete-node-confirm'))) {
- deleteNode(SelectedNode);
+ deleteNode(self.SelectedNode);
return new OO.ui.Process(function () {
thisDialog.close({ action: action });
});
@@ -513,21 +523,21 @@ KnowledgeGraph = function () {
case 'done':
return new OO.ui.Process(function () {
thisDialog.close({ action: action }).then(function () {
- // createNodes(TmpData);
+ // createNodes(self.TmpData);
});
- createNodes(TmpData);
- TmpData = {};
+ createNodes(self.TmpData);
+ self.TmpData = {};
});
case 'continue':
return getActionProcess
.call(thisDialog, action)
.next(function () {
return new Promise((resolve, reject) => {
- var selectedTab = thisDialog.indexLayout.getCurrentTabPanelName();
- var titleValue = null;
- var properties = null;
- var categories = null;
- var depth, limit, offset;
+ const selectedTab = thisDialog.indexLayout.getCurrentTabPanelName();
+ let titleValue = null;
+ let properties = null;
+ let categories = null;
+ let depth, limit, offset;
switch (selectedTab) {
case 'by-article':
@@ -537,11 +547,16 @@ KnowledgeGraph = function () {
resolve();
return;
}
- var titleFullText = thisDialog.titleInputWidget
- .getMWTitle()
- .getPrefixedText();
+ let ns = parseInt(thisDialog.namespaceDropdown.getValue() || 0, 10);
+ let titleObj = mw.Title.newFromText(titleValue, ns);
- if (titleFullText in Data) {
+ if (!titleObj) {
+ resolve();
+ return;
+ }
+ let titleFullText = titleObj.getPrefixedText();
+
+ if (titleFullText in self.Data) {
thisDialog.actions.setMode('existing-node');
thisDialog.initializeResultsPanel('existing-node');
resolve();
@@ -563,14 +578,14 @@ KnowledgeGraph = function () {
const newTitles = [];
for (let i = 0; i < titles.length; i++) {
- const titleObj = mw.Title.newFromText( titles[i] );
+ const titleObj = mw.Title.newFromText(titles[i]);
if (!titleObj) continue;
const fullTitle = titleObj.getPrefixedText();
- if (fullTitle in Data) {
- existingTitles.push( fullTitle );
+ if (fullTitle in self.Data) {
+ existingTitles.push(fullTitle);
} else {
- newTitles.push( fullTitle );
+ newTitles.push(fullTitle);
}
}
@@ -610,24 +625,19 @@ KnowledgeGraph = function () {
offset: parseInt(offset),
})
.then(function (data) {
- // Properties = data[titleFullText];
- TmpData = data;
+ self.TmpData = data;
+ let mode;
if (selectedTab === 'by-article') {
- var properties = data[titleFullText];
- var mode = Object.keys(properties).length
- ? 'show-results'
- : 'no-results';
+ let ns = parseInt(thisDialog.namespaceDropdown.getValue() || 0, 10);
+ let titleObj = mw.Title.newFromText(titleValue, ns);
+ titleFullText = titleObj ? titleObj.getPrefixedText() : titleValue;
+
+ let properties_ = data[titleFullText] || data[titleValue] || {};
+ mode = Object.keys(properties_).length ? 'show-results' : 'no-results';
} else {
- var mode = Object.keys(data).length
- ? 'show-results'
- : 'no-results';
+ mode = Object.keys(data).length ? 'show-results' : 'no-results';
}
- thisDialog.initializeResultsPanel(
- mode,
- selectedTab,
- data,
- selectedTab === 'by-article' ? titleFullText : null
- );
+ thisDialog.initializeResultsPanel(mode, selectedTab, data, selectedTab === 'by-article' ? titleFullText : null);
thisDialog.actions.setMode(mode);
resolve();
})
@@ -646,173 +656,92 @@ KnowledgeGraph = function () {
}
function getDialogOnSetupCallback(thisDialog, data) {
- var self = thisDialog;
if (data && data.nodeId) {
- SelectedNode = data.nodeId;
- var mode = 'edit';
- self.initializeResultsPanel(mode);
- self.actions.setMode(mode);
+ self.SelectedNode = data.nodeId;
+ const mode = 'edit';
+ thisDialog.initializeResultsPanel(mode);
+ thisDialog.actions.setMode(mode);
} else {
- self.actions.setMode('select');
+ thisDialog.actions.setMode('select');
}
}
- function getDialogInitializeResultsPanel(
- thisDialog,
- mode,
- selectedTab,
- data,
- titleFullText
- ) {
+ function getDialogInitializeResultsPanel(thisDialog, mode, selectedTab, data, titleFullText) {
+ let $el;
if (mode === 'no-results') {
- var msg = mw.msg(
- selectedTab === 'by-article'
- ? 'knowledgegraph-dialog-results-no-properties'
- : 'knowledgegraph-dialog-results-no-articles'
- );
-
+ const msg = mw.msg(selectedTab === 'by-article' ? 'knowledgegraph-dialog-results-no-properties' : 'knowledgegraph-dialog-results-no-articles');
$el = $('
' + msg + '');
} else if (mode === 'existing-node') {
- $el = $(
- '
' +
- mw.msg('knowledgegraph-dialog-results-existing-node') +
- ''
- );
+ $el = $('
' + mw.msg('knowledgegraph-dialog-results-existing-node') + '');
} else {
$el = $('
');
switch (selectedTab) {
case 'by-article':
- thisDialog.panelB.$element.append(
- '' +
- mw.msg('knowledgegraph-dialog-results-has-properties') +
- '
'
- );
- var properties = data[titleFullText].properties;
- for (var i in properties) {
- var url = mw.config.get('wgArticlePath').replace('$1', i);
-
- $el.append(
- $(
- '- ' +
- (properties[i].preferredLabel !== ''
- ? properties[i].preferredLabel
- : properties[i].canonicalLabel) +
- ' (' +
- properties[i].typeLabel +
- ')' +
- '
'
- )
- );
+ thisDialog.panelB.$element.append('' + mw.msg('knowledgegraph-dialog-results-has-properties') + '
');
+ const properties = data[titleFullText].properties;
+ for (const i in properties) {
+ const url = mw.config.get('wgArticlePath').replace('$1', i);
+ $el.append($('- ' + (properties[i].preferredLabel !== '' ? properties[i].preferredLabel : properties[i].canonicalLabel) + ' (' + properties[i].typeLabel + ')
'));
}
break;
case 'by-properties':
- // mw.msg
- if (Object.keys(data).some((i) => !(i in Data) && data[i] !== null)) {
- thisDialog.panelB.$element.append(
- '' + mw.msg('knowledgegraph-dialog-results-importing-nodes') + '
'
- );
-
- var $newList = $('');
- for (var i in data) {
- if (!(i in Data) && data[i] !== null) {
- var url = mw.config.get('wgArticlePath').replace('$1', i);
- $newList.append(
- $(
- '- ' +
- i +
- '
'
- )
- );
+ if (Object.keys(data).some((i) => !(i in self.Data) && data[i] !== null)) {
+ thisDialog.panelB.$element.append('' + mw.msg('knowledgegraph-dialog-results-importing-nodes') + '
');
+
+ const $newList = $('');
+ for (const i in data) {
+ if (!(i in self.Data) && data[i] !== null) {
+ const url = mw.config.get('wgArticlePath').replace('$1', i);
+ $newList.append($('- ' + i + '
'));
}
}
thisDialog.panelB.$element.append($newList);
}
- if (
- thisDialog._skippedTitles &&
- thisDialog._skippedTitles.length > 0
- ) {
- thisDialog.panelB.$element.append(
- '' + mw.msg('knowledgegraph-dialog-results-skipped-existing') + '
'
- );
-
- var $skippedList = $('');
+ if (thisDialog._skippedTitles && thisDialog._skippedTitles.length > 0) {
+ thisDialog.panelB.$element.append('' + mw.msg('knowledgegraph-dialog-results-skipped-existing') + '
');
+ const $skippedList = $('');
thisDialog._skippedTitles.forEach(function (title) {
- var url = mw.config.get('wgArticlePath').replace('$1', title);
- $skippedList.append(
- $(
- '- ' +
- title +
- '
'
- )
- );
+ const url = mw.config.get('wgArticlePath').replace('$1', title);
+ $skippedList.append($('- ' + title + '
'));
});
thisDialog.panelB.$element.append($skippedList);
}
break;
case 'by-categories':
- // mw.msg
- thisDialog.panelB.$element.append(
- '' +
- mw.msg('knowledgegraph-dialog-results-importing-nodes') +
- '
'
- );
-
- var $el = $('