I have a ruby on rails app, and i'm using HAML for HTML structure,
I'd like to minify/uglify the output "html", remove unnecessary whitespaces and new lines.
Something like this:
<div class='wrapper v2-header-wrapper' id='fix-content'><div class='header header-search' style='text-align: center !important;'><nav class='navbar navbar-toggleable-md navbar-light bg-faded navbar-expand-lg'><button class='navbar-toggler navbar-toggler-right' onclick='openNav()' type='button'><span class='navbar-toggler-icon'><i class='fa fa-bars'></i></span></button><a class='navbar-brand mobile pull-left' href='/'><i class='fa fa-search'></i>
Instead of this:
<div class='wrapper v2-header-wrapper' id='fix-content'>
<div class='header header-search' style='text-align: center !important;'>
<nav class='navbar navbar-toggleable-md navbar-light bg-faded navbar-expand-
lg'>
<button class='navbar-toggler navbar-toggler-right' onclick='openNav()'
type='button'>
<span class='navbar-toggler-icon'>
<i class='fa fa-bars'></i>
</span>
</button>
<a class='navbar-brand mobile pull-left' href='/'>
<i class='fa fa-search'></i>
Your help is highly appreciated, thanks in advance.
Have a go at this:
app/middleware/html_minifier.rb
class HtmlMinifier
def initialize(app)
#app = app
end
def call(env)
# Call the underlying application, return a standard Rack response
status, headers, response = #app.call(env)
# Make sure we don't process CSS or JavaScript
if headers["Content-Type"] =~ /text\/html/
response.each do |chunk|
[
# Join lines
[/[\r\n]+/, ""],
# Remove whitespace between tags
[/>\s+</, "><"],
# Remove comments
[/<!--(.|\s)*?-->/, ""],
# Remove whitespace in inline JavaScript
[/;\s+/, ";"],
[/{\s+/, "{"]
].each do |regex, substitute|
chunk.gsub! regex, substitute
end
end
end
# Return the new Rack response
[status, headers, response]
end
end
There is a Gem for that. Just add this to your Gemfile:
gem 'htmlcompressor', '~> 0.4.0'
And this to your application.rb:
config.middleware.use HtmlCompressor::Rack
I'm using that within a Rails 6 application and it worked out of the box.
I would also recommend to enable GZip compression. You can do that with this middleware:
config.middleware.use Rack::Deflater
No external Gem needed for the last middleware.
Related
I am trying to render an image on my React Web App, but the image is broken. However, when I do a console.log of the file's path, I see the following which seems to be correct.
We are using url-loader and file-loader with webpack, and am importing the file path directly in the import statements as required. I have tried adding required ('imageFilePath.png'), but that did not work either.
Currently, the image is placed directly in the src folder along with App.js.
Here is my App.js:
import React, { Component } from 'react'
import './App.css'
import {Tweet} from 'react-twitter-widgets';
import logoFinal from './logoFinal.png';
class App extends Component {
render () {
const { error, isLoaded} = this.state;
if (error){
return <div> Error: {error.message}</div>
} else if (!isLoaded){
return <div>Loading...</div>
} else{
return (
<div className="container">
<nav className="navbar navbar-expand-lg navbar-light fixed-top">
<div className="container">
<a className="navbar-brand pull-left" href="/home">
<div>
<img src={require('./logoFinal.png')} width='100' margintop='-7' /></div></a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNavAltMarkup">
<div className="navbar-nav">
<a className="nav-item nav-link active" href="#">Home <span className="sr-only">(current)</span></a>
<a className="nav-item nav-link" href="#">By State</a>
</div>
</div>
</div>
</nav>
And then in my webpack.config.js I have the following in the module section:
{
test: /\.(png|jp(e*)g|svg)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8000, // Convert images < 8kb to base64 strings
name: 'images/[hash]-[name].[ext]'
}
}]
},
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
// "url" loader works like "file" loader except that it embeds assets
// smaller than specified limit in bytes as data URLs to avoid requests.
// A missing `test` is equivalent to a match.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
Welcome to StackOverflow!
With this line, you have imported the image.
import logoFinal from './logoFinal.png';
The logoFinal variable will be the actual image-path, which will be generated by webpack. So you can use it in the <img /> tag as the src attribute.
<img src={logoFinal} />
You webpack config looks weird, because you have defined two rules for images. The later one with require.resolve('url-loader') seams to be correct.
I'm working with Natalie. I've removed the extra url-loader in the web pack and kept the second. I've tried the suggestions and the image still renders as broken. I see the img in the html as this: http://localhost:3000static/media/logoFinal.fb830421.png
I've also attempted to use file-loader.
I am creating a Twitter Tweet Analysis web page. I want to localize the page based on the user selection. On the header I am providing a nav bar : Country which lists different locales. Based on the country selected by the user, the entire webpage should get translated.
Here is my HTML header:
<header>
<div class="navbar navbar-default navbar-fixed-top">
<div class="navbar-header">
<a class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="navbar-brand" href="#">Twitter Analysis</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav ">
<li class="dropdown">
Country
<ul class="dropdown-menu">
<li>United States</li>
<li>China</li>
</ul>
</li>
</ul>
</div>
</div>
</header>
Any idea how I should implement this? Please advice. Thanks
You can use i18n-2 module for this.
First, you need to install i18n-2 using npm
$ npm install --save i18n-2
After that, add these config line into your app.js file. Remember to add it after you have loaded the cookieParser.
var cookieParser = require('cookie-parser');
app.use(cookieParser('your secret here')); // put the config after this line
i18n.expressBind(app, {
// setup some locales - other locales default to vi silently
locales: ['vi', 'en'],
// set the default locale
defaultLocale: 'vi',
// set the cookie name
cookieName: 'locale'
});
// set up the middleware
app.use(function(req, res, next) {
req.i18n.setLocaleFromQuery();
req.i18n.setLocaleFromCookie();
next();
});
The i18n object will now reside within the request object of each request. The above config also allows the locale to be set from query string or from cookie. For example, the mysite.com/?lang=en will automatically set the locale to en. To use the i18n object, simply use the __ function
function handlerFunc1(req, res){
res.render('index', { title: req.i18n.__("hello") });
}
Or if you want to use it in your view, simply use __ again
<h1>
<%= __("hello") %>
</h1>
i18n-2 will then look up the key hello in the locale files (by default located in locales/en.js and locales/vi.js). If the keys or the files is not exist yet, it will then create those files and keys automatically for you so you don’t have to worry about the errors. You can then open those files and edit the values already there or add your new one. The file syntax is just normal JSON syntax.
Note: you cannot use // for comments.
{
"hello": "Hello",
"title": "title",
}
To change the language, you can set it directly using setLocale(locale) function. Beside that, you can set the cookie locale value for the browser to remember the current language for the next access.
function handlerFunc(req, res){
// you can set it directly like this
req.i18n.setLocale('vi');
// or set it via the cookie
res.cookie('locale', 'vi');
req.i18n.setLocaleFromCookie();
// redirect back
res.redirect('back');
};
Source - link
I am trying to access sequential elements and don't know how to advance the the script down the page. So, here is a snippet of the code.
<div class="card highlighted">
<div class="card-fix">
<a class="card-remove icon" title="Don't show member again" onclick="$jq( '#search-results' ).msg( 'remove', this )" data-userid="93872246">
X
</a>
<dl></dl>
<div class="card-actions">
<a class="quickview" title="Quick view" onclick="$jq('#dialog-profile').msg('show', '/profileinfo/SearchProfi…lTseEdmEQ==&lid=1&searchType=S&pageNumber=1'); return false;" href="#"></a>
<a class="button button-primary" title="Favorite her!" href="/matchbook/addEntry.aspx?uid=Mu/BwYPn0nxhWlTseEdmEQ==&pn=1&rn=4&tp=S&handle=Annie5170&lid=1065"></a>
</div>
</div>
<div class="card-fix">
<a class="card-remove icon" title="Don't show member again" onclick="$jq( '#search-results' ).msg( 'remove', this )" data-userid="108272583"></a>
<dl></dl>
<div class="card-actions">
<a class="quickview" title="Quick view" onclick="$jq('#dialog-profile').msg('show', '/profileinfo/SearchProfi…DA3R3daaQ==&lid=1&searchType=S&pageNumber=1'); return false;" href="#"></a>
<a class="button button-primary" title="Favorite her!" href="/matchbook/addEntry.aspx?uid=y98x/Mj+hc+61DA3R3daaQ==&pn=1&rn=4&tp=S&handle=dancer4498&lid=1065"></a>
</div>
</div>
In other words I need to continue looping through these quickviews and then running a small script and doing it all again. Then go to the next page and do it again...etc. However, if I use this now in a loop it will only pick up the first quickview 5 times.
# 5.times do |n|
# browser.link(:title => 'Quick view').click
# sleep (2)
# browser.link(:class => "icon dialog-abandon").click
# # sleep (2)
# end
So my question is how can I have it advance through all of the quick views which there are 18 per page.
It would also be great to be able to save the
data-user-id="93872246"
and the portion after the handle in the href, ie.
handle=Annie5710
in a file in a hash as well.
Thanks and I appreciate all of the help.
You have to do this:
//get total title in a page
totalTitleSize=browser.link(:title => 'Quick view').size
and looping it:
totalTitleSize.times do |n|
browser.link(:title => 'Quick view', :index=> n).click
sleep (2)
//test it or use index in it If you need
browser.link(:class => "icon dialog-abandon").click
sleep (2)
end
this should be added to the code.
browser.link(:title => 'Quick view', :index=> n).click
now everything works fine.
As the other answers show, the problem is that the line:
browser.link(:title => 'Quick view').click
Will always click the first matching link. Using the :index allows you to override the default first match. However, I would argue the better solution is to get a collection of matching links and iterate through that. It will save you from manually handling the index.
browser.links(:title => 'Quick view').each do |link|
link.click
sleep (2)
browser.link(:class => "icon dialog-abandon").click
sleep (2)
end
Notice here that we have created a collection using links that we iterate though using each.
However, since you also want to collect other data, you might want to iterate through the parent divs:
browser.divs(:class => 'card-fix').each do |parent_div|
puts parent_div.link(:class => 'card-remove').data_userid
#=> "93872246"
href = parent_div.button(:class => 'button-primary').href
puts href[/handle=([^&]+)/, 1]
#=> "Annie5170"
parent_div.link(:class => "quickview").click
sleep (2)
browser.link(:class => "icon dialog-abandon").click
sleep (2)
end
Well this is probably kind of a silly question but I'm wondering if there's any way to have the generated markup in Jekyll to preserve the indentation of the Liquid-tag. World doesn't end if it isn't solvable. I'm just curious since I like my code to look tidy, even if compiled. :)
For example I have these two:
base.html:
<body>
<div id="page">
{{content}}
</div>
</body>
index.md:
---
layout: base
---
<div id="recent_articles">
{% for post in site.posts %}
<div class="article_puff">
<img src="/resources/images/fancyi.jpg" alt="" />
<h2>{{post.title}}</h2>
<p>{{post.description}}</p>
Read more
</div>
{% endfor %}
</div>
Problem is that the imported {{content}}-tag is rendered without the indendation used above.
So instead of
<body>
<div id="page">
<div id="recent_articles">
<div class="article_puff">
<img src="/resources/images/fancyimage.jpg" alt="" />
<h2>Gettin' down with responsive web design</h2>
<p>Everyone's talking about it. Your client wants it. You need to code it.</p>
Read more
</div>
</div>
</div>
</body>
I get
<body>
<div id="page">
<div id="recent_articles">
<div class="article_puff">
<img src="/resources/images/fancyimage.jpg" alt="" />
<h2>Gettin' down with responsive web design</h2>
<p>Everyone's talking about it. Your client wants it. You need to code it.</p>
Read more
</div>
</div>
</div>
</body>
Seems like only the first line is indented correctly. The rest starts at the beginning of the line... So, multiline liquid-templating import? :)
Using a Liquid Filter
I managed to make this work using a liquid filter. There are a few caveats:
Your input must be clean. I had some curly quotes and non-printable chars that looked like whitespace in a few files (copypasta from Word or some such) and was seeing "Invalid byte sequence in UTF-8" as a Jekyll error.
It could break some things. I was using <i class="icon-file"></i> icons from twitter bootstrap. It replaced the empty tag with <i class="icon-file"/> and bootstrap did not like that. Additionally, it screws up the octopress {% codeblock %}s in my content. I didn't really look into why.
While this will clean the output of a liquid variable such as {{ content }} it does not actually solve the problem in the original post, which is to indent the html in context of the surrounding html. This will provide well formatted html, but as a fragment that will not be indented relative to tags above the fragment. If you want to format everything in context, use the Rake task instead of the filter.
-
require 'rubygems'
require 'json'
require 'nokogiri'
require 'nokogiri-pretty'
module Jekyll
module PrettyPrintFilter
def pretty_print(input)
#seeing some ASCII-8 come in
input = input.encode("UTF-8")
#Parsing with nokogiri first cleans up some things the XSLT can't handle
content = Nokogiri::HTML::DocumentFragment.parse input
parsed_content = content.to_html
#Unfortunately nokogiri-pretty can't use DocumentFragments...
html = Nokogiri::HTML parsed_content
pretty = html.human
#...so now we need to remove the stuff it added to make valid HTML
output = PrettyPrintFilter.strip_extra_html(pretty)
output
end
def PrettyPrintFilter.strip_extra_html(html)
#type declaration
html = html.sub('<?xml version="1.0" encoding="ISO-8859-1"?>','')
#second <html> tag
first = true
html = html.gsub('<html>') do |match|
if first == true
first = false
next
else
''
end
end
#first </html> tag
html = html.sub('</html>','')
#second <head> tag
first = true
html = html.gsub('<head>') do |match|
if first == true
first = false
next
else
''
end
end
#first </head> tag
html = html.sub('</head>','')
#second <body> tag
first = true
html = html.gsub('<body>') do |match|
if first == true
first = false
next
else
''
end
end
#first </body> tag
html = html.sub('</body>','')
html
end
end
end
Liquid::Template.register_filter(Jekyll::PrettyPrintFilter)
Using a Rake task
I use a task in my rakefile to pretty print the output after the jekyll site has been generated.
require 'nokogiri'
require 'nokogiri-pretty'
desc "Pretty print HTML output from Jekyll"
task :pretty_print do
#change public to _site or wherever your output goes
html_files = File.join("**", "public", "**", "*.html")
Dir.glob html_files do |html_file|
puts "Cleaning #{html_file}"
file = File.open(html_file)
contents = file.read
begin
#we're gonna parse it as XML so we can apply an XSLT
html = Nokogiri::XML(contents)
#the human() method is from nokogiri-pretty. Just an XSL transform on the XML.
pretty_html = html.human
rescue Exception => msg
puts "Failed to pretty print #{html_file}: #{msg}"
end
#Yep, we're overwriting the file. Potentially destructive.
file = File.new(html_file,"w")
file.write(pretty_html)
file.close
end
end
We can accomplish this by writing a custom Liquid filter to tidy the html, and then doing {{content | tidy }} to include the html.
A quick search suggests that the ruby tidy gem may not be maintained but that nokogiri is the way to go. This will of course mean installing the nokogiri gem.
See advice on writing liquid filters, and Jekyll example filters.
An example might look something like this: in _plugins, add a script called tidy-html.rb containing:
require 'nokogiri'
module TextFilter
def tidy(input)
desired = Nokogiri::HTML::DocumentFragment.parse(input).to_html
end
end
Liquid::Template.register_filter(TextFilter)
(Untested)
In coding layout is the link styled like
<span>Previous</span>
<a href="#" class="class1 class2 class3"><span>Next</span>
I tried to search and I found some possible modifications of a shape of these two links. It's:
<div class="pag">
<%= will_paginate #data, :page_links => false, :next_label => '' %>
</div>
<div class="pag">
<%= will_paginate #data, :page_links => false, :previous_label => '' %>
</div>
My question: How can I update the second example to the shape of the first example? My problem is those two span elements...
Thanks
It's can be very easy - just add html markup for your paginate components to your config/locales/en.yml file
for example:
en:
hello: "Hello world"
will_paginate:
previous_label: <span class="glyph glyph-prev"></span>
next_label: <span class="glyph glyph-next"></span>
It's all!
http://thewebfellas.com/blog/2010/8/22/revisited-roll-your-own-pagination-links-with-will_paginate-and-rails-3 You can have your own formatter so you can customize anything you want.
Wiki
en:
will_paginate:
previous_label: "← Previous"
next_label: "Next →"
page_gap: "…"
https://github.com/mislav/will_paginate/wiki/I18n#translating-will_paginate-output