From 6ae00e49732ded39c69c2b8b2291f1654afae0d4 Mon Sep 17 00:00:00 2001 From: Simon Huber Date: Sun, 15 Sep 2024 11:49:09 +0200 Subject: [PATCH] Improve View Widget Refreshing (#8135) * Update view.js * add viewhandler base class and view subclasses * simplify render methods of wikified views * use fakewidget's text for rendering * simplify wikified refresh methods * remove unneeded self=this * pass mode to makeTranscludeWidget * update view widget * update view widget --- core/modules/widgets/view.js | 434 +++++++++++++++++++++++++---------- 1 file changed, 314 insertions(+), 120 deletions(-) diff --git a/core/modules/widgets/view.js b/core/modules/widgets/view.js index 070836bff..ba0e8e989 100755 --- a/core/modules/widgets/view.js +++ b/core/modules/widgets/view.js @@ -18,6 +18,89 @@ var ViewWidget = function(parseTreeNode,options) { this.initialise(parseTreeNode,options); }; +var ViewHandler = function(widget) { + this.wiki = widget.wiki; + this.widget = widget; + this.document = widget.document; +}; + +/* +Base ViewHandler render method +*/ +ViewHandler.prototype.render = function(parent,nextSibling) { + this.text = this.getValue(); + this.createTextNode(parent,nextSibling); +}; + +/* +Base ViewHandler render method for wikified views +*/ +ViewHandler.prototype.renderWikified = function(parent,nextSibling) { + this.createFakeWidget(); + this.text = this.getValue(); + this.createWikifiedTextNode(parent,nextSibling); +}; + +/* +ViewHandler method to create a simple text node +*/ +ViewHandler.prototype.createTextNode = function(parent,nextSibling) { + if(this.text) { + var textNode = this.document.createTextNode(this.text); + parent.insertBefore(textNode,nextSibling); + this.widget.domNodes.push(textNode); + } else { + this.widget.makeChildWidgets(); + this.widget.renderChildren(parent,nextSibling); + } +}; + +/* +ViewHandler method to always create a text node, even if there's no text +*/ +ViewHandler.prototype.createWikifiedTextNode = function(parent,nextSibling) { + var textNode = this.document.createTextNode(this.text || ""); + parent.insertBefore(textNode,nextSibling); + this.widget.domNodes.push(textNode); +}; + +/* +ViewHandler method to create a fake widget used by wikified views +*/ +ViewHandler.prototype.createFakeWidget = function() { + this.fakeWidget = this.wiki.makeTranscludeWidget(this.widget.viewTitle,{ + document: $tw.fakeDocument, + field: this.widget.viewField, + index: this.widget.viewIndex, + parseAsInline: this.widget.viewMode !== "block", + mode: this.widget.viewMode === "block" ? "block" : "inline", + parentWidget: this.widget, + subTiddler: this.widget.viewSubTiddler + }); + this.fakeNode = $tw.fakeDocument.createElement("div"); + this.fakeWidget.makeChildWidgets(); + this.fakeWidget.render(this.fakeNode,null); +}; + +ViewHandler.prototype.refreshWikified = function(changedTiddlers) { + var refreshed = this.fakeWidget.refresh(changedTiddlers); + if(refreshed) { + var newText = this.getValue(); + if(newText !== this.text) { + this.widget.domNodes[0].textContent = newText; + this.text = newText; + } + } + return refreshed; +}; + +/* +Base ViewHandler refresh method +*/ +ViewHandler.prototype.refresh = function(changedTiddlers) { + return false; +}; + /* Inherit from the base widget class */ @@ -30,14 +113,8 @@ ViewWidget.prototype.render = function(parent,nextSibling) { this.parentDomNode = parent; this.computeAttributes(); this.execute(); - if(this.text) { - var textNode = this.document.createTextNode(this.text); - parent.insertBefore(textNode,nextSibling); - this.domNodes.push(textNode); - } else { - this.makeChildWidgets(); - this.renderChildren(parent,nextSibling); - } + this.view = this.getView(this.viewFormat); + this.view.render(parent,nextSibling); }; /* @@ -52,49 +129,238 @@ ViewWidget.prototype.execute = function() { this.viewFormat = this.getAttribute("format","text"); this.viewTemplate = this.getAttribute("template",""); this.viewMode = this.getAttribute("mode","block"); - switch(this.viewFormat) { - case "htmlwikified": - this.text = this.getValueAsHtmlWikified(this.viewMode); - break; - case "plainwikified": - this.text = this.getValueAsPlainWikified(this.viewMode); - break; - case "htmlencodedplainwikified": - this.text = this.getValueAsHtmlEncodedPlainWikified(this.viewMode); - break; - case "htmlencoded": - this.text = this.getValueAsHtmlEncoded(); - break; - case "htmltextencoded": - this.text = this.getValueAsHtmlTextEncoded(); - break; - case "urlencoded": - this.text = this.getValueAsUrlEncoded(); - break; - case "doubleurlencoded": - this.text = this.getValueAsDoubleUrlEncoded(); - break; - case "date": - this.text = this.getValueAsDate(this.viewTemplate); - break; - case "relativedate": - this.text = this.getValueAsRelativeDate(); - break; - case "stripcomments": - this.text = this.getValueAsStrippedComments(); - break; - case "jsencoded": - this.text = this.getValueAsJsEncoded(); - break; - default: // "text" - this.text = this.getValueAsText(); - break; - } }; /* -The various formatter functions are baked into this widget for the moment. Eventually they will be replaced by macro functions +Initialise the view subclasses */ +ViewWidget.prototype.getView = function(format) { + var View = this.initialiseView(); + View.prototype = Object.create(ViewHandler.prototype); + switch(format) { + case "htmlwikified": + View = this.initialiseHTMLWikifiedView(View); + break; + case "plainwikified": + View = this.initialisePlainWikifiedView(View); + break; + case "htmlencodedplainwikified": + View = this.initialiseHTMLEncodedPlainWikifiedView(View); + break; + case "htmlencoded": + View = this.initialiseHTMLEncodedView(View); + break; + case "htmltextencoded": + View = this.initialiseHTMLTextEncodedView(View); + break; + case "urlencoded": + View = this.initialiseURLEncodedView(View); + break; + case "doubleurlencoded": + View = this.initialiseDoubleURLEncodedView(View); + break; + case "date": + View = this.initialiseDateView(View); + break; + case "relativedate": + View = this.initialiseRelativeDateView(View); + break; + case "stripcomments": + View = this.initialiseStripCommentsView(View); + break; + case "jsencoded": + View = this.initialiseJSEncodedView(View); + break; + default: // "text" + View = this.initialiseTextView(View); + break; + }; + return new View(this); +}; + +/* +Return the function to intitialise the view subclass +*/ +ViewWidget.prototype.initialiseView = function() { + return function(widget) { + ViewHandler.call(this,widget); + }; +}; + +/* +Initialise HTML wikified view methods +*/ +ViewWidget.prototype.initialiseHTMLWikifiedView = function(View) { + + View.prototype.render = function(parent,nextSibling) { + this.renderWikified(parent,nextSibling); + }; + + View.prototype.getValue = function() { + return this.fakeNode.innerHTML; + }; + + View.prototype.refresh = function(changedTiddlers) { + return this.refreshWikified(changedTiddlers); + }; + return View; +}; + +/* +Initialise plain wikified view methods +*/ +ViewWidget.prototype.initialisePlainWikifiedView = function(View) { + + View.prototype.render = function(parent,nextSibling) { + this.renderWikified(parent,nextSibling); + }; + + View.prototype.getValue = function() { + return this.fakeNode.textContent; + }; + + View.prototype.refresh = function(changedTiddlers) { + return this.refreshWikified(changedTiddlers); + }; + return View; +}; + +/* +Initialise HTML encoded plain wikified methods +*/ +ViewWidget.prototype.initialiseHTMLEncodedPlainWikifiedView = function(View) { + + View.prototype.render = function(parent,nextSibling) { + this.renderWikified(parent,nextSibling); + }; + + View.prototype.getValue = function() { + return $tw.utils.htmlEncode(this.fakeNode.textContent); + }; + + View.prototype.refresh = function(changedTiddlers) { + return this.refreshWikified(changedTiddlers); + }; + return View; +}; + +/* +Initialise HTML encoded mehods +*/ +ViewWidget.prototype.initialiseHTMLEncodedView = function(View) { + var self = this; + View.prototype.getValue = function() { + return $tw.utils.htmlEncode(self.getValueAsText()); + }; + return View; +}; + +/* +Initialise HTML text encoded mehods +*/ +ViewWidget.prototype.initialiseHTMLTextEncodedView = function(View) { + var self = this; + View.prototype.getValue = function() { + return $tw.utils.htmlTextEncode(self.getValueAsText()); + }; + return View; +}; + +/* +Initialise URL encoded mehods +*/ +ViewWidget.prototype.initialiseURLEncodedView = function(View) { + var self = this; + View.prototype.getValue = function() { + return $tw.utils.encodeURIComponentExtended(self.getValueAsText()); + }; + return View; +}; + +/* +Initialise double URL encoded mehods +*/ +ViewWidget.prototype.initialiseDoubleURLEncodedView = function(View) { + var self = this; + View.prototype.getValue = function() { + return $tw.utils.encodeURIComponentExtended($tw.utils.encodeURIComponentExtended(self.getValueAsText())); + }; + return View; +}; + +/* +Initialise date mehods +*/ +ViewWidget.prototype.initialiseDateView = function(View) { + var self = this; + View.prototype.getValue = function(format) { + format = format || "YYYY MM DD 0hh:0mm"; + var value = $tw.utils.parseDate(self.getValue()); + if(value && $tw.utils.isDate(value) && value.toString() !== "Invalid Date") { + return $tw.utils.formatDateString(value,format); + } else { + return ""; + } + }; + return View; +}; + +/* +Initialise relative date mehods +*/ +ViewWidget.prototype.initialiseRelativeDateView = function(View) { + var self = this; + View.prototype.getValue = function(format) { + var value = $tw.utils.parseDate(self.getValue()); + if(value && $tw.utils.isDate(value) && value.toString() !== "Invalid Date") { + return $tw.utils.getRelativeDate((new Date()) - (new Date(value))).description; + } else { + return ""; + } + }; + return View; +}; + +/* +Initialise stripcomments mehods +*/ +ViewWidget.prototype.initialiseStripCommentsView = function(View) { + var self = this; + View.prototype.getValue = function() { + var lines = self.getValueAsText().split("\n"), + out = []; + for(var line=0; line