embedding multiple RunKit REPL instances on a single web page - runkit

I am creating a web-based presentation using RemarkJS, my preferred tool for presentations. Since I want to demo small bits of nodejs code, I want to use RunKit's REPL embedding capability. I couldn't quite figure out how to embed multiple instances on a single web page, but I managed to kludge something up by running the following code multiple times
var nb1 = RunKit.createNotebook({
// the parent element for the new notebook
element: document.getElementById("div1"),
// specify the source of the notebook
source: source1
})
var nb2 = RunKit.createNotebook({
// the parent element for the new notebook
element: document.getElementById("div2"),
// specify the source of the notebook
source: source2
})
and so on. This actually works, but is super-inefficient. When I launch my presentation, multiple calls to RunKit are made even though the entire page is loaded only once. Not only that, multiple calls to RunKit are made every time I change the slides, which, in RemarkJS, simple hides and displays different parts of the same, single web page. Since the web page itself is being loaded only once, RunKit should be called only once. At least, that is what I think (obviously, it seems I am wrong).
Finally, the actual RunKit REPL frame takes a while before it is rendered. In the beginning, only a few lines of truncated code shows up, but after a while of waiting, the entire frame gets rendered.
What am I doing wrong? Is there a better way of doing this?

I had the same problem and figured it out. So for future users here is how you do it.
<script src="https://embed.runkit.com"></script>
<style>.embed { overflow: visible; }</style>
<pre class="embed" data-gutter="inside">console.log("hello inside");
1 + 1</pre>
<pre class="embed" data-gutter="outside">console.log("hello outside");
1 + 1</pre>
<pre class="embed" data-gutter="none">console.log("hello none");
1 + 1</pre>
<script>
const elements = [...document.getElementsByClassName('embed')]
const notebooks = elements.reduce((notebooks, element) => {
const innerText = element.firstChild
const currentCell = window.RunKit.createNotebook({
element,
gutterStyle: element.getAttribute("data-gutter"),
source: innerText.textContent,
// Remove the text content of the pre tag after the embed has loaded
onLoad: () => innerText.remove()
})
return notebooks
}, [])
</script>
The sample is taken from here: https://runkit.com/docs/embed
Scroll down and you will find it.

Related

How to detect svelte component from DOM?

Currently making an google chrome extension to visualize svelte components, this would only be used only development mode. Currently I am grabbing all svelte components by using const svelteComponets = document.querySelectorAll(`[class^="svelte"]`); on my content scripts but it is grabbing every svelte element. What are some approaches to grab only the components?
Well you mostly can't get to the Svelte component from the DOM elements.
The reason, appart from Svelte won't give you / expose what's needed, is that there isn't a reliable link between components and elements.
A component can have no elements:
<slot />
Or "maybe no elements":
{#if false}<div />{/if}
It can also have multiple root elements:
<div> A </div>
<div> B </div>
<div> C </div>
By bending the cssHash compiler option a lot, you would probably be able to extract the component "name", maybe class name from the CSS scoping classes generated by Svelte. (Which, in turn could break CSS-only HMR updates with Vite, but that's another story.)
But from there, you won't be able to reliably get to the individual component instances... If we keep the component from the last example, once you've grabbed those 6 divs:
<div> A </div>
<div> B </div>
<div> C </div>
<div> A </div>
<div> B </div>
<div> C </div>
... how do you know where one component instance ends and where the other begins? Or even that there are two components?
I believe, the most reliable way to achieve what you want is probably to use internal Svelte APIs, including those that are used by the actual Svelte dev tools that you want to mimic. (Gotta love when private APIs are the "most reliable"!)
Necessary disclaimer: this only seems reasonable to do this in your case because it is a study subject, and because it's dev only. It would certainly not be wise to rely on this for something important. Private / internal APIs can change with any release without any notice.
If you go in the Svelte REPL and look at the generated JS after enabling the "dev" option, you'll see that the compiler adds some events that are provided for the dev tools.
By trials and experimentation, you can get a sense of how Svelte works, and what dev events are available. You'd also probably need to dig the sources of the compiler itself to understand what's happening with some functions... Being comfortable with a good debugger can help a lot!
For your intended usage, that is build a representation of the Svelte component tree, you'll need to know when a component instance is created, what is its parent component, and when it is destroyed. To add it to the tree, in the right place, and remove it when it goes away. With that you should be able to maintain a representation of the component tree for yourself.
You can know when a component is created with the "SvelteRegisterComponent" dev event (squared in red in the above screenshot). You can know the parent component of a component being instantiated by abusing { current_component } from 'svelte/internal'. And you can know when a component is destroyed by abusing the component's this.$$.on_destroy callbacks (which seems like the most fragile part of our plan).
Going into much more detail about how to proceed with this seems of bit out of scope for this question, but the following basic example should give you some ideas of how you can proceed. See it in action in this REPL.
Here's some code that watches Svelte dev events to maintain a component tree, and exposes it as a Svelte store for easy consumption by others. This code would need to run before your first Svelte component is created (or before the components you want to catch are created...).
import { current_component } from 'svelte/internal';
import { writable } from 'svelte/store';
const nodes = new Map();
const root = { children: [] };
// root components created with `new Component(...)` won't have
// a parent, so we'll put them in the root node's children
nodes.set(undefined, root);
const tree = writable(root);
// notify the store that its value has changed, even
// if it's only a mutation of the same object
const notify = () => {
tree.set(root);
};
document.addEventListener('SvelteRegisterComponent', e => {
// current_component is the component being initialized; at the time
// our event is called, it has already been reverted from the component
// that triggered the event to its parent component
const parentComponent = current_component;
// inspect the event's detail to see what more
// fun you could squizze out of it
const { component, tagName } = e.detail;
let node = nodes.get(component);
if (!node) {
node = { children: [] };
nodes.set(component, node);
}
Object.assign(node, e.detail);
// children creation is completed before their parent component creation
// is completed (necessarilly, since the parent needs to create all its
// children to complete itself); that means that the dev event we're using
// is fired first for children... and so we may have to add a node for the
// parent from the (first created) child
let parent = nodes.get(parentComponent);
if (!parent) {
parent = { children: [] };
nodes.set(parentComponent, parent);
}
parent.children.push(node);
// we're done mutating our tree, let the world know
notify();
// abusing a little bit more of Svelte private API, to know when
// our component will be destroyed / removed from the tree...
component.$$.on_destroy.push(() => {
const index = parent.children.indexOf(node);
if (index >= 0) {
parent.children.splice(index, 1);
notify();
}
});
});
// export the tree as a read only store
export default { subscribe: tree.subscribe }

How to update content of an existing jodit editor in a function

i use jodit in my website and wonder, how the content of an existing jodit editor can be updated within a function.
first i do....
$( document ).ready(function() {
var editor_adressblock = new Jodit('#adressblock', {
fullsize: false
});
});
and the editor is correctly created.
I have a function, which can be executed by the user and then should load content (via json-request) and then put the content into the existing jodit textarea.
Here is my try (for this example i have simplified the function) :
function takeover_adressblock(kunde_id, kunden_adresse_id) {
// get data by ajax.... not shown in this example
var data_by_ajax="test";
editor_adressblock.value=data_by_ajax; // this fails....
}
Means, i don't know how i can send data to the existing jodit texteditor...
I am a jquery beginner, so please don't be too hard ;-)
Best regards
Daniel
Per the documentation you seem to have the right format, so it would help to see the code for the ajax request you're making in case the issue is there.
Otherwise, I would suggest initializing the editor without jQuery in case it's a reference or scoping issue:
const editor = Jodit.make('#editor');
editor.value = '<p>start</p>';

What is the proper way to integrate dynamic content into the layout.ejs file in a Sails.JS application?

Say I wrote a blog app in Sails.js.
On every page in this app, there is a sidebar widget called "Recent Posts", where it lists the titles of the 5 most recent posts and clicking on them takes you to the post in question.
Because this sidebar widget is present on every page, it should be in layout.ejs. But, here we have a conflict - dynamic content is only supposed to be pulled from the database in the controller action for rendering a specific view.
This dynamic content isn't for a specific view, it's for the whole site (via layout.ejs).
By the conventions that I understand, I'd have to get that dynamic content data for the sidebar widget in every controller action that renders a view (otherwise I would get an undefined error when I attempt to call that local in my layout.ejs file).
Things I've tried / considered:
Load that dynamic content in every controller action that renders a view (this solution is very bad) and calling that dynamic content in layout.ejs as if it were a local for the specific view. This works fine, but goes against D.R.Y. principles and quite frankly is a pain in the ass to have to run the same query to the database in every controller action.
As per another similar stackoverflow question, create a new config (E.G. config/globals.js), load my dynamic content from my database into that config file as a variable, and then calling sails.config.globals.[variable_name] in my layout.ejs file. This also worked, since apparently config variables are available everywhere in the application -- but it 's a hacky solution that I'm not a fan of (the content I'm loading is simply the titles and slugs of 5 recent posts, not a "global config option", as the solution implies).
Run the query to get the dynamic content inside the .EJS file directly between some <% %> tags. I'm not sure if this would work, but even if it did, it goes against the separation of concerns MVC principle and I'd like to avoid doing this if at all possible (if it even works).
As per a lengthy IRC discussion # http://webchat.freenode.net/?channels=sailsjs, it was suggested to create a policy and map that policy to all my controllers. In that policy, query the database for the 5 most recent posts, and set them to the req.recentposts. The problem with this solution is that, while the recent posts data will be passed to every controller, I still have to pass that req.recentposts data to my view -- making it so I still have to modify every single res.view({}) in every action. I don't have to have the database query in every action, which is good, but I still have to add a line of code to every action that renders a view... this isn't D.R.Y. and I'm looking for a better solution.
So, what is the proper solution, without needing to load that dynamic content in every controller action (a solution that adheres to D.R.Y. is what I'm lookng for), to get some dynamic content available to my layout.ejs file?
In folder /config you should create a file express.js and add something like that:
module.exports.express = {
customMiddleware: function(app){
app.use(function(req, res, next){
// or whatever query you need
Posts.find().limit(5).exec(function(err, posts){
res.locals.recentPosts = posts;
// remember about next()
next();
});
});
}
}
Then just make some simple loop in your view:
<% for(var i=0; i<recentPosts.length; i++) { %>
<% recentPosts[i].title %>
<% } %>
Here are some links to proper places in documentation:
https://github.com/balderdashy/sails-docs/blob/0.9/reference/Configuration.md#express
and
https://github.com/balderdashy/sails-docs/blob/0.9/reference/Response.md#reslocals
I found out another way to do this. What I did was to create a service that could render .ejs files to plain html by simply taking advantage of the ejs library already in sails. This service could either be invoked by the controller, or even passed as a function in the locals, and executed from within the .ejs. The service called TopNavBarService would look like:
var ejs = require('ejs');
exports.render = function() {
/* database finds goes here */
var userInfo = {
'username' : 'Kallehopp',
'real_name' : 'Kalle Hoppson'
};
var html = null;
ejs.renderFile('./views/topNavBar.ejs', {'locals':userInfo}, function(err, result) { html = result; });
return html;
}
In the constroller it could look like:
module.exports = {
testAction: function (req, res) {
return res.view('testView', {
renderNavbar: TopNavBarService.render // service function as a local!
});
}
};
This way you can create your customized ejs-helper that could even take arguments (although not shown here). When invoked, the helper could access the database and render a part of the html.
<div>
<%- renderNavbar() %>
</div>

How to reference dynamically added element after DOM loaded without a need to act on any events?

I know there is .on and .live (deprecated) available from JQuery, but those assume you want to attach event handlers to one ore more events of the dynamically added element which I don't. I just need to reference it so I can access some of the attributes of it.
And to be more specific, there are multiple dynamic elements like this all with class="cluster" set and each with a different value for the: title attribute, top attribute, and left attribute.
None of these jquery options work:
var allClusters = $('.cluster');
var allClusters2 = $('#map').children('.cluster');
var allClusters3 = $('#map').find('.cluster');
Again, I don't want to attach any event handlers so .on doesn't seem like the right solution even if I were to hijack it, add a bogus event, a doNothing handler, and then just reference my attributes.
There's got to be a better solution. Any ideas?
UPDATE:
I mis-stated the title as I meant to say that the elements were dynamically added to the DOM, but not through JQuery. Title updated.
I figured it out. The elements weren't showing up because the DOM hadn't been updated yet.
I'm working with Google Maps and MarkerClustererPlus to give some more context, and when I add the map markers using markerclustererplus, they weren't available in the javascript code following the add.
Adding a google maps event listener to my google map fixed the problem:
google.maps.event.addListener(myMarkerClusterer, 'clusteringend', function () {
// access newly added DOM elements here
});
Once I add that listener, all the above JQuery selectors and/or methods work just fine:
var allClusters = $('.cluster');
var allClusters3 = $('#map').find('.cluster');
Although this one didn't, but that's because it only finds direct decendants of parent:
var allClusters2 = $('#map').children('.cluster');
Do what you need to do in the ajax callback:
$.ajax(...).done(function (html) {
//append here
allClusters = $('.cluster');
});
If you want them to be separate, you can always bind handlers after the fact, or use $.when:
jqxhr = $.ajax(...).done(function (html) { /* append html */ });
jqxhr.done(function () { allClusters = $('.cluster') });
$.when(jqxhr).done(function () { /* you get it */ });
If these are being appended without ajax changes, then just move the cluster-finding code to wherever the DOM changes take place.
If that's not an option, then I guess you would just have to check on an interval.

getBoundingClientRect() is returning zero in XUL

I have a problem with my firefox extension
I have a XUL popup panel with a hbox for the tag cloud, and a JS code to add divs to this hbox:
<hbox id="tag_base" ondblclick="alert('done')"/>
JS:
var root = document.getElementById('tag_base');
var tag = document.createElement('div');
tag.textContent = 'test';
root.appendChild(tag);
var rect = tag.getBoundingClientRect()
alert(rect.top)
I need to get the dimensions of each added div, however, getBoundingClientRect simply refuses to work.
If I remove alerts, it's always zero.
With alerts the story is different:
The first time the alert is called it returns zero, although the div appears on the screen.
Any subsequent alerts return the correct coordinates.
If I set a breakpoint in Chromebug, everything is reported correctly.
If I do not interupt the execution in any way, and run a loop, only zeroes got returned.
This has got me quite confused.
Calling "boxObject" produces the same results, while "getClientRects[0]" is undefined on the first call.
Any hints on what might be causing this will be greatly appreciated.
Note :
Caution, if you use getBoundingClientRect with an element which has display:none then it will return 0, anywhere in the dom.
Although I can't find any documentation on this seemingly fundamental issue, the problem you noticed is most likely because the layout (aka "reflow") process has not yet run by the moment you ask for the coordinates.
The layout/reflow process takes the page's DOM with any styles the page has and determines the positions and dimensions of the elements and other portions of the page (you could try to read Notes on HTML reflow, although it's not targeted at web developers and probably is a bit outdated).
This reflow process doesn't run synchronously after any change to the DOM, otherwise code like
elt.style.top = "5px";
elt.style.left = "15px";
would update the layout twice, which is inefficient.
On the other hand, asking for elements position/dimension (at least via .offsetTop) is supposed to force layout to return the correct information. This doesn't happen in your case for some reason and I'm not sure why.
Please create a simple testcase demonstrating the problem and file a bug in bugzilla.mozilla.org (CC me - ***********#gmail.com).
My guess is that this is related to XUL layout, which is less robust than HTML; you could try creating the cloud in an HTML doc in an iframe or at least in a <description> using createElementNS to create real HTML elements instead of xul:div you're creating with your current code.
Be sure the DOM is ready. In my case, even when using the getBoundingClientRect function on click events. The binding of the events needed to happen when the DOM is ready.