Skip to content
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

class tag doesn't render correctly #224

Closed
junwenwaynepeng opened this issue Nov 27, 2023 · 2 comments
Closed

class tag doesn't render correctly #224

junwenwaynepeng opened this issue Nov 27, 2023 · 2 comments

Comments

@junwenwaynepeng
Copy link
Contributor

Details

  • OS (Operating System) version:
  • Browser and browser version:
  • Django version: >4.0
  • Martor version & theme: 1.6.28

Steps to reproduce

  1. Let two different models have same fields name, e.g.
    class ModelA(models.Model):
    description = MartorField()
    class ModelB(models.Model):
    description = MartorField()
  2. render forms of these two models on the same page
  3. Then, there will have two elements that share the same id. (In the following picture, two elements use the same id=martor-detail.)
    螢幕擷取畫面 2023-11-28 005933
  4. One of the element has class="martor-field martor-field-detail ace_editor ace-github resizable", and the other element has class="martor-field martor-field-detail"

I guess the is because the js(resizable.min.js, theme-github.js, ace.js) doesn't expact there are two elements use the same id.

@junwenwaynepeng
Copy link
Contributor Author

I try to fix it by naively add the following script after page loaded

  <script>
    // Wait for the DOM to be ready
    document.addEventListener("DOMContentLoaded", function () {
      // Get all forms with an ID starting with "martor-"
      var martorForms = document.querySelectorAll('[id^="martor-"]');
      console.log('I am runing')
      console.log(martorForms)
      // Loop through each form and add the specified classes
      martorForms.forEach(function (form) {
        form.classList.add("ace_editor", "ace_hidpi", "ace-github", "resizable");
        // Insert the specified content between div tags in the form
        if (form.innerHTML == null) {
          form.innerHTML = '<div style="position: absolute;"></div><textarea class="ace_text-input" wrap="off" autocorrect="off" autocapitalize="none" spellcheck="false" style="opacity: 0; font-size: 1px;" aria-haspopup="false" aria-autocomplete="both" role="textbox"></textarea><div class="ace_gutter" aria-hidden="true"><div class="ace_layer ace_gutter-layer ace_folding-enabled" style="height: 1000000px;"></div></div><div class="ace_scroller" style="line-height: 0px;"><div class="ace_content"><div class="ace_layer ace_print-margin-layer"><div class="ace_print-margin" style="left: 4px; visibility: hidden;"></div></div><div class="ace_layer ace_marker-layer"></div><div class="ace_layer ace_text-layer" style="height: 1000000px; margin: 0px 4px;"></div><div class="ace_layer ace_marker-layer"></div><div class="ace_layer ace_cursor-layer ace_hidden-cursors"><div class="ace_cursor"></div></div></div></div><div class="ace_scrollbar ace_scrollbar-v" style="display: none; width: 20px;"><div class="ace_scrollbar-inner" style="width: 20px;">&nbsp;</div></div><div class="ace_scrollbar ace_scrollbar-h" style="display: none; height: 20px;"><div class="ace_scrollbar-inner" style="height: 20px;">&nbsp;</div></div><div style="height: auto; width: auto; top: 0px; left: 0px; visibility: hidden; position: absolute; white-space: pre; font: inherit; overflow: hidden;"><div style="height: auto; width: auto; top: 0px; left: 0px; visibility: hidden; position: absolute; white-space: pre; font: inherit; overflow: visible;"></div><div style="height: auto; width: auto; top: 0px; left: 0px; visibility: hidden; position: absolute; white-space: pre; font: inherit; overflow: visible;">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</div></div><div class="resizable-handle resizable-b"></div>' + form.innerHTML;
        }
      });
    });
  </script>

It didn't work of course. I also tried to change the line 33 of the templates/martor/editor.html from

          <div id="martor-{{ field_name }}" class="martor-field martor-field-{{ field_name }}"></div>

to

        <div id="martor-{{ field_name }}-test" class="martor-field martor-field-{{ field_name }}"></div>

And, everything goes wrong.

From these tests, I feel the process to render a proper markdown editor is controlled by some js in plugins, but without an overview of how an editor is generated, I am afraid that I am not able to solve this bug.

@junwenwaynepeng
Copy link
Contributor Author

junwenwaynepeng commented Nov 29, 2023

I attempted two approaches to address this bug. The first one, which I considered more robust, unfortunately, did not succeed. Let me begin by presenting the code that was successful. If authors acknowledged this approach. I can push my version to brench.

Succesful way

class MartorWidget(forms.Textarea):
    def render(self, name, value, attrs=None, renderer=None, **kwargs):
        import random, string
        random_string = ''.join(random.choice(string.ascii_letters + string.digits) for x in range(10))
        attrs['id'] = attrs['id'] + '-' + random_string
        # Make the settings the default attributes to pass
        attributes_to_pass = {
            "data-enable-configs": MARTOR_ENABLE_CONFIGS,
            "data-markdownfy-url": reverse("martor_markdownfy"),
        }

        if MARTOR_UPLOAD_URL:
            attributes_to_pass["data-upload-url"] = reverse("imgur_uploader")
        if MARTOR_SEARCH_USERS_URL:
            attributes_to_pass["data-search-users-url"] = reverse("search_user_json")
        if MARTOR_SEARCH_USERS_URL:
            attributes_to_pass["data-base-emoji-url"] = MARTOR_MARKDOWN_BASE_EMOJI_URL

        # Make sure that the martor value is in the class attr passed in
        if "class" in attrs:
            attrs["class"] += " martor"
        else:
            attrs["class"] = "martor"

        # Update and overwrite with the attributes passed in
        attributes_to_pass.update(attrs)

        # Update and overwrite with any attributes that are on the widget
        # itself. This is also the only way we can push something in without
        # being part of the render chain.
        attributes_to_pass.update(self.attrs)

        template = get_template("martor/%s/editor.html" % get_theme())
        emoji_enabled = MARTOR_ENABLE_CONFIGS.get("emoji") == "true"
        mentions_enabled = MARTOR_ENABLE_CONFIGS.get("mention") == "true"

        widget = super().render(name + '-' + random_string, value, attributes_to_pass)

        return template.render(
            {
                "martor": widget,
                "field_name": name,
                "random_string": random_string,
                "emoji_enabled": emoji_enabled,
                "mentions_enabled": mentions_enabled,
                "toolbar_buttons": MARTOR_TOOLBAR_BUTTONS,
            }
        )

Lines that I added and changed:

  1. Line 3 and 4: I add
        import random, string
        random_string = ''.join(random.choice(string.ascii_letters + string.digits) for x in range(10))
  1. Line 37: I change widget = super().render(name, value, attributes_to_pass) to widget = super().render(name + '-' + random_string, value, attributes_to_pass)
  2. Line 43: I add "random_string": random_string,

Then, in editor.html, we have to put -{{ random_string }} after each {{ field_name }}.

{% load i18n %}
<div class="main-martor main-martor-{{ field_name }}-{{random_string}}" data-field-name="{{ field_name }}-{{random_string}}">
  <div class="section-martor">
    <nav class="tab-martor-menu" style="position:relative">
      <div class="nav nav-tabs" id="nav-tab" role="tablist">
        <a class="nav-link active" data-tab="editor-tab-{{ field_name }}-{{random_string}}" id="nav-editor-tab-{{ field_name }}-{{random_string}}" data-bs-toggle="tab" data-bs-target="#nav-editor-{{ field_name }}-{{random_string}}" type="button" role="tab" aria-controls="nav-editor-{{ field_name }}-{{random_string}}" aria-selected="true">
          <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-pencil-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
            <path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
            <path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
          </svg>
          <span>{% trans "Editor" %}</span>
        </a>
        <a class="nav-link" data-tab="preview-tab-{{ field_name }}-{{random_string}}" id="nav-preview-tab-{{ field_name }}-{{random_string}}" data-bs-toggle="tab" data-bs-target="#nav-preview-{{ field_name }}-{{random_string}}" type="button" role="tab" aria-controls="nav-preview-{{ field_name }}-{{random_string}}" aria-selected="false">
          <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-eye-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
            <path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z"/>
            <path fill-rule="evenodd" d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8zm8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z"/>
          </svg>
          <span>{% trans "Preview" %}</span>
        </a>

        {% include "martor/bootstrap/toolbar.html" %}
      </div>
    </nav>

    <div class="tab-content" id="nav-tabContent">
      <div class="tab-pane fade show active" id="nav-editor-{{ field_name }}-{{random_string}}" role="tabpanel" aria-labelledby="nav-editor-tab-{{ field_name }}-{{random_string}}">
        <div class="text-center upload-progress" data-field-name="{{ field_name }}-{{random_string}}" style="display:none">
          <div class="spinner-border" role="status">
            <span class="visually-hidden">Loading...</span>
          </div>
          <div>{% trans "Uploading... please wait..." %}</div>
        </div>
        <div id="martor-{{ field_name }}-{{random_string}}" class="martor-field martor-field-{{ field_name }}"></div>
        {{ martor }}
        <span class="icon expand-editor">
          <svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-arrows-expand" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
            <path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h13a.5.5 0 0 1 0 1h-13A.5.5 0 0 1 1 8zM7.646.146a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1-.708.708L8.5 1.707V5.5a.5.5 0 0 1-1 0V1.707L6.354 2.854a.5.5 0 1 1-.708-.708l2-2zM8 10a.5.5 0 0 1 .5.5v3.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.708 0l-2-2a.5.5 0 0 1 .708-.708L7.5 14.293V10.5A.5.5 0 0 1 8 10z"/>
          </svg>
        </span>
      </div>

      <div class="tab-pane fade martor-preview" id="nav-preview-{{ field_name }}-{{random_string}}" role="tabpanel" aria-labelledby="nav-preview-tab-{{ field_name }}-{{random_string}}">
        <p>{% trans "Nothing to preview" %}</p>
      </div>
    </div>
  </div><!-- end  /.section-martor -->

  {% include 'martor/bootstrap/guide.html' %}
  {% include 'martor/bootstrap/emoji.html' %}
</div>

Unsuccesful way

My original plan is to change all {{ field_name }} to {{ model_name }}-{{ field_name }}-{{ instance_id }}. It means that I have to input model_name and instance_id into MartorWidget and let it render. There is no problem I can put model_name as a parameter of MartorWidget. However, the trouble is instance_id. It needs to be dynamic in Form's metaclass. After googling for hours, I found it was impossible to do so. Therefore, I discard this method.

junwenwaynepeng added a commit to junwenwaynepeng/django-markdown-editor that referenced this issue Dec 2, 2023
agusmakmun added a commit that referenced this issue Mar 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants