How do I get in-line code with Jekyll and Pygments? - syntax-highlighting

I'm using Markdown with Liquid tags to mark up some code for a Jekyll-generated site and would like to include some code that is both in-line (in a paragraph) and has coloured syntax (with Pygments), but it does not appear to work.
The markup
Lorem ipsum dolor {% highlight javascript %} var sit = "amet"; {% endhighlight %} consectetur adipiscing elit.
results in
<p>Lorem ipsum dolor <div class='highlight'><pre><code class='javascript'> <span class='kd'>var</span> <span class='nx'>sit</span> <span class='o'>=</span> <span class='s2'>"amet"</span><span class='p'>;</span></code></pre></div> consectetur adipiscing elit.</p>
I would very much like highlighted text not be wrapped in <div class='highlight'>, or at least have it be a <span class='highlight'>.
Using {% highlight javascript nowrap %} does not work, as suggested elsewhere. (Perhaps this is an issue with my setup—which is Ruby 2.0, Jekyll 0.12.1, pygments.rb 0.3.7?)
I would like to host this page on GitHub, which means I cannot rely on a plugin. Bummer, right?
Addendum: Line numbering (ie. {% highlight javascript linenos %}) does not appear to work either. Man.

The easiest way to do this is to use Github Flavoured Markdown and use their default inline code.
in your _config.yml file, change your markdown to redcarpet. You're now ready to go with GFM.
markdown: redcarpet
Now you can follow all the GitHub Markdown. To do inline code as follow:
Here is some `inline code` in the middle of sencence

You can add a CSS class to any object you put in a post.
If you define a CSS entry like this:
.inlined { display:inline; }
You can then add this class to the generated <div> doing this:
Lorem ipsum dolor
{% highlight javascript %}var sit="amet"; {% endhighlight %}{: .inlined }
consectetur adipiscing elit.
This works with all kind of objects (tables, images, etc). I can not test it right now, but I think this will solve the issue.
When you test it, look at the output HTML. You will then find that your <div> now has the class=inlined attribute set.

What's the problem with the .highlight div? It's put there to make syntax highlighting easy to theme.
To change the wrapper to a span my bet is that you haveto change Jekyll configuration.
Linenumbers only appear when you have a multi-line snippet.

If you include the following two functions (and call them):
var inlineElements = function() {
var inlinedElements = document.getElementsByClassName('inlined');
inlinedElements = Array.prototype.concat.apply([], inlinedElements); // copy
for (var i = 0; i < inlinedElements.length; i++) {
var div = inlinedElements[i];
var span = document.createElement('span');
span.innerHTML = div.children[0].innerHTML;
var previous = div.previousElementSibling;
var paragraph;
if (previous.tagName.toLowerCase() === 'p') {
paragraph = previous;
} else {
paragraph = document.createElement('p');
div.parentNode.insertBefore(paragraph, div);
}
div.remove();
paragraph.innerHTML += ' ' + span.innerHTML + ' ';
paragraph.classList.add('highlight');
paragraph.classList.add('inlined');
if (div.classList.contains('connectNext')) {
paragraph.classList.add('connectNext');
}
}
}
var connectElements = function() {
while (true) {
var connectNextElements = document.getElementsByClassName('connectNext');
if (connectNextElements.length == 0) break;
var prefix = connectNextElements[0];
var next = prefix.nextElementSibling;
prefix.innerHTML += ' ' + next.innerHTML;
next.remove();
if (!next.classList.contains('connectNext')) {
prefix.classList.remove('connectNext');
}
}
}
inlineElements();
connectElements();
You can use .inline and .connectNext in your Markdown:
{% highlight objective-c %}
[[NSObject alloc] init];
{% endhighlight %} {: .inlined .connectNext }
vs.
{% highlight java %}
new Object();
{% endhighlight %} {: .inlined }
.connectNext will make ensure that the text following the code block is also inlined in the same <p>.

Related

How do I mark a filter as being safe?

I am trying to set up a syntax highlighting filter for nunjucks using highlight.js. It seems pretty easy to do. In my elevnety.js file I included:
const hljs = require('highlight.js');
eleventyConfig.addFilter('highlight', function(txt) {
return hljs.highlightAuto(txt).value;
});
It appears that highlight.js is a safe filter and will properly escape it's contents and add markup to control the highlighting, so there is nothing else to do.
In my njk page I try to use this with
{% filter highlight %}
<xmlstuff>
<myelements attr1="foo" />
</xmlsfuff>
{% endfilter %}
The highlighting markup is being generated correctly, but the whole result is being escaped (by nunjucks perhaps) so the resulting page renders all the markup code. Here is what is getting added to the output html page:
<span class="hljs-tag">&lt;<span class="hljs-name">xmlstuff</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">myelements</span> <span class="hljs-attr">attr1</span>=<span class="hljs-string">"foo"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">xmlsfuff</span>&gt;</span>
I know that nunjucks has a safe filter to prevent this from happening, but I don't know how to apply that to say that my filter block doesn't need escaping, and I could not find anything in the documentation. I tried a number of approaches, but they all failed:
{% filter highlight | safe %}
<xmlstuff>
<myelements attr1="foo" />
</xmlsfuff>
{% endfilter %}
{% filter highlight %}
<xmlstuff>
<myelements attr1="foo" />
</xmlsfuff>
{% endfilter | safe %}
{{ {% filter highlight %}
<xmlstuff>
<myelements attr1="foo" />
</xmlsfuff>
{% endfilter %} | safe }}
Is there any way to mark this filter block as safe?
Try to apply env.filters.safe before output.
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
env.addFilter('safeFilter', str => env.filters.safe(str));
env.addFilter('unsafeFilter', str => str);
var html = env.renderString(
`{{ str | safeFilter }}\n{{ str | unsafeFilter }}`,
{str: '<h1>Hello</h1>'}
);
console.log(html);
#aikon-mogwai posted the correct answer. However for eleventy a bit more is required because the filter needs to be set up in the eleventy.js file and we need to get access to the nunjucks environment. I'll add my complete solution here for posterity.
I does not look like eleventy provides access to the nunjucks environment, so we need to create one and set it to override the existing environment. After that it is pretty well what he said:
module.exports = function(eleventyConfig) {
eleventyConfig.addPassthroughCopy('src/images')
/*
* Create and register a Nunjucks environment, just so we
* can get access to the safe filter.
*/
let Nunjucks = require("nunjucks");
let nunjucksEnvironment = new Nunjucks.Environment(
new Nunjucks.FileSystemLoader("src/_includes"), { }
);
eleventyConfig.setLibrary("njk", nunjucksEnvironment);
/*
* Set up a syntax highlighting filter for code blocks
*/
const hljs = require('highlight.js');
eleventyConfig.addNunjucksFilter('highlight', function(txt, lang) {
var txt2;
if (lang == undefined)
txt2 = hljs.highlightAuto(txt).value;
else
txt2 = hljs.highlight(lang, txt).value;
return nunjucksEnvironment.filters.safe(txt2);
});
return {
dir: { input: 'src', output: 'dist', data: '_data' },
passthroughFileCopy: true,
templateFormats: ['njk', 'md', 'css', 'html', 'yml'],
htmlTemplateEngine: 'njk'
}
}
I achieved this with Eleventy#1.0.0-beta.8 like this:
eleventyConfig.addNunjucksFilter('doThing', function (value) {
return this.env.filters.safe(doThing(value));
})

Shopify Liquid Search.Results - How to Sort by Barcode?

we have been struggling with this problem for weeks and still have not found a solution.
We have a Shopify store with 2000 products and 300 vendors. Each vendor has a "ranking number" which we have assigned to them and entered into the "barcode" field in the variants of every single product.
The goal is that we want Shopify to be able to sort our search results by the Barcode using the liquid "sort" tag like so:
{% assign foo = search.results | sort:'title' %}
However, when we attempt to use this syntax to sort by "barcode" it doesn't return any results
{% assign foo = search.results | sort:'barcode' %}
We have attempted to use 'sort' with all the following arguments but none of them work:
{% assign foo = search.results | sort:'product.barcode' %}
{% assign foo = search.results | sort:'item.barcode' %}
{% assign foo = search.results | sort:'item.variants.first.barcode' %}
{% assign foo = search.results | sort:'items.variants.variant.barcode' %}
etc, but none of them work.
If ANYONE can point us in the right direction for which argument/syntax to use to sort our search results by variant.barcode, it would be MUCH APPRECIATED!!!!!
Thanks
In terms of native functionality you're out of luck.
You can do something like this though by rolling your own.
Manually sort your collections by vendor rank
Limit your search to collection results by creating your own search function
or
creat your own search function but make it a "filter" on your current collection and leave the native search alone.
Create Your Own Search:
Put your item results cell in a snippet. collection-product.liquid
Include that snippet in
your collecton liquid.
Make a new collection template
(collection.search-results.liquid)
collection.search-results.liquid looks something like the following. Your pagination size should match the pagination size of your main collection.
{% layout none %}
{% paginate collection.products by 50 %}
<div class="search-results">
{% for product in collection.products %}
{% include 'collection-product' %}
{% endfor %}
</div>
{% endpaginate %}
Now your search becomes an input box with some javascript. The fun part. The script below was pulled from a working site. I've trimmed it up somewhat to hopefully make the bones of it clearer but it probably won't run as is. I've removed references to adding a search because for your purposes you want the native custom search.
As to why not just sort the results. You could use this concept to do that but it returns results a page at a time. In order to custom search all the results you'd have to accumulate the results and sort them when the search is finished. Search time then depends heavily on the size of the collection and the user's network speed.
var defaultView = $(".filters-off"); // add this class to your main collection wrapper
var facetView = $("#filter_target"); // <div id="filter_target"></div> below your main collection wrapper
var currentRequest = 0;
function filterCollection(term){
//[[t1, t2], [t3,t4]]
// console.log('applying '+ JSON.stringify(facets));
var anyResults = false;
var resultsPage=0;
var PAGE_SIZE = 50;
var productsFound = 0;
var collectionPath = location.pathname;
console.log('get from '+ collectionPath);
currentRequest = new Date().getTime();
var viewLink = collectionPath+'?view=search-results'; // same as your layout none template
function applyFilter(myRequest){
resultsPage++;
if(resultsPage > 20) return false; // arbitrary abort for too many results
if(resultsPage > 1) viewLink+='&page='+resultsPage;
return $.get(viewLink).then(function(page){
if(currentRequest != myRequest){
console.log('mid abort');
return false;
}
var pageProducts = $("div[data-tags]", page); //some markup you added to collection-product snippet to help plucking the product elements
if(!pageProducts.length) return false;
console.log('found: '+ pageProducts.length);
var filteredProducts = pageProducts.filter(function(){
if($(this).text().indexOf(term) != -1) return true; // search the returned text
if($(this).attr('data-keywords').indexOf(term) != -1) return true;
return false;
});
if(filteredProducts.length){
if(!anyResults){
anyResults = true;
toggleView(true);
}
filterView.append(filteredProducts);
productsFound+= filteredProducts.length;
}
return (pageProducts.length == PAGE_SIZE && currentRequest == myRequest) ? applyFilter(myRequest) : false;
}).then(function(proceed){
if(currentRequest == myRequest){
if(!anyResults){
toggleView(false, true);
}
}
});
}
applyFilter(currentRequest);
}
function toggleView (showFacets, showNoGo){
facetView.empty();
$(".filter-progress").empty();
if(showFacets) {
defaultView.add('.pagination').hide();
}else {
if(!showNoGo){
defaultView.add('.pagination').show();
}else {
$(".no-facets").clone(true).appendTo(facetView).show(); // .no-facets is normally hidden chunk that allows for easy internationaliztion of "No results found" type messages
}
}
};

Is it possible to use vue-specific directives in localized text?

I just started using vue-i18n and tried to use the v-on-directive (shorthand: #) in my language specific text.
What i tried to do:
// locale definition
let locale = {
en: {
withEventListener: 'Some HTML with <a #click="onClickHandler">event handling</a>'
}
}
and the vue template:
<!-- vue template be like -->
<p v-html="$t('withEventListener')" />
This doesn't throw an error but unfortunately it does not get evaluated by vue-js either. This would result to Plain-HTML like:
<p>
Some HTML with <a #click="onClickHandler">event handling</a>
</p>
So my question is if there is a way to make Vue 'evaluate' the text and thus 'translate' the directives within the text.
You can use Vue.compile to do something like this if you are including the standalone build script. I'm not familiar with vue-i18n, but this might put you on the right path.
Note that I had withEventListener to wrap it in a div because of the rules around templates.
let locale = {
en: {
withEventListener: '<div>Some HTML with <a #click="onClickHandler">event handling</a></div>'
}
}
const res = Vue.compile(Vue.t("withEventListener"));
Vue.component("internationalized", {
methods:{
onClickHandler(){
alert("clicked")
}
},
render: res.render,
staticRenderFns: res.staticRenderFns
})
new Vue({
el:"#app"
})
With the template
<div id="app">
<internationalized></internationalized>
</div>
Working example.

Drupal 8 img as backgroundimage

I'm making my own drupal 8 theme. This is the first time I use drupal. But I have the following problem: I would like to show the article's image as a background image. In Drupal 7 (I have found) you could do that with the following preprocessor, but how do you achieve this in drupal 8?
function THEME_preprocess_page(&$vars) {
$node = menu_get_object('node');
if (!empty($node) && !empty($node->field_background_image)) {
$image = file_create_url($node->field_background_image[LANGUAGE_NONE][0]['uri']);
drupal_add_css('div#content {background: url(' . $image . ') no-repeat scroll center center / cover #FCFCFC; }', array('type' => 'inline'));
}
}
Thank you
You can get an image field in a background by just doing some tweak with twig.
In a Node-template.html.twig you can get this like.
{% set background_image = node.yourimage_field|striptags|trim %}
OR if the above line does not help then just replace node.yourimage_field|striptags|trim with content.youimage_field|striptags|trim OR you can check the array path to variable by just enabling the module deval and then in your node template file type {{ kint(content) }}
OR {{ kint(node) }}
<div style="background-image:url({{ background_image }});"></div>
In a views-template.html.twig you can do some like this.
{% set background_image = fields.yourimage_field.content|striptags|trim %}
<div style="background-image:url({{ background_image }});"></div>
And That's it.
Adding inline css or js can be achived by adding an '#attached' key to your render array.
$elemnt['#attached']['css_inline'] = // Your css
See https://www.drupal.org/node/2391025
If you use twig for templating you can pass the image url as a variable with th $vars array and use it in in your template.
Anyway, drupal_add_css and drupal_add_js function aren't availablein Drupal 8
There are lots of contributed modules that allow an image field to be rendered as a background image. Using these modules would likely not require any custom code.
My personal favorite is Layout BG. Here is a page with links to several other modules that make it easy to render an image as a background image using a Field Formatter: https://www.drupal.org/docs/contributed-field-formatters/image#s-background-image
You have to write this code in your theme_name.theme file. It is the same as template.php in Drupal 7
function THEME_preprocess_page(&$vars) {
$node = Drupal::request()->attributes->get('node');
if($node){
if ($node->bundle() == "articles")) { // Content type check
$uri = $node->field_background_image->entity->getFileUri();
$image = file_create_url($uri); // Creating file path from uri
$vars['articles'] = true; // Setting value true for current node is articles.
$vars['background_img'] = $image;
}
}
}
Now in your page.twig.html
{% if blog %} // Accessing the blog variable from $vars
<div style="background: url({{ background_img }})"> // Accessing the background_img variable from $vars
// Other content
</div>
{% endif %}
Don't forget to clear cache!
Hope it helps!

Polymer and Disqus on Firefox: Argument 1 of Window.getComputedStyle does not implement interface Element

I'm currently building a website using Polymer framework and Jekyll. I've added the Disqus block in the bottom of the page and it works perfectly in Chrome browser.
{% if site.disqus %}
<div class="comments">
<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_shortname = '{{ site.disqus }}';
var disqus_identifier = "{{ page.url }}";
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the comments powered by Disqus.</noscript>
comments powered by <span class="logo-disqus">Disqus</span>
</div>
{% endif %}
But when I open it on Firefox 35.0.1 I receive the following message in console:
TypeError: Argument 1 of Window.getComputedStyle does not implement interface Element.
and Disqus comments don't load. I've found the same behavior for Disqus comments on Polymer docs website, so you can check it out. Any ideas how it can be fixed? Or this is a bug?
I was running into this exact issue, and came across this thread:
https://github.com/webcomponents/webcomponentsjs/issues/89
Mikanoshi provides a helpful gist at the end of the thread that illustrates a fix:
var disqus_shortname = ''; /* Your shortname */
var DISQUS = ... /* From the start of DISQUS' embed.js */
(function(document) {
DISQUS.define(... /* The rest of DISQUS' embed.js */
})(wrap(document));
Unfortunate that we have to modify DISQUS' embed.js, but it seems to work!
An explanation of why this is necessary is here: http://webcomponents.org/polyfills/shadow-dom/ (under wrap and unwrap).

Resources