Use Knockout extend to overwrite function

Hi,

I like to change the behavior of the GUI (here the file browser). Actually I changed the HTML code generated to exchange the function used (see replaceAll to exchange enableAdditionalData with slicerEnableAdditionalData). This works fine but I see issues if another plugin does the same. So the RegEx will not match anymore for one of the plugins.

I did some research and tried to overwrite the function but it is not set to writeable, also I tried to use extend function of Knckout but I cannot get it running. As I am a JavaScript noop perhaps somebody can advise how to implement it at best.

This is working fine:

    self.onBeforeBinding = function() {
      // inject filament metadate into template
      if (self.settingsViewModel.settings.plugins.SlicerEstimator.add_slicer_metadata() == true) {
        $("#files_template_machinecode").text(function () {
          let return_value = $(this).text();
          let regex = /<div class="additionalInfo hide"/mi;
          return_value = return_value.replace(regex, '<div class="additionalInfo hide" data-bind="html: $root.get_slicer_data($data)"></div> <div class="additionalInfo hide"');
          return_value = return_value.replaceAll("$root.enableAdditionalData($data)", "$root.slicerEnableAdditionalData($data)");
          return return_value
        });
      }
    };

This is tried to extend (small example which should override the function with itself....

    ko.extenders.slicerEnableAdditionalData = function(target, option) {
      let result = ko.pureComputed(function () {
         return target();
      });
      return result;
    };


    // Add the new extend
    self.filesViewModel.enableAdditionalData =
    self.filesViewModel.enableAdditionalData.extend({
      slicerEnableAdditionalData: gettext("Based on information added by the slicer.")});

Full code is available here: GitHub - NilsRo/OctoPrint-SlicerEstimator at Development

Cheers,
Nils

Ha. :slight_smile: Get it running with these:

    
    // Overwrite the enableAdditionalData function to handle available metadata
    self.filesViewModel.slicerEnableAdditionalData = function(data) {      
      if (data.slicer != null && Object.keys(data.slicer).length > 0) {
          return true;
      } else {
          return self.filesViewModel.enableAdditionalData(data);
      }
    };  
    self.filesViewModel.enableAdditionalData = self.filesViewModel.slicerEnableAdditionalData;

Sometimes it is good to write it down somewhere.... :innocent:

Curious if your approach is just replacing the existing metadata or just overwriting it in the UI? Would be concerned if other plugins do the same like you mentioned, and you could be introducing a conflict. I've manipulated the data in these areas a couple of ways. Also, your screenshot reminded me of PrintTimeGenius so I looked to see how it was doing it. It's a similar approach you took, except it's appending the callback to the original one.

Hi.

The missing callback was only to simplify the code as I did not get it running because of a missing "writeable" option in JavaScript. But suddenly it worked simplified. Perhaps I changed something in the order of commands.
The first approach I got from you like chnaging the HTML template. But your prusaslicerthumbnail does it in the same way and at the end there could be a conflict that the RegEx does not match anymore. So I spend some thoughts to cascade the function via an override. Other plugins overriding this as well will be simply added to the result. Got that idea from printtimegenius but did not get it working in the past. :slight_smile:

That is my actual code:

    // Overwrite the enableAdditionalData function to handle available metadata
    self.filesViewModel.slicerEnableAdditionalData = function(data) {      
      if (data.slicer != null && Object.keys(data.slicer).length > 0 && self.settingsViewModel.settings.plugins.SlicerEstimator.add_slicer_metadata() == true) {
          return true;
      } else {
          return self.filesViewModel.originalEnableAdditionalData(data);
      }
    };
    self.filesViewModel.originalEnableAdditionalData = self.filesViewModel.enableAdditionalData;
    self.filesViewModel.enableAdditionalData = self.filesViewModel.slicerEnableAdditionalData;

    //Add the slicer metadata to "additionalMetadata"
    self.filesViewModel.getSlicerData = function(data) {
      let return_value = "";
      if (data.slicer != null && Object.keys(data.slicer).length > 0) {
        for (const [key, value] of Object.entries(data.slicer)) {
          return_value += value[0] + ": " + value[1] + "<br>";
        }
      }
      return_value += self.filesViewModel.originalGetAdditionalData(data);
      return return_value;
    };
    self.filesViewModel.originalGetAdditionalData = self.filesViewModel.getAdditionalData;
    self.filesViewModel.getAdditionalData = self.filesViewModel.getSlicerData;

Cool. Might be a good idea for me to do something similar with the thumbnail plugins, which was partially the reason I was asking. I suspect it would really depend on how well formed the regex is for the injected template approach. I thought I had made mine fairly generic enough to not interfere with other plugins (ie gcode editor) for the buttons.

Is it possible to edit the template directly in the DOM? Perhaps this would be better as we can refere on e.g. first object occurence instead of building a RegEx.

In my actual implementation it is not neccessary anymore to change the HTML as I only inject additional information but this would not be the case for the thumbnail plugins.