The Cheeky Monkey Media Blog
A few words from the apes, monkeys, and various primates that make up the Cheeky Monkey Super Squad.
Require JS, Grunt, Bower, Foundation and Drupal – Part 2
September 15, 2015 / Treena BjarnasonWelcome back. If you just found this, you might want to start with Part 1 before reading on.
Okay, now we’re going into our custom theme folder (that’s based on the STARTER foundation sub-theme. Check out Justin’s Blog post, on how to set that up.
So, now that you’ve got your custom sub-theme folder and files set up, we are changing some things, and adding MOAR files.
Drupal — showing it some love.
File: [drupal-root-folder]/sites/all/themes/[themename]/[themename].info
First your .info file. If you’re using foundation, it should have these lines:
; Theme scripts (minified). ; Grunt will compress any custom theme scripts matching the _*.js naming convention; into app.min.js. scripts[] = js/app.min.js
Problem (not really, but it’s not using our workflow)… The issue here is that foundation does a good thing, and compresses all your custom JS into an “app.min.js” file. But, we can’t use require with that, and our require will uglify and compress our custom code anyway. So, let’s comment that out. Put a “;” in front of that “scripts[] = js/app.min.js” line.
Now the template PHP file.
File: [drupal-root-folder]/sites/all/themes/[themename]/template.php
In your custom theme folder, load up the ‘template.php’ file. Add this line to the top under the <php:
include ('theme-functions.php');
Now that theme-functions.php file, that will help Drupal determine if we’re using a ‘development’ environment, or ‘production’.
File: [drupal-root-folder]/sites/all/themes/[themename]/theme-functions.php
/** * debug switch for debug mode */ function cmm_is_dev_mode () { if ( preg_match('/dev$/', $_SERVER['HTTP_HOST']) ) { return true; } else { return false; } }
So, this is the function that helps us define whether or not we are in a ‘development’ environment or a ‘production’ one. Since I like to have all my ‘dev’ environments end in a .dev domain suffix, it works. But you can tailor it to whatever you use for your local dev environment. In the case of this article, I’m using “drupaltest.dev”. Production would presumably be “drupaltest.com”.
/** * Path to theme. */ function cmm_path_to_theme() { return drupal_get_path('theme', 'drupaltest'); }
Pretty obvious what this one does. Gets the path to your custom sub-theme.
/** * Helper functions for Require JS include */ function cmm_rjs_rev() { return cmm_path_to_theme() ? time() : hashDirectory( drupal_realpath(cmm_path_to_theme() . '/js/src') ); } function cmm_path_to_rjs_main() { return '/' . cmm_path_to_theme() . (cmm_is_dev_mode() ? '/js/src' : '/js/dist') . '/main.js?rev=' . cmm_rjs_rev(); } function cmm_path_to_rjs() { return '/' . cmm_path_to_theme() . (cmm_is_dev_mode() ? '/js/src' : '/js/dist') . '/vendor/require.js?rev=' . cmm_rjs_rev(); }
Okay, the first method just returns a random hash string, that we’ll need for cache busting for requireJS.
The second method there just returns the proper path to the main.js file, based on if we are in a ‘production’ environment, or ‘development’ environment.
The third method does the same thing, except for the require.js library file itself.
The last method hasDirectory() is what creates that hash string… Not bothering to print it out here, since… you’ll have it.. you downloaded the source, didn’t you?
Change up some template files.
File: [drupal-root-folder]/sites/all/themes/[themename]/templates/html.php.tpl
I just copied the file from the supplied zurb-foundation templates. And we’re just going to add one line.
<script id="requirejs" data-rev="<?php print cmm_rjs_rev(); ?>" data-main="<?php print cmm_path_to_rjs_main(); ?>" src="<?php print cmm_path_to_rjs(); ?>"></script>
That should look familiar. You can see those methods hard at work, printing out the correct paths, to the require.js file, and your main js file that will hold all your custom code. Put that line in the <head>…</head> area.
We’re almost here. Stuff should be loading up and working in drupal now, but let’s just take this one step further, and try an example out.
File: [drupal-root-folder]/sites/all/themes/[themename]/js/src/main.js
So in this file, we do one more thing to help get rid of that caching that requires js does when we don’t want it, for development.
var __getrev = function () { 'use strict'; var rev = document.getElementById('requirejs').getAttribute('data-rev'); return rev || (new Date()).getTime(); }; requirejs.config({ urlArgs: 'rev=' + __getrev() });
Finally, we can get to our regularly scheduled programming, and load up the dependencies, and JS files. In this example, I just load the ‘homepage’ dependency only IF we are on drupal’s ‘front’ page.
require([ 'jquery', 'underscore', 'intentcontext', ], function ($, _, IntentContext) { 'use strict'; // DOM ready $(function() { // init the DOM elements with intentionJS IntentContext.intent.elements(document); IntentContext.intent.on('desktop', function () { }); IntentContext.intent.on('tablet', function () { }); IntentContext.intent.on('mobile', function () { }); // js loaded only on homepage if ($('body').hasClass('front')) { require(['homepage']); } }); // DOM ready });
And in our homepage.js file.
File: [drupal-root-folder]/sites/all/themes/[themename]/js/src/modules/homepage.js
define([ 'jquery', ], function ($) { 'use strict'; /** * object constructor */ var Homepage = function() { this.init(); }; /** * init homepage module */ Homepage.prototype.init = function() { var self = this; console.log("Homepage JS Init", self) }; /** * DOM ready */ $(function () { var home = new Homepage(); }); });
So this file requires ‘jquery’. We then create a new instance and run it in the .init method — I also console.log the object, just to see it. You don’t have to follow this same prototyping style of JS programming. But, I find it to be a very scaleable/clean way to work with JS.
Hope it wasn’t too painful, happy coding!
*cuddles*