modernizr.module in Modernizr 8
Same filename and directory in other branches
Main module file for Modernizr
File
modernizr.moduleView source
<?php
/**
* @file
* Main module file for Modernizr
*/
// Regular expression to determine which version of Modernizr is installed.
define('MODERNIZR_VERSION_REGEX', '/Modernizr [v]?([\\d\\.]*)/');
// Regular expression to detect valid Modernizr filenames.
define('MODERNIZR_FILENAME_REGEX', '/^modernizr[A-Za-z0-9\\.-]*\\.js$/');
// Our drupal_add_js() and libraries_load() calls use this value to maintain
// consistency between the position of the library and its inline settings.
define('MODERNIZR_SCRIPT_GROUP', JS_LIBRARY - 10);
define('MODERNIZR_SCRIPT_WEIGHT', -100);
// Default drupal_add_js() settings. Used in multiple places
global $_modernizr_js_settings;
$_modernizr_js_settings = array(
'type' => 'file',
'scope' => 'header',
'group' => MODERNIZR_SCRIPT_GROUP,
'weight' => MODERNIZR_SCRIPT_WEIGHT,
'every_page' => TRUE,
'preprocess' => 0,
// This setting is not in Drupal core API. Both Omega 4.x and Aurora 2.x
// use this new flag in order to preserve scripts in the header in Drupal 7.
// If you maintain a Drupal theme you should follow suit :)
'force header' => TRUE,
);
/**
* Implements hook_menu().
*/
function modernizr_menu() {
$items = array();
$items['modernizr_settings'] = array(
'title' => 'Modernizr settings',
'description' => 'Queries Drupal for Modernizr dependencies and generates a custom link to the Modernizr builder.',
'route_name' => 'modernizr_settings',
// 'page callback' => 'modernizr_generate_url',
'file' => 'modernizr.admin.inc',
'type' => MENU_NORMAL_ITEM,
'access arguments' => array(
'administer modernizr',
),
);
return $items;
}
/**
* Implements hook_page_build().
*
* We used to use hook_init(), but that loads the JA files needlessly
* on AJAX requests, private file requests, etc.
*/
function modernizr_page_build(&$page) {
global $_modernizr_js_settings;
// Load Modernizr on the page by invoking our implementation of hook_libraries_info().
//
// We can only use this method when Libraries API 2.0 is installed. Since Libraries 1.0
// did not contain a function called libraries_load(), we must explicitly check for a
// valid function to avoid fatal errors.
//
// @see http://drupal.org/node/1919796
if (module_exists('libraries') && function_exists('libraries_load')) {
libraries_load('modernizr');
}
else {
// No Libraries API? Load the regular way.
drupal_add_js(modernizr_get_path(), $_modernizr_js_settings);
}
// We want Modernizr.load() commands to be issued immediately after the call
// to Modernizr so that they download while the page renders. The overrides
// to $inline_js_settings will format the output as inline JS.
if ($output = _modernizr_load_generate()) {
$inline_js_settings = $_modernizr_js_settings;
$inline_js_settings['type'] = 'inline';
$inline_js_settings['weight'] = MODERNIZR_SCRIPT_WEIGHT + 1;
// Load JS
drupal_add_js($output, $inline_js_settings);
}
}
/**
* Implements hook_permission().
*/
function modernizr_permission() {
return array(
'administer modernizr' => array(
'title' => t('Administer Modernizr'),
'description' => t('Perform administration tasks for Modernizr.'),
),
);
}
/**
* Implements hook_libraries_info().
*
* @return array
*/
function modernizr_libraries_info() {
global $_modernizr_js_settings;
$libraries = array();
$file_name = modernizr_get_filename();
// Define Modernizr within Libraries API
$libraries['modernizr'] = array(
'name' => t('Modernizr'),
'vendor url' => 'http://modernizr.com',
'download url' => 'http://modernizr.com/download/',
'version arguments' => array(
'file' => $file_name,
'pattern' => MODERNIZR_VERSION_REGEX,
),
'files' => array(
'js' => array(
$file_name => $_modernizr_js_settings,
),
),
);
return $libraries;
}
/**
* Returns the full path of modernizr, along with the filename.
*
* @return string
*/
function modernizr_get_path() {
$path =& drupal_static(__FUNCTION__);
if ($path === NULL) {
$paths = array();
// Check for directory specified in hook_libraries_info()
if (module_exists('libraries')) {
$library_path = libraries_get_path('modernizr');
if (file_exists($library_path)) {
$paths[] = $library_path;
}
}
// The best location for downloaded libraries is sites/all/libraries.
if (is_dir('libraries/modernizr')) {
$paths[] = 'libraries/modernizr';
}
// Check inside the module folder itself
// NOTE: this will be removed in 7.x-3.2
$paths[] = drupal_get_path('module', 'modernizr');
// Scan directories for files
$path = _modernizr_scan_for_library($paths);
}
return $path;
}
/**
* Helper function to scan for acceptably named libraries
*/
function _modernizr_scan_for_library($paths) {
$path = '';
foreach ($paths as $p) {
if ($files = file_scan_directory($p, MODERNIZR_FILENAME_REGEX)) {
$path = reset($files)->uri;
break;
}
}
return $path;
}
/**
* Helper function to generate the current filename of
* the active Modernizr library
*/
function modernizr_get_filename() {
// Get the full path to the library,
$full_path = modernizr_get_path();
// Break it up into its directories and file
$file_parts = explode('/', $full_path);
// Isolate the filename
$file_name = $file_parts[count($file_parts) - 1];
return $file_name;
}
/**
* Guesses the modernizr library version.
*
* This function is using a regex, which assumes that the format of the version
* string won't change. If it changes, feel free to submit a bug report.
*
* @return mixed The version number if exists, or a boolean FALSE if it can't be
* determined.
*/
function modernizr_get_version($reset = FALSE) {
$version =& drupal_static(__FUNCTION__);
// if ($version === NULL || $reset == TRUE) {
// if ($cached = cache_get('modernizr_version') && isset($cached->data) && $reset != TRUE) {
// $version = $cached->data;
// }
// else {
// $version = FALSE;
// $modernizr_path = modernizr_get_path();
// if (file_exists($modernizr_path)) {
// $modernizr = file_get_contents($modernizr_path);
// $matches = array();
// preg_match(MODERNIZR_VERSION_REGEX, $modernizr, $matches);
// if (isset($matches[1])) {
// $version = $matches[1];
// if ($version) {
// cache_set('modernizr_version', $version);
// }
// }
// unset($modernizr);
// }
// }
// }
return '2.6.2';
}
/**
* Implements MODULE_preprocess_html().
*/
function modernizr_preprocess_html(&$vars, $hook) {
// This will set up all of our tests for Modernizr.
modernizr_load_data();
}
/**
* A function to generate the load data from the current themes.
*
* Reads async-loaded CSS/JS from theme .info files. Stores info in variable.
* Prints Modernizr.load() calls into drupal_add_js() as inline settings.
*
* @return array
*/
function modernizr_load_data() {
$load =& drupal_static(__FUNCTION__);
if (!isset($load)) {
// This is the first time this is called.
global $base_url, $base_theme_info, $theme_info;
$load = array();
$num_tests = 0;
// Make a list of base themes and the current theme.
$themes = $base_theme_info;
$themes[] = $theme_info;
foreach (array_keys($themes) as $key) {
$theme_path = dirname($themes[$key]->filename) . '/';
if (isset($themes[$key]->info['modernizr'])) {
// Loop through Modernizr calls and assemble Load variable.
foreach (array_keys($themes[$key]->info['modernizr']) as $test) {
// Skip the ['tests'] variable because it is reserved for selecting
// specific tests that Modernizr must include.
if ($test != 'tests') {
// All other entries inside a theme's modernizr[] settings should be scanned
$load[$num_tests]['test'] = $test;
foreach (array_keys($themes[$key]->info['modernizr'][$test]) as $action) {
foreach ($themes[$key]->info['modernizr'][$test][$action] as $asset) {
// First figure out which property we're reading.
// callback/complete need different processing than yep/nope/both/load
$functions = array(
'callback',
'complete',
);
// Is this a function or a resource?
if (in_array($action, $functions)) {
// It's a function
$load[$num_tests][$action][] = _modernizr_sanitize_callback($asset);
}
else {
// It's a resource
$load[$num_tests][$action][] = _modernizr_sanitize_resource($asset, $theme_path);
}
}
}
$num_tests++;
}
}
}
}
}
return $load;
}
/**
* Helper function to render the Modernizr.load() calls.
*/
function _modernizr_load_generate() {
$output = FALSE;
// Get Modernizr.load() calls from the active theme.
$theme = modernizr_load_data();
// Collect data from modules that implement hook_modernizr_load().
$modules = modernizr_load_list();
// Combine the data from the .info file and the Drupal modules.
// Themes go first because they are more visual and in most cases
// it's probably best to load them first. Modules whose assets
// truly need to be loaded first have hook_modernizr_load_alter()
// at their disposal.
$testObjects = array_merge($theme, $modules);
// Build the Modernizr.load() commands.
if (count($testObjects)) {
$num_tests = 1;
$output .= 'Modernizr.load([';
foreach ($testObjects as $load) {
$output .= $num_tests > 1 ? ',' : '';
$output .= '{' . "\n";
$output .= ' test: ' . $load['test'] . ',' . "\n";
// Print each action and its resources
$actions = array(
'yep',
'nope',
'both',
'load',
);
foreach ($actions as $action) {
if (isset($load[$action])) {
// Begin output for this action
$output .= ' ' . sprintf('%-4s', $action) . ': ';
// How many resources for this action?
if (count($load[$action]) == 1) {
// Single resource
$output .= "'" . $load[$action][0] . "',\n";
}
else {
// Multiple resources
$output .= '[';
foreach ($load[$action] as $resource) {
$output .= "'" . $resource . "',";
}
// Truncate last comma
$output = substr($output, 0, -1);
$output .= "],\n";
}
}
}
// Output these two properties without quotes around the output
$callbacks = array(
'callback',
'complete',
);
foreach ($callbacks as $action) {
if (isset($load[$action])) {
// Begin output for this action
$output .= ' ' . sprintf('%-4s', $action) . ': ';
// How many callbacks for this action?
if (count($load[$action]) == 1) {
// Single resource
$output .= $load[$action][0] . ",\n";
}
else {
// Multiple resources
$output .= '[';
foreach ($load[$action] as $callback) {
$output .= $callback . ",";
}
// Truncate last comma
$output = substr($output, 0, -1);
$output .= "],\n";
}
}
}
// Truncate last comma and newline
$output = substr($output, 0, -2);
$output .= "\n}";
$num_tests++;
}
// If more than one test was registered, finish the Array notation.
// Finally, close the Modernizr.load() function parenthesis.
$output .= $num_tests > 1 ? ']' : '';
$output .= ');';
}
return $output;
}
/**
* Implements MODULE_preprocess_maintenance_page().
*/
function modernizr_preprocess_maintenance_page(&$vars, $hook) {
modernizr_preprocess_html($vars, $hook);
}
/**
* Helper function to sanitize Modernizr.load() callbacks
*/
function _modernizr_sanitize_callback($callback) {
global $base_url;
$output = '';
$function_regex = '/^function(\\s)*\\(\\)(\\s)*\\{(.*)\\}$/';
// Save the people who don't wrap their code in anonymous functions.
// Yes, an extra semi-colon has been added for safety :)
$output = preg_match($function_regex, $callback) ? $callback : 'function(){' . $callback . ';}';
return $output;
}
/**
* Helper function to sanitize Modernizr.load() assets
*/
function _modernizr_sanitize_resource($resource, $theme_path) {
global $base_url;
$output = '';
// If a path starts with 'sites' we assume they know exactly where they're
// going. Otherwise, they seem like relative URLs so append theme path.
$output = strpos($resource, 'sites/') !== FALSE ? $resource : $base_url . '/' . $theme_path . $resource;
return $output;
}
/**
* Helper function for hook_modernizr_info().
* Returns a Modernizr argument's type.
*/
function _modernizr_get_type($arg) {
$data = _modernizr_get_arg_info($arg, 'type');
// Since community-created detects are by far the most likely unknown entry,
// we assume that a value not found in modernizr.args.inc is a community detect.
// Note: 'tests' does NOT need t() because it is a machine value.
return $data ? $data : 'tests';
}
/**
* Helper function for hook_modernizr_info().
* Returns a Modernizr argument's description.
*/
function _modernizr_get_desc($arg) {
$data = _modernizr_get_arg_info($arg, 'desc');
// If we can't find a description, just admit it.
return $data ? $data : t('No description available.');
}
/**
* A helper function to get the information stored in modernizr.args.inc.
*
* @param string $arg
* The test machine name.
* @param string $type (default: 'desc')
* The data wanted, currently just 'desc' or 'type'.
* @return
* The data in the field, or FALSE if it doesn't exist.
*/
function _modernizr_get_arg_info($arg, $type = 'desc') {
static $loaded = FALSE;
if (!$loaded) {
$loaded = module_load_include('inc', 'modernizr', 'modernizr.args');
}
$data = _modernizr_args_return($arg);
// This data doesnt exist.
return $data && isset($data[$type]) ? $data[$type] : FALSE;
}
/**
* Helper function to pulls all tests from the current modernizr.js
*/
function _modernizr_current_build() {
$tests =& drupal_static(__FUNCTION__);
if (!isset($tests)) {
$path = modernizr_get_path();
$path_parts = explode('/', $path);
$file = $path ? file_get_contents($path) : NULL;
$filename = $path_parts[count($path_parts) - 1];
$tests = array();
// $matches holds two items:
// - [0] the full URL
// - [1] a string containing the args captured in the parens vvvv
$build_url = preg_match('/http:\\/\\/modernizr.com\\/download\\/#-(.*)/', $file, $matches);
// Turn URL args into test entries for Drupal module
if (isset($matches[1])) {
$args_and_prefix = explode(':', $matches[1]);
$build_args = explode('-', $args_and_prefix[0]);
foreach ($build_args as $arg) {
$tests[] = $arg;
}
}
else {
// Modernizr must not be downloaded, return null.
return null;
}
}
return $tests;
}
/**
* Asks other Drupal modules which Modernizr tests they need.
*
* @return array
*/
function modernizr_api_list() {
$tests =& drupal_static(__FUNCTION__);
if (!isset($tests)) {
// Grab all module implementations
// Note: this is a slightly augmented version of module_invoke_all(), so
// that we can know which module is providing which test.
$hook = 'modernizr_info';
foreach (module_implements($hook) as $module) {
$function = $module . '_' . $hook;
if (function_exists($function)) {
$result = call_user_func($function);
if (isset($result) && is_array($result)) {
$tests[$module] = $result;
}
}
}
// Grabbing the information with an hook_alter is not enough for themes
// because they will not all be active, nor their code added into the session.
$themes = list_themes();
$active_themes = array();
foreach ($themes as $theme_name => $theme) {
if ($theme->status == 1) {
$active_themes[$theme_name] = $theme;
if (isset($theme->base_themes)) {
foreach ($theme->base_themes as $base_theme_name => $base_theme) {
$active_themes[$base_theme_name] = $themes[$base_theme_name];
}
}
}
}
// We now go into every active theme and pull from the .info file the tests.
foreach ($active_themes as $active_theme) {
$data = drupal_parse_info_file($active_theme->filename);
if (isset($data['modernizr']) && isset($data['modernizr']['tests'])) {
// There are modernizr tests within this theme.
$theme_name = $data['name'];
$tests[$theme_name] = $data['modernizr']['tests'];
}
}
// The last thing we do is send it to have its data cleaned and organized.
$tests = _modernizr_api_list_clean($tests);
}
return $tests;
}
/**
* Cleans up the array of tests to be unified.
*
* @param $raw_tests array
* An array of tests provided by hook_modernizr_info.
* @return array
*/
function _modernizr_api_list_clean($raw_tests) {
$clean_tests = array();
foreach ($raw_tests as $module => $tests) {
foreach ($tests as $name => $data) {
// First, we check and correct if the tests have been added using indexed
// arrays, fixing the name variable.
if (is_int($name) && !is_array($data)) {
// The test is stored as a simple array, therefore the data is the name.
$name = $data;
$data = array();
}
elseif (is_int($name) && is_array($data)) {
// Still stored as a indexed array, but the data is an array.
$name = $data['name'];
}
// Now, we add these tests to our array of cleaned up data.
if (isset($clean_tests[$name])) {
// We already have the test, we are just going to add our module name.
$clean_tests[$name]['source'][] = $module;
}
else {
// The test has not been marked, we are adding it to the array.
$clean_tests[$name] = $data;
$clean_tests[$name]['source'] = array(
$module,
);
}
}
}
// Cleaning up the data to ensure all data we need is present.
foreach ($clean_tests as $name => $clean_test) {
$data = array(
'name' => $name,
'desc' => _modernizr_get_desc($name),
'docs' => '',
'camiuse' => '',
);
$clean_tests[$name] = array_merge($data, $clean_test);
}
return $clean_tests;
}
/**
* Implements hook_modernizr_info().
*
* This function implements our own hook to ensure that any custom Modernizr
* builds downloaded from either the settings screen or drush commands contain
* the essential components needed to support the module's functionality.
*
* cssclasses
* - Automatically adds cssclasses to download links since most users want this.
*
* html5shiv w/ printshiv
* - Includes some utility JS that allows IE to recognize HTML5 elements
*
* load
* - Includes yepnope.js as Modernizr.load() - allows conditional asynchronous
* CSS/JS loading.
*/
function modernizr_modernizr_info() {
$items = array();
$items[] = 'cssclasses';
$items[] = 'printshiv';
$items[] = 'load';
return $items;
}
/**
* Asks other Drupal modules for Modernizr.load() commands.
*
* @return array
*/
function modernizr_load_list($reset = FALSE) {
$load =& drupal_static(__FUNCTION__);
if (!isset($load) || $reset) {
$load = module_invoke_all('modernizr_load');
drupal_alter('modernizr_load', $load);
}
return $load;
}
Functions
Name | Description |
---|---|
modernizr_api_list | Asks other Drupal modules which Modernizr tests they need. |
modernizr_get_filename | Helper function to generate the current filename of the active Modernizr library |
modernizr_get_path | Returns the full path of modernizr, along with the filename. |
modernizr_get_version | Guesses the modernizr library version. |
modernizr_libraries_info | Implements hook_libraries_info(). |
modernizr_load_data | A function to generate the load data from the current themes. |
modernizr_load_list | Asks other Drupal modules for Modernizr.load() commands. |
modernizr_menu | Implements hook_menu(). |
modernizr_modernizr_info | Implements hook_modernizr_info(). |
modernizr_page_build | Implements hook_page_build(). |
modernizr_permission | Implements hook_permission(). |
modernizr_preprocess_html | Implements MODULE_preprocess_html(). |
modernizr_preprocess_maintenance_page | Implements MODULE_preprocess_maintenance_page(). |
_modernizr_api_list_clean | Cleans up the array of tests to be unified. |
_modernizr_current_build | Helper function to pulls all tests from the current modernizr.js |
_modernizr_get_arg_info | A helper function to get the information stored in modernizr.args.inc. |
_modernizr_get_desc | Helper function for hook_modernizr_info(). Returns a Modernizr argument's description. |
_modernizr_get_type | Helper function for hook_modernizr_info(). Returns a Modernizr argument's type. |
_modernizr_load_generate | Helper function to render the Modernizr.load() calls. |
_modernizr_sanitize_callback | Helper function to sanitize Modernizr.load() callbacks |
_modernizr_sanitize_resource | Helper function to sanitize Modernizr.load() assets |
_modernizr_scan_for_library | Helper function to scan for acceptably named libraries |
Constants
Globals
Name | Description |
---|---|
$_modernizr_js_settings |