Issues with .js files/polyfills that have dependencies that must be in the same folder

#Problem

The problem is pretty straight forward: I am attempting to implement .js libraries that require non-js files to be in the same directory in order to work.

An example of this is the webpjs javascript library which polyfills the ability for the next-generation Webp(Weppy) image format be supported by browsers that don’t natively support it, such as Firefox, desktop and mobile versions safari, and even IE6.

I’ve been able to verify the library works outside of Rails by merely following the defacto instructions of including the library and its dependency, a .swf file, in the same directory and attaching both files in the header or footer of the .html/.haml file. I’ve been able to even have the library mentioned and verified to be valid to be mentioned on CanIUse.com after testing the library on iOS Safary, IE6, IE8 and 10; and Firefox.

Nonetheless, when I attempt to integrate the library in any rails app via the assets/javascripts/ folder, the app doesn’t implement the library correctly (including the shouter app everyone who participated in Thoughtbot’s Intermediate Rails Course created) .

What I’ve attempted to do to solve the problem with no luck:

Naturally I attempted seeing if the removal of the sprocket directive //= require_tree . and manually inserting the library in the application.html.haml file with the following link directive would work:

    = javascript_include_tag "webpjs-0.0.2", "data-turbolinks-track" => true 

Unfortunately, I’ve had no luck getting this to work either. I’m begun to think whether there’s an implicit expectation for a folder called js these files are stored in being the issue given the asynchronous version I don’t use but is still featured on the website being the following:


<script>(function(){var WebP=new Image();WebP.onload=WebP.onerror=function(){
if(WebP.height!=2){var sc=document.createElement('script');sc.type='text/javascript';sc.async=true;
var s=document.getElementsByTagName('script')[0];sc.src='js/webpjs-0.0.2.min.js';s.parentNode.insertBefore(sc,s);}};
WebP.src='data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';})();</script>

Again I know it’s not the format or files, being able to successfully hook the library up to my XAMP local server configuration and including the files both outside of Rails, as well as within Wordpress, with no problems.

In addition, ImageMagick was able to handle the format just fine within my style directive below involving PaperClip:

class PhotoShout < ActiveRecord::Base
  has_attached_file :image, styles: {
    shout: ["200x200>", :webp]
  }
end

In general, trying out similar js libraries that have non .js dependencies and having the same issue, I thought this validates a forum entry.

Any help the next few days would be greatly appreciated.

Update 1: Seems the MIME-TYPE in the headers is ‘plain’ which is rather odd; seems I have to alter my local server’s or RACK’s MIME-TYPE file (or both)? . Would appreciate any ideas on which would be the correct course of action.

Attempted to do the following within Rails to see if it’ll fix the problem:

Rack::Mime::MIME_TYPES[".webp"] = "image/webp"

Update 2 (Partial solution) : As stated by @cpytel and @halogenandtoast, non-js files should never be inside the assets/javascripts/, regardless if a JavaScript file depends on it . Such files are appropriate to be in the public folder.

The only problem now seems to be related to “fingerprinted” .webp fies. Based on what’s been investigated so far, the library has a brittle way of identifying what’s .webp files and what aren’t through a substring if/else clause instead of a more surer way such as a regular expression test.

As a result, I"m testing more rigid ways to test whether an image is a webp image or not . From there I’ll issue a ticket/pull request on Github for the creator to then look into towards an improved version of this otherwise excellent library.

@kevinlozandier Can you post the link to the code and where it is deployed? Thanks.

Hi @cpytel: https://github.com/lozandier/shouter_being_debugged

Where is it deployed?

Didn’t deploy it, the problem doesn’t seem to be environment-centric; I also used rake to compile the assets production-style as a test to see if that was the problem hosting the site via my local server, but no luck doing that either.

The link to the Github version isn’t restricted, right?

Probably can test it on Heroku, but have no idea how to update their version of ImageMagick on one that’s not a dinosaur version (Only 6.68 and above properly support .webp being encoded/decoded from image files). There’s probably a way to fix that I’m sure, but didn’t bother with that yet.

The image output works given the fact that Chrome and Opera, the two browsers that don’t need the webpjs library to natively support Webp, displays the images just fine.

@cpytel It seems to be a “gotcha” sort of problem; I’m not understanding the internals of Rails involving the views, and as a result the library isn’t working.

Only thing I can think of is forcing JavaScript to not use the fingerprint digest on the .swf file the library depends on for IE. How would I do that?

But then again, that doesn’t explain why the library is unable to parse through the document at all for its effect (the .swf is only for IE). If it was erroring out after being unable to locate the .swf file, The Chrome Dev Tools console would have notified me of an uncaught exception.

As stated before, trying the library with no backend, wordpress, node.js, and even spagetti PHP with no issues, it seems it involves a gotcha involving the rendering of the views.

Will see if an active Rails debugger such as better_errors will help me sort out the problem in the meantime; bummed I can’t use the technology under Rails for all the image-heavy projects I wanted to pursue before the end of the summer.

The reason why I wanted to see the deployed version was not because I thought it was environment specific, it is so that I have something to look at to help you debug. It takes a lot of time for me to set up your application and get it working.

Hi @cpytel:

Okay, let me clarify what’s the problem; the webpjs library can be found here with a file called webpjs.0.0.2.min.js & a .swf file with the same name.

Example of what’s supposed to happen

Suppose I had the following markup ignoring any backend and including various, alternate ways of including the script haml commented out:

!!! 5 
%html{:lang => "en"} 
%head 
  %meta{:charset => "UTF-8"} 
  %meta{content: "width=device-width", name: 'viewport'}
  %link{:rel => "stylesheet", :href => "./stylesheets/main.css"}/ 
  %script{ src: 'javascripts/webpjs-0.0.2.min.js'}
  -# -:javascript
  -#   (function(){var WebP=new Image();WebP.onload=WebP.onerror=function(){
  -#   if(WebP.height!=2){var sc=document.createElement('script');sc.type='text/javascript';sc.async=true;
  -#   var s=document.getElementsByTagName('script')[0];sc.src='js/webpjs-0.0.2.min.js';s.parentNode.insertBefore(sc,s);}};
  -#   WebP.src='data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';})();
  %title WebP test 
  %img{src: "images/download.webp", alt: "Hopefully this is fixed"} 
%body 
    

In browsers that lack native support for the Webp image format --whether it’s IE6, Firefox, or even the infamous Android browsers–the following should be the output generally to any image that’s a webpjs file on the fly:



<!DOCTYPE html>
<html lang='en'></html>
<head>
  <meta charset='UTF-8'>
  <meta content='width=device-width' name='viewport'>
  <link href='./stylesheets/main.css' rel='stylesheet'>
  <script src='javascripts/webpjs-0.0.2.min.js'></script>
  <title>WebP test</title>
  <img alt='Hopefully this is fixed' src='data:image/png;base64,iVBOR…3DRogpFZntpgAAAAASUVORK5CYII'>
</head>
<body></body>

It’s pretty straightforward what’s going on here: The webpjs library will convert the .webp image to an optimized version of the image typically as a .png or .jpg image.

I’ve tested this library on a variety of alternate backend frameworks and languages with no issues (Node.js (Express), Wordpress, and spaghetti PHP) until I tried to utilize it with Rails. I’m inclined to say it has something to do with the asset pipeline or the rendering system I’m not understanding. Considering I tried defer, async, and other javascript tricks, I’ do not know any other alternaties to force the script to execute differently if it was the problem.

In general, I have issues how to incorporate libraries that have non-js files as a fallback [which doesn’t seem to be issue, because only ancient versions of IE need that file; doesn’t explain why it’s not working on Firefox within the Rails application(s)].

As a result, I cannot use

ActionView::Helpers::AssetTagHelper.register_javascript_expansion webpjs: ["webpjs.0.0.2.min", "file2", "file3"]
javascript_include_tag :webpjs 

That helper is to group.js files together rather than group it with another type of file related to it.

I only utilized the “shouter” app for the sake of clarity as a result of the intermediate rails course. Perhaps since @halogenandtoast teaches the class, he can immediately focus on this rendering issue?

Here’s a quick way to get this working with what you’ve done so far. First you don’t want a swf file in app/assets/javascripts. So what I did was move both the js file and swf file to public/js (only because that’s what the examples on the page show, from what I’ve read these two files just need to be in the same directory). I then added the following haml

:javascript
  (function(){var WebP=new Image();WebP.onload=WebP.onerror=function(){
  if(WebP.height!=2){var sc=document.createElement('script');sc.type='text/javascript';sc.async=true;
  var s=document.getElementsByTagName('script')[0];sc.src='js/webpjs-0.0.2.min.js';s.parentNode.insertBefore(sc,s);}};
       WebP.src='data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';})();

And that seems to have worked for me.

I wish I had seen Matt’s comment on this before I worked on it tonight :wink:

I experimented with this as well, and came to the same conclusion as Matt. You’re not going to be able to use the asset pipeline for this because of the path expectations, but you’re also going to run into an issue that your assets are going to have hashes in the filenames in the production environment.

So you’ll need to put your files in public.

thanks!
-Chad

Thanks Chad & Matthew for the solution!

@cpytel & @halogenandtoast: I thank you both; I owe you both a lot. This has been such a pain in the rear-end for so long. Thank you senseis, it’s time to kick butt with WebP images.

@halogenandtoast, you’re right about these two files needing to be in the same directory.

Understanding this solution further

For educational purposes, why is it that app/assets/javascripts filter out any non-javascript files but not the public/js folder (or any folder in the public/ directory. The folders in thi direcotry I can guess are merely arbitrary containers so I can call it /jakkascript and refer to these folders with no problem? I didn’t see a log abou this happening in the command prompt.

I read the Action View Overview, Layout and Rendering in Rails, and Working with JavaScript in Rails guides (though I think Klabnik updated some of them yesterday apparently) before and referred back to them during this problem; what’s the best way to understand this nuance of the asset pipeline system?

Uh oh… Images inside partials don’t seem to work.

Hi @halogenandtoast and @cpytel: It seems that image inside partials don’t seem to be able to be manipulated as they should (only paperclip-created images are inside the partials) but images outside the partials with the use of image_tag("file_name.webp") seem to work fine.

For example I had an image called cat.webp inside of /assets/images/ that rendered find inside image_tag("cat.webp")

Any idea why that is; is this what you meant @cpytel regarding problems related to path expectations? (Do note I would use the aws-sdk gem in production).

I don’t know exactly what you mean isn’t working. Can you provide more details. You have details about what is working, can you provide those details about what is not working?

What does the image_tag call like and what is the path expanded to in the rendered html? Does that image actually exist?

@kevinlozandier the files in the public folder don’t undergo the asset pipeline process. The reason the swf file is ignored inside of the app/assets/javascripts folder is because there are no registered handlers for swf files (and on that note there shouldn’t be). The point of the asset pipeline is two-fold, to allow you to use your preferred syntax (e.g. coffeescript or sass) and to compress the assets into a single file (this won’t happen on development).

For these reasons it doesn’t make sense to put the swf file into the asset pipeline. This was the reason I put it into the public folder. Similarly you shouldn’t put JavaScript libraries from third parties into app/assets, at the very least they should go into vendor/assets, but that aside, the reason you won’t see anything in the log about files in public is because all files in the public directory bypass the rails stack (including logging). This is because of the middleware ActionDispatch::Static which is on top of the rails middleware stack, if it locates a file inside of the public folder it will serve the file and quit.

As to a guess to your problems. When I was messing around with it last night, I noticed that imagemagick (the version I had installed at least), wouldn’t recognize webp images and therefore wouldn’t attach them to my PhotoShout model, so I’d suggest making sure the image_tag itself is showing up WITH an actual URL being rendered in the HTML. It might not be your problem, but it’s a guess, some more details about what you’re seeing will definitely help @cpytel or I in diagnosing what might be happening.

1 Like

Hi @halogenandtoast: ImageMagick releases earlier this year support the WebP image format; the most recent versions (finally) provide support for the format on Windows platforms.

##Regarding Image Tags

The problem is sort of in the air given the use of image_tag is the same outside of the partials as it is inside the partials.

For example, I can include a webp image with the path app/assets/images and call it the following way with no problems

= image_tag("cat.webp")

However, the photoShouts with the following image_tag declaration won’t work

= link_to image_tag(photo_shout.image.url(:shout)),  photo_shout.image.url

Potential Solutions

As a result, during my mentorship call with @cpytel yesterday, investigating the .js file may be necessary which would require the unminified version. This version has been hard to find; though I can un-minify it using a un-minifier, the ambiguous variables probably would make it very hard to debug if investigation on how it’s decoding .webp images.

Nonetheless the general way the library works is use the Libwebpjs library to decode all images with the extension of .webp it finds in the DOM.

The asynchronous version of the webpjs library checks if WebP is an image format the browser supports; Modernizr also includes similar logic with its implementation of yepnope testing for the webp image format.

Either way, the libwebpjs decodes the webp images to be .png files when necessary; the webpjs has special logic for OLDIES that made the swf file necessary to accommodate such ancient browsers. .

I took a look at the unminified source.

It uses document.images to find all the images. The first thing I would check is to make sure that document.images contains the images in question. However, I bet it does.

For each image in document.images it then does the following check:

(a.substr(a.length - 5, a.length) == ".webp")

This is in the IsWebPFile function. In the code above, a is the src attribute of the image. So its checking whether the last 5 characters of the image are .webp. This means that if it isn’t, its not going to try to convert the image.

I don’t remember what the src attributes of the images were that we looked at, but my guess is that they aren’t matching. And I know that if you start to store the images on S3, they definitely won’t match as images from S3 often have extra query parameters at the end.

1 Like

Very surprising a regular expression wasn’t used to do such a check.

While it’s possible but not recommended to disable the fingerprint digest feature of the assets pipeline, I’m assuming it’s virtually impossible to ensure no query parameters are added to the end of files with Amazon’s S3 service.

Isn’t it odd however the cat.webp (or any webp image in the apps/assets/images folder would have had query parameters attached to their file extension as well yet still work?

@kevinlozandier I had to install Imagemagick specifically with the support.

brew install imagemagick --with-webp

And then I was able to run

identify -format '%wx%h,%[exif:orientation]' app/assets/images/test.webp

Which is what paperclip will use. Then, once I uploaded the image as a PhotoShout it worked fine for me

Good morning. That’s an interesting update. Did you use a browser that’s not Chrome or Opera (the only two desktop browsers that natively support the .webP image format) ?

How are your Paperclip image_tag methods being called?

On my end still no luck. I’m using Paperclip version 3.5.1 . I ran rake paperclip:refresh CLASS="PhotoShout" as well with no success.

Chad’s rationale for why it wasn’t working was strong as well. The substring check being used to check if an image is a .webp image or not is brittle, and such a check would likely fail often on a production site.

Later today, I was going to look into the Javascript nuances I may have to deal with towards applying a regular expression that matches to be true or not the .webp image format instead. (only have done regular expressions in the context of PHP scripts sparingly; as strings added to the HTML5 pattern attribute, and throughout a few Ruby and Ruby on Rails learning experiences).

@kevinlozandier I apologize, I wasn’t using a non-Chrome browser. Using Safari I did run into the issue that the filenames from image_tag end with extensions like foo.webp?23423423. When just using regular HTML image tags though, it still didn’t work it returns undefined for the image src. My brief investigation led to no immediate results.