CKEditor5 - Making Mention work together with Markdown - javascript

I'm using CKEditor5 and trying to make the Mention plugin work together with the Markdown plugin. Currently, adding a #mention works but it is exported simply as #mention by editor.getData(), and instanciating an editor with data = '#mention' will not lead the package to parsing this data as a mention in the editor.
Ideally I'd like to have an escaped markdown inline block such as {objectName[objectId]} as the data input, which would then be upcast to the model as a MentionAttribute. For downcasting, one would need to find these MentionAttributes and downcast them to the correct syntax.
I have no clue how to do this, I am new to CKEditor5 the architecture of the package is complex and it can be hard to add customization. I know that there are upcast and downcast converters I can create, but I couldn't find anything for matching text and inserting it into the model.
Does anyone know how I could achieve this?

I was able to make this work by:
Using the Editor as a controlled component
Implementing a custom syntax for mentions in our markdown format
Passing markdown converted into HTML interpretable by the Model layer, including mention syntax used by the Mention plugin, to the data props of the Editor wrapper
On onChange events, pass the HTML converted back into markdown to the controller

Related

How does Vue implement the double curlys/mustache syntax?

How does a frameworks like Vue implement their own custom syntaxes within an HTML document?
So I want to create a template engine, consequently I need to know how to implement my own custom syntax. I thought that an easy one would be the double curlys that can be used in Vue. Example of curlys:
<h1>{{pageTitle}}</h1>
My first thought was to use `String.prototype.replaceAll(regex, string); but I got stuck at the regex I would use. In fact thinking about it now, I probably need a dynamic regular expression maybe?
p.replaceAll(/\{\{()\}\}/g, '<p>{{embeddedVar}}</p>')
The other option I considered was a parser, or a lexer, but I didn't even know where to start. I built them in school in C++. I thought maybe NPM has one pre-built?
It seems like several developers have wrote their own custom template engine that has built-in support for the double curly brackets. I was thinking that maybe there is a common way that its being implemented.
The Vue syntax is indeed not understood by the browser. the work is done by the Vue library that's imported in every Vue app.
The original markup (with the curly braces showing up) can even be seen for a split second when the page is loading, and this is because Vue hasn't loaded up all the way yet.

React - Parse HTML string and insert components at particular places within it

I'm working on an app for a client who wants to display Zendesk Support article data with richer content than the API/tool supports. The API, powered by a basic WYSIWYG editor on Zendesk, is able to serve up an HTML blob with <p> and <img> and <a> tags. But it's unable to, say, easily embed a video, show an image gallery, or link to another location in the app without necessitating a hard refresh.
So we decided to build out a small amount of DSL syntax whereby the client could type something that would be parsed on our app and turned into the richer content. For example:
[[ video src="youtube.com/whatever" ]]
might, on our app, be parsed out, and replaced with an iframe embedding that video directly in the article.
I've got the above example working just fine, using all sorts of fun/problematic RegExp magic.
I'm aware that parsing HTML with RegExp is a major anti-pattern, and I'm getting around it by not really parsing HTML per se -- I'm just looking for [[...]]).
Basically, I parse each code out, determine what to replace it with, pass back that component, and then call ReactDOMServer.renderToString(replacementEl) to insert the resulting HTML into the article HTML string, which is then rendered to the page using dangerouslySetInnerHtml.
This is working great, despite all the obvious messiness issues. (I know, but the client wants a very feature-rich solution, and wants to use the Zendesk API).
Here's the problem: This solution only works when rendering HTML. It doesn't work when I try to render, for example, a <Link> (from React Router) or a home-made <Gallery /> component.
So I'm trying to figure out if there's a good way to do this. One that ideally doesn't involve going full RegExp on the HTML blob (which is obviously a mess).
I'm guessing that the solution will involve removing the codes, converting the code-free HTML string into actual React components (as opposed to just spitting the string into another element), and then, based on some sort of ledger of where the short codes go in this new React component, inserting the contents into the page.
But I'd appreciate some suggestions if anyone has encountered a similar problem. I want to do this in a way that isn't totally crazy and brittle, though it's obviously an inherently messy problem.
EDIT: To clarify, when I say it doesn't work, what I mean is that the JavaScript associated with the React components doesn't run. If I replace a code with a <Link to="whatever" />, for example, ReactDOMServer.renderToString successfully turns that component into its final HTML (an <a> tag), but nothing happens on click of that tag, because the JavaScript that's actually changing browserHistory on a regular click just isn't run.
So maybe the more pointed question is:
Is there a way to insert React Components into an HTML string in such a way that they retain their JavaScript functionality and don't just become HTML?

TinyMCE: Custom characters for format?

I want to change the behaviour for the "BOLD" - Format. Instead of wrapping
<b>text</b>
around it, it should instead wrap
*text*
around it. I found examples to wrap other html-Tags than the default, but not how to wrap simple characters.
One of those examples: bold: {inline: 'span', 'classes': 'bold'},
To my knowledge, this isn't really possible without changing the library, making an undocumented library call or building some JS to hook into TinyMCE's output. Neither of these are options you should really consider; they could break as soon as you try and update the library. And being unable to update the library means you won't be able to opt into bugfixes and the like.
The API for custom formats is documented here: http://www.tinymce.com/wiki.php/configuration:formats
Is your intention to allow users to submit markdown formatted text to a server? If so, I wonder if you might have to perform reparsing. I know of one project that offers this: https://github.com/hgilani/html2markdown. Not sure if it suits your needs, though.

Searching good JavaScript formatter for eclipse

I am using eclipse as my IDE but I don't like the installed formatters. I also builded my own one but didn't get it to work like I want it to. For example things like that happen often:
Is there somebody out there with a good JavaScript formatter or a link with a list of some. I only found a few (e.g. the google formatter) but I don't like one of them.
Try changing the formatting preferences individually.
Window->Preferences->JavaScript->Code Style->Formatter
You'll have to save them as a new file. The only issue i have is that the object declaration won't use tabs instead of spaces for the property declarations.

Passing Python Data to JavaScript via Django

I'm using Django and Apache to serve webpages. My JavaScript code currently includes a data object with values to be displayed in various HTML widgets based on the user's selection from a menu of choices. I want to derive these data from a Python dictionary. I think I know how to embed the JavaScript code in the HTML, but how do I embed the data object in that script (on the fly) so the script's functions can use it?
Put another way, I want to create a JavaScript object or array from a Python dictionary, then insert that object into the JavaScript code, and then insert that JavaScript code into the HTML.
I suppose this structure (e.g., data embedded in variables in the JavaScript code) is suboptimal, but as a newbie I don't know the alternatives. I've seen write-ups of Django serialization functions, but these don't help me until I can get the data into my JavaScript code in the first place.
I'm not (yet) using a JavaScript library like jQuery.
n.b. see 2018 update at the bottom
I recommend against putting much JavaScript in your Django templates - it tends to be hard to write and debug, particularly as your project expands. Instead, try writing all of your JavaScript in a separate script file which your template loads and simply including just a JSON data object in the template. This allows you to do things like run your entire JavaScript app through something like JSLint, minify it, etc. and you can test it with a static HTML file without any dependencies on your Django app. Using a library like simplejson also saves you the time spent writing tedious serialization code.
If you aren't assuming that you're building an AJAX app this might simply be done like this:
In the view:
from django.utils import simplejson
def view(request, …):
js_data = simplejson.dumps(my_dict)
…
render_template_to_response("my_template.html", {"my_data": js_data, …})
In the template:
<script type="text/javascript">
data_from_django = {{ my_data }};
widget.init(data_from_django);
</script>
Note that the type of data matters: if my_data is a simple number or a string from a controlled source which doesn't contain HTML, such as a formatted date, no special handling is required. If it's possible to have untrusted data provided by a user you will need to sanitize it using something like the escape or escapejs filters and ensure that your JavaScript handles the data safely to avoid cross-site scripting attacks.
As far as dates go, you might also want to think about how you pass dates around. I've almost always found it easiest to pass them as Unix timestamps:
In Django:
time_t = time.mktime(my_date.timetuple())
In JavaScript, assuming you've done something like time_t = {{ time_t }} with the results of the snippet above:
my_date = new Date();
my_date.setTime(time_t*1000);
Finally, pay attention to UTC - you'll want to have the Python and Django date functions exchange data in UTC to avoid embarrassing shifts from the user's local time.
EDIT : Note that the setTime in javascript is in millisecond whereas the output of time.mktime is seconds. That's why we need to multiply by 1000
2018 Update: I still like JSON for complex values but in the intervening decade the HTML5 data API has attained near universal browser support and it's very convenient for passing simple (non-list/dict) values around, especially if you might want to have CSS rules apply based on those values and you don't care about unsupported versions of Internet Explorer.
<div id="my-widget" data-view-mode="tabular">…</div>
let myWidget = document.getElementById("my-widget");
console.log(myWidget.dataset.viewMode); // Prints tabular
somethingElse.addEventListener('click', evt => {
myWidget.dataset.viewMode = "list";
});
This is a neat way to expose data to CSS if you want to set the initial view state in your Django template and have it automatically update when JavaScript updates the data- attribute. I use this for things like hiding a progress widget until the user selects something to process or to conditionally show/hide errors based on fetch outcomes or even something like displaying an active record count using CSS like #some-element::after { content: attr(data-active-transfers); }.
For anyone who might be having a problems with this, be sure you are rendering your json object under safe mode in the template. You can manually set this like this
<script type="text/javascript">
data_from_django = {{ my_data|safe }};
widget.init(data_from_django);
</script>
As of mid-2018 the simplest approach is to use Python's JSON module, simplejson is now deprecated. Beware, that as #wilblack mentions you need to prevent Django's autoescaping either using safe filter or autoescape tag with an off option. In both cases in the view you add the contents of the dictionary to the context
viewset.py
import json
def get_context_data(self, **kwargs):
context['my_dictionary'] = json.dumps(self.object.mydict)
and then in the template you add as #wilblack suggested:
template.html
<script>
my_data = {{ my_dictionary|safe }};
</script>
Security warning:
json.dumps does not escape forward slashes: an attack is {'</script><script>alert(123);</script>': ''}. Same issue as in other answers. Added another answer hopefully fixing it.
You can include <script> tags inside your .html templates, and then build your data structures however is convenient for you. The template language isn't only for HTML, it can also do Javascript object literals.
And Paul is right: it might be best to use a json module to create a JSON string, then insert that string into the template. That will handle the quoting issues best, and deal with deep structures with ease.
It is suboptimal. Have you considered passing your data as JSON using django's built in serializer for that?
See the related response to this question. One option is to use jsonpickle to serialize between Python objects and JSON/Javascript objects. It wraps simplejson and handles things that are typically not accepted by simplejson.
Putting Java Script embedded into Django template is rather always bad idea.
Rather, because there are some exceptions from this rule.
Everything depends on the your Java Script code site and functionality.
It is better to have seperately static files, like JS, but the problem is that every seperate file needs another connect/GET/request/response mechanism. Sometimes for small one, two liners code os JS to put this into template, bun then use django templatetags mechanism - you can use is in other templates ;)
About objects - the same. If your site has AJAX construction/web2.0 like favour - you can achieve very good effect putting some count/math operation onto client side. If objects are small - embedded into template, if large - response them in another connection to avoid hangind page for user.
Fixing the security hole in the answers by #willblack and #Daniel_Kislyuk.
If the data is untrusted, you cannot just do
viewset.py
def get_context_data(self, **kwargs):
context['my_dictionary'] = json.dumps(self.object.mydict)
template.html
<script>
my_data = {{ my_dictionary|safe }};
</script>
because the data could be something like
{"</script><script>alert(123);</script>":""}
and forward slashes aren't escaped by default. Clearly the escaping by json.dumps may not 100% match the escaping in Javascript, which is where the problems come from.
Fixed solution
As far as I can tell, the following fixes the problem:
<script>
my_data = JSON.parse("{{ my_dictionary|escapejs }}");
</script>
If there are still issues, please post in the comments.

Categories

Resources