How to get local storage value and use it in inline style as a condition in EJS - ejs

Let's say in certain component I am setting local storage value. Now in my main file.ejs
I would like to get this value and base on this value set inline styles to my div.
In our local storage:
age: "25"
// const someValue = get value from local storage
<body>
<div style={someValue ? A : B}>Cool Div</div>
</body>
How Can I implement something like this? In what tags should I wrap my 'js' code

Related

Is there a way/workaround to have the slot principle in hyperHTML without using Shadow DOM?

I like the simplicity of hyperHtml and lit-html that use 'Tagged Template Literals' to only update the 'variable parts' of the template. Simple javascript and no need for virtual DOM code and the recommended immutable state.
I would like to try using custom elements with hyperHtml as simple as possible
with support of the <slot/> principle in the templates, but without Shadow DOM. If I understand it right, slots are only possible with Shadow DOM?
Is there a way or workaround to have the <slot/> principle in hyperHTML without using Shadow DOM?
<my-popup>
<h1>Title</h1>
<my-button>Close<my-button>
</my-popup>
Although there are benefits, some reasons I prefer not to use Shadow DOM:
I want to see if I can convert my existing SPA: all required CSS styling lives now in SASS files and is compiled to 1 CSS file. Using global CSS inside Shadow DOM components is not easily possible and I prefer not to unravel the SASS (now)
Shadow DOM has some performance cost
I don't want the large Shadow DOM polyfill to have slots (webcomponents-lite.js: 84KB - unminified)
Let me start describing what are slots and what problem these solve.
Just Parked Data
Having slots in your layout is the HTML attempt to let you park some data within the layout, and address it later on through JavaScript.
You don't even need Shadow DOM to use slots, you just need a template with named slots that will put values in place.
<user-data>
<img src="..." slot="avatar">
<span slot="nick-name">...</span>
<span slot="full-name">...</span>
</user-data>
Can you spot the difference between that component and the following JavaScript ?
const userData = {
avatar: '...',
nickName: '...',
fullName: '...'
};
In other words, with a function like the following one we can already convert slots into useful data addressed by properties.
function slotsAsData(parent) {
const data = {};
parent.querySelectorAll('[slot]').forEach(el => {
// convert 'nick-name' into 'nickName' for easy JS access
// set the *DOM node* as data property value
data[el.getAttribute('slot').replace(
/-(\w)/g,
($0, $1) => $1.toUpperCase())
] = el; // <- this is a DOM node, not a string ;-)
});
return data;
}
Slots as hyperHTML interpolations
Now that we have a way to address slots, all we need is a way to place these inside our layout.
Theoretically, we don't need Custom Elements to make it possible.
document.querySelectorAll('user-data').forEach(el => {
// retrieve slots as data
const data = slotsAsData(el);
// place data within a more complex template
hyperHTML.bind(el)`
<div class="user">
<div class="avatar">
${data.avatar}
</div>
${data.nickName}
${data.fullName}
</div>`;
});
However, if we'd like to use Shadow DOM to keep styles and node safe from undesired page / 3rd parts pollution, we can do it as shown in this Code Pen example based on Custom Elements.
As you can see, the only needed API is the attachShadow one and there is a super lightweight polyfill for just that that weights 1.6K min-zipped.
Last, but not least, you could use slots inside hyperHTML template literals and let the browser do the transformation, but that would need heavier polyfills and I would not recommend it in production, specially when there are better and lighter alternatives as shown in here.
I hope this answer helped you.
I have a similar approach, i created a base element (from HyperElement) that check the children elements inside a custom element in the constructor, if the element doesn't have a slot attribute im just sending them to default slot
import hyperHTML from 'hyperhtml/esm';
class HbsBase extends HyperElement {
constructor(self) {
self = super(self);
self._checkSlots();
}
_checkSlots() {
const slots = this.children;
this.slots = {
default: []
};
if (slots.length > 0) {
[...slots].map((slot) => {
const to = slot.getAttribute ? slot.getAttribute('slot') : null;
if (!to) {
this.slots.default.push(slot);
} else {
this.slots[to] = slot;
}
})
}
}
}
custom element, im using a custom rollup plugin to load the templates
import template from './customElement.hyper.html';
class CustomElement extends HbsBase {
render() {
template(this.html, this, hyperHTML);
}
}
Then on the template customElement.hyper.html
<div>
${model.slots.body}
</div>
Using the element
<custom-element>
<div slot="body">
<div class="row">
<div class="col-sm-6">
<label for="" class="">Name</label>
<p>
${model.firstName} ${model.middleInitial} ${model.lastName}
</p>
</div>
</div>
...
</div>
</custom-element>
Slots without shadow dom are supported by multiple utilities and frameworks.
Stencil enables using without shadow DOM enabled. slotted-element gives support without framework.

Dynamically set fancybox 3 iframe size

When dealing with dynamically sizing the iframe in fancybox, I accidentally found that setting data-width and data-height on the [data-fancybox] is helpful after reading this answer.
Exmaple HTML element:
<a data-fancybox data-width="<?= $banner_width; ?>" data-height="<?= $banner_height; ?>" data-src="example.com" href="javascript:;">example.com</a>
and js:
$("[data-fancybox]").fancybox({
afterLoad: function ( instance, slide ) {
$('body').find('.fancybox-content').css({"width": slide.opts.width + "px", "height": slide.opts.height + "px"});
}
});
What I couldn't figure out is that there is no explanation of data-width and data-height usage on HTML element from fancybox documentation (please correct me if I'm wrong).
NOTE: these two code snippets above do work for me, but they have to work together, it wouldn't work if one of them is taken off.
Can anyone explain that a little bit for me please?
Based on fancybox 3 documentation, they do offer a way to set our own custom options by creating data-options attribute. Here is the example from the documentation:
<a data-fancybox
data-options='{"caption" : "My caption", "src : "iframe.html"}' href="javascript:;">
Open external page using iframe
</a>
You can see the value of these options by console.info( slide.opts ) from the callback function like onComplete (slide is one argument in the callback function ).
Not surprisingly, these two snippets are the same:
<a data-fancybox data-width="<?= $banner_width; ?>" data-height="<?= $banner_height; ?>" data-src="example.com" href="javascript:;">example.com</a>
<a data-fancybox data-options='{"width" : "<?= $banner_width; ?>", "height" : data-height="<?= $banner_height; ?>", "src" : "example.com" }' href="javascript:;">example.com</a>
So, in my javascript, I use slide.opts.width to get the value from data-width and slide.opts.heightto get the height from data-height, that is how my width and height value get passed from backend to frontend.
Since these two values are processed in afterLoad callback, so every iframe will be initiated with different width and height.
Properties - data-width and data-height - are used to tell the script real dimension of the image. The script then can calculate position and start zooming thumbnail while real image is still loading.
See documentation - http://fancyapps.com/fancybox/3/docs/#images
These properties are not used anywhere else.
You can use { iframe : { css : { width: '', height : '' } } } to set iframe element width/height, but there is no option to change iframe content.

How to disabled wysihtml5 HTML Clean Up in Editor?

How to disable HTML Clean Up while in the editor mode? I'm in a need of allowing css format & inline html in code. The idea is to disable parser and html clean up action when pasting the code and entering the Editor for editing. Thanks.
You can provide an identity function as the parser attribute upon initializing the wysihtml5 editor. The below script is a coffeescript snippet that disables the cleanup completely.
enableWysiwyg: ->
#$el.find('textarea').wysihtml5
"style": false
"font-styles": false #Font styling, e.g. h1, h2, etc. Default true
"emphasis": true #Italics, bold, etc. Default true
"lists": false #(Un)ordered lists, e.g. Bullets, Numbers. Default true
"html": true #Button which allows you to edit the generated HTML. Default false
"link": false #Button to insert a link. Default true
"image": false #Button to insert an image. Default true,
"color": false #Button to change color of font
parser: (html) -> html
JavaScript version of the above code:
$('textarea').wysihtml5({
"style": false,
"font-styles": false,
"emphasis": true,
"lists": false,
"html": true,
"link": false,
"image": false,
"color": false,
parser: function(html) {
return html;
}
});
Actually, this is what the parser rules are for.
You can attach your custom rules to the included var wysihtml5ParserRules before instantiate the editor object or just create your own rules object and give to the editor's constructor.
For example, to allow the h1 and h3 tag in addition to the tags allowed in the distributed simple example rules, you'd need to set up as follows:
<form>
<div id="toolbar" style="display: none;">
<a data-wysihtml5-command="bold" title="CTRL+B">bold</a> |
<a data-wysihtml5-command="italic" title="CTRL+I">italic</a>
<a data-wysihtml5-action="change_view">switch to html view</a>
</div>
<textarea id="textarea" placeholder="Enter text ..."></textarea>
<br><input type="reset" value="Reset form!">
</form>
<!-- The distributed parser rules -->
<script src="../parser_rules/simple.js"></script>
<script src="../dist/wysihtml5-0.4.0pre.min.js"></script>
<script>
// attach some custom rules
wysihtml5ParserRules.tags.h1 = {remove: 0};
wysihtml5ParserRules.tags.h3 = {remove: 0};
var editor = new wysihtml5.Editor("textarea", {
toolbar: "toolbar",
parserRules: wysihtml5ParserRules,
useLineBreaks: false
});
</script>
Now, when you enter/paste <title>test</title> into the editor, while you're in the editor mode, and then switch to html view, you'll get <title>test</title>. And when you switch back to editor view, you'll get <title>test</title> again.
That was the general part.
Now, in your case, I'm not sure if it's the best idea to work with 121 custom parser rules (the count of HTML tags to handle) or if it wouldn't be better to take the time and dig into the source code to find a more performant solution (doesn't make much sense to tell a parser to actualy just return the input string anyway, right?). Furthermore, you said you want to allow CSS as well. So your custom parser rules will even extend.
Anyway, as a starting point, feel free to use my custom parser rule set from here: https://github.com/eyecatchup/wysihtml5/blob/master/parser_rules/allow-all-html5.js.

Are global variables required for some types of DOM manipulation with javascript?

In the example code below, I want to know why the variable called child must be global ( no var) in order for the code to work. I also want to know if the code below is considered bad practice due to having a global variable and how a better practiced rendition of the code below might look. Thanks.
<!DOCTYPE html>
<meta charset="UTF-8">
<title>dom</title>
<div class="product">
<h2> Product Name </h2>
<img src="pic.jpg" />
<p> Description </p> </div>
<script>
var products = document.getElementsByClassName("product"),
child; // how come var breaks the code ?
for ( i = 0; i < products.length; i++) {
child = products[i].firstChild;
while (child.nodeType !== 1) {
child = child.nextSibling;
}
console.log(child);
}
</script>
You already have a var, as there is a comma before child. Hence adding var would give you
var product, var child
which is illegal.
child is not global, because the var in
var product, child
applies to the whole list of variables following var. (Well, child is global anyway since it is not nested in a function. But that does not have to do with var or not var.)
If you insist on having var twice, write
var product = ... ;
var child;

Insert locallang values into TypoScript [stdWrap]

I wonder if it is possible to insert a locallang value into my typoscript wrap / stdWrap object:
I have:
singleView {
related.wrap = <div class="related">Related items: |</div>
}
What I need is something like that:
singleView {
related.wrap = <div class="related">{LLL:related_items}:|</div>
}
EDIT:
What I am looking for is an TS equivalent for $this->pi_getLL (which works fine).
In our case, we would be replacing values in our extension with
$this->cObj->stdWrap($item, $this->conf['singleView.']['related']);
locallang.xml in extensions /pi1 directory:
<languageKey index="default" type="array">
<label index="related_items">Related items: </label>
</languageKey>
singleView {
related.dataWrap = <div class="related">{LLL:EXT:myextensionkey/pi1/locallang.xml:related_items}:|</div>
}
Of course related_items must be a valid locallang key.
Edit: Do not use wrap with insertData here, because insertData is then run on the whole content, including the user input. If somebody uses {DB:be_users|0|password} inside the content you are wrapping, he can just see the password (hash) of the backend user with uid 0.
Building on the answer of pgampe here, sadly I don't have enough reputation to do it in a comment.
For easier use, do something like this:
constants:
extension.key = myextensionkey
LLL = LLL:EXT:{$extension.key}/Resources/Private/Language/locallang.xlf
and in your typoscript:
singleView {
related.dataWrap = <div class="related">{{$LLL}:related_items}:|</div>
}