EVOLUTION-NINJA
Edit File: navbar.module
<?php /** * @file * Administration navbar for quick access to top level administration items. */ /** * Implements hook_hook_info(). */ function navbar_hook_info() { $hooks = array( 'navbar', 'navbar_alter', 'navbar_breakpoints_alter', ); return array_fill_keys($hooks, array('group' => 'navbar')); } /** * Implements hook_help(). */ function navbar_help($path, $arg) { switch ($path) { case 'admin/help#navbar': $output = '<h3>' . t('About') . '</h3>'; $output .= '<p>' . t('The Navbar module displays links to top-level administration menu items and links from other modules at the top of the screen. For more information, see the online handbook entry for <a href="@navbar">Navbar module</a>.', array('@navbar' => 'https://drupal.org/node/1993254')) . '</p>'; $output .= '<h3>' . t('Uses') . '</h3>'; $output .= '<dl>'; $output .= '<dt>' . t('Displaying administrative links') . '</dt>'; $output .= '<dd>' . t('The Navbar module displays a bar containing top-level administrative components across the top of the screen. Below that, the Navbar module has a <em>drawer</em> section where it displays links provided by other modules, such as the core <a href="@shortcuts-help">Shortcut module</a>. The drawer can be hidden/shown by clicking on its corresponding tab.', array('@shortcuts-help' => url('admin/help/shortcut'))) . '</dd>'; $output .= '</dl>'; return $output; } } /** * Implements hook_permission(). */ function navbar_permission() { return array( 'access navbar' => array( 'title' => t('Use the administration navbar'), ), ); } /** * Implements hook_theme(). */ function navbar_theme($existing, $type, $theme, $path) { $items['navbar'] = array( 'render element' => 'element', ); $items['navbar_item'] = array( 'render element' => 'element', ); $items['navbar_tab_wrapper'] = array( 'render element' => 'element', ); $items['navbar_tray_wrapper'] = array( 'render element' => 'element', ); $items['navbar_tray_heading_wrapper'] = array( 'render element' => 'element', ); // Core menu theming overrides. $items['menu_tree__management'] = array( 'render element' => 'tree', 'function' => 'theme_navbar_menu_tree', 'preprocess functions' => array('template_preprocess_navbar_menu_tree'), ); if (module_exists('shortcut')) { // Shortcut module assigns an incrementing set_name to every shortcut set and // menu_tree_output() does not add any useful wildcard suggestion. $shortcut_sets = db_query('SELECT set_name FROM {shortcut_set}'); foreach ($shortcut_sets as $shortcut_set) { // Override theming for every single shortcut set. $items['menu_tree__' . strtr($shortcut_set->set_name, '-', '_')] = array( 'render element' => 'tree', 'function' => 'theme_navbar_menu_tree', 'preprocess functions' => array('template_preprocess_navbar_menu_tree'), ); } } return $items; } /** * Implements hook_menu(). */ function navbar_menu() { $items['navbar/subtrees/%'] = array( 'page callback' => 'navbar_subtrees_jsonp', 'page arguments' => array(2), 'access callback' => '_navbar_subtrees_access', 'access arguments' => array(2), 'type' => MENU_CALLBACK, ); return $items; } /** * Implements hook_page_alter(). */ function navbar_page_alter(&$page) { $current_path = current_path(); $current_path_is_admin = FALSE; // The function path_is_admin() is not available on update.php pages. if (!defined('MAINTENANCE_MODE')) { $current_path_is_admin = path_is_admin($current_path); } drupal_add_js(array('currentPath' => $current_path, 'currentPathIsAdmin' => $current_path_is_admin), 'setting'); } /** * Implements hook_element_info(). */ function navbar_element_info() { $elements = array(); $elements['navbar'] = array( '#pre_render' => array('navbar_pre_render'), '#theme' => 'navbar', '#attached' => array( 'library' => array( array('navbar', 'navbar'), ), ), // Metadata for the navbar wrapping element. '#attributes' => array( // The id cannot be simply "navbar" or it will clash with the simpletest // tests listing which produces a checkbox with attribute id="navbar" 'id' => 'navbar-administration', // The 'overlay-displace-top' class is necessary in overlay-parent so that // the drupalOverlayResize and drupalOverlayClose events will be bound // to the document. The navbar does not use this class. It is present // to enable compatibility with the Overlay module. 'class' => array('drupal-navbar', 'overlay-displace-top'), 'role' => 'navigation', ), // Metadata for the administration bar. '#bar' => array( '#heading' => t('Navbar items'), '#attributes' => array( 'id' => 'navbar-bar', 'class' => array('navbar-bar', 'clearfix'), ), ), ); // A navbar item is wrapped in markup for common styling. The 'tray' // property contains a renderable array. theme_navbar_tab() is a light // wrapper around the l() function. The contents of tray are rendered in // theme_navbar_tab(). $elements['navbar_item'] = array( '#pre_render' => array('navbar_pre_render_item'), '#theme' => 'navbar_item', '#theme_wrappers' => array('navbar_tab_wrapper'), 'tab' => array( '#type' => 'link', '#title' => NULL, '#href' => '', ), ); return $elements; } /** * Access callback: Returns if the user has access to the rendered subtree requested by the hash. * * @see navbar_menu(). */ function _navbar_subtrees_access($hash) { return user_access('access navbar') && ($hash == _navbar_get_subtree_hash()); } /** * Page callback: Returns the rendered subtree of each top-level navbar link. * * @see navbar_menu(). */ function navbar_subtrees_jsonp($hash) { _navbar_initialize_page_cache(); $subtrees = navbar_get_rendered_subtrees(); $response = new JsonResponse($subtrees); $response->setCallback('Drupal.navbar.setSubtrees.resolve'); return $response; } /** * Use Drupal's page cache for navbar/subtrees/*, even for authenticated users. * * This gets invoked after full bootstrap, so must duplicate some of what's * done by _drupal_bootstrap_page_cache(). * * @todo Replace this hack with something better integrated with DrupalKernel * once Drupal's page caching itself is properly integrated. */ function _navbar_initialize_page_cache() { $GLOBALS['conf']['system.performance']['cache']['page']['enabled'] = TRUE; drupal_page_is_cacheable(TRUE); // If we have a cache, serve it. // @see _drupal_bootstrap_page_cache() $cache = drupal_page_get_cache(); if (is_object($cache)) { header('X-Drupal-Cache: HIT'); // Restore the metadata cached with the page. $_GET['q'] = $cache->data['path']; date_default_timezone_set(drupal_get_user_timezone()); drupal_serve_page_from_cache($cache); // We are done. exit; } // Otherwise, create a new page response (that will be cached). header('X-Drupal-Cache: MISS'); // The Expires HTTP header is the heart of the client-side HTTP caching. The // additional server-side page cache only takes effect when the client // accesses the callback URL again (e.g., after clearing the browser cache or // when force-reloading a Drupal page). $max_age = 3600 * 24 * 365; drupal_add_http_header('Expires', gmdate(DATE_RFC1123, REQUEST_TIME + $max_age)); drupal_add_http_header('Cache-Control', 'private, max-age=' . $max_age); } /** * Implements hook_page_build(). * * Add admin navbar to the page_top region automatically. */ function navbar_page_build(&$page) { if (navbar_suppress(FALSE)) { return; } $page['page_top']['navbar'] = array( '#type' => 'navbar', '#access' => user_access('access navbar'), ); } /** * Builds the Navbar as a structured array ready for drupal_render(). * * Since building the navbar takes some time, it is done just prior to * rendering to ensure that it is built only if it will be displayed. * * @param array $element * A renderable array. * * @return * A renderable array. * * @see navbar_page_build(). */ function navbar_pre_render($element) { // Define the breakpoints to switch from vertical to horizontal // navbar presentation. $breakpoints = array( 'narrow' => 'only screen and (min-width: 16.5em)', 'standard' => 'only screen and (min-width: 38.125em)', 'wide' => 'only screen and (min-width: 50em)', ); // Allow for altering of the breakpoints. drupal_alter('navbar_breakpoints', $breakpoints); if (!empty($breakpoints)) { $element['#attached']['js'][] = array( 'data' => array( 'navbar' => array( 'breakpoints' => $breakpoints, ), ), 'type' => 'setting', ); } // Get navbar items from all modules that implement hook_navbar(). $items = module_invoke_all('navbar'); // Allow for altering of hook_navbar(). drupal_alter('navbar', $items); // Sort the children. uasort($items, 'element_sort'); // Merge in the original navbar values. $element = array_merge($element, $items); // Render the children. $element['#children'] = drupal_render_children($element); return $element; } /** * Returns HTML that wraps the administration navbar. * * @param array $variables * An associative array containing: * - element: An associative array containing the properties and children of * the tray. Properties used: #children, #attributes and #bar. */ function theme_navbar(&$variables) { if (!empty($variables['element']['#children'])) { $element = $variables['element']; $trays = ''; foreach (element_children($element) as $key) { $trays .= drupal_render($element[$key]['tray']); } return '<nav' . drupal_attributes($element['#attributes']) . '>' . '<div' . drupal_attributes($element['#bar']['#attributes']) . '>' . '<h2 class="element-invisible">' . $element['#bar']['#heading'] . '</h2>' . $element['#children'] . '</div>' . $trays . '</nav>'; } } /** * Provides markup for associating a tray trigger with a tray element. * * A tray is a responsive container that wraps renderable content. Trays present * content well on small and large screens alike. * * @param array $element * A renderable array. * * @return * A renderable array. */ function navbar_pre_render_item($element) { // Assign each item a unique ID. $id = drupal_html_id('navbar-item'); // Provide attributes for a navbar item. $attributes = array( 'id' => $id, ); // If tray content is present, markup the tray and its associated trigger. if (!empty($element['tray'])) { // Provide attributes necessary for trays. $attributes += array( 'data-navbar-tab-trigger' => '', 'data-navbar-tray' => $id . '-tray', 'aria-owns' => $id, 'role' => 'button', 'aria-pressed' => 'false', ); // Merge in module-provided attributes. $element['tab'] += array('#attributes' => array()); $element['tab']['#attributes'] += $attributes; // Provide attributes for the tray theme wrapper. $attributes = array( 'id' => $id . '-tray', 'data-navbar-tray' => $id . '-tray', 'aria-owned-by' => $id, ); // Merge in module-provided attributes. if (!isset($element['tray']['#wrapper_attributes'])) { $element['tray']['#wrapper_attributes'] = array(); } $element['tray']['#wrapper_attributes'] += $attributes; $element['tray']['#wrapper_attributes']['class'][] = 'navbar-tray'; if (!isset($element['tray']['#theme_wrappers'])) { $element['tray']['#theme_wrappers'] = array(); } // Add the standard theme_wrapper for trays. array_unshift($element['tray']['#theme_wrappers'], 'navbar_tray_wrapper'); // If a #heading is provided for the tray, provided a #theme_wrapper // function to append it. array_unshift($element['tray']['#theme_wrappers'], 'navbar_tray_heading_wrapper'); } return $element; } /** * Implements template_preprocess_HOOK() for theme_navbar_menu_tree(). */ function template_preprocess_navbar_menu_tree(&$variables) { $variables['tree'] = $variables['tree']['#children']; } /** * Implements template_preprocess_HOOK(). */ function template_preprocess_navbar_tab_wrapper(&$variables) { if (!isset($variables['element']['#wrapper_attributes'])) { $variables['element']['#wrapper_attributes'] = array(); } $variables['element']['#wrapper_attributes']['class'][] = 'navbar-tab'; } /** * Returns HTML for a navbar item. * * This theme function only renders the tab portion of the navbar item. The * tray portion will be rendered later. * * @param array $variables * An associative array containing: * - element: An associative array containing the properties and children of * the tray. Property used: tab. * * @see navbar_pre_render_item(). * @see theme_navbar(). */ function theme_navbar_item(&$variables) { return drupal_render($variables['element']['tab']); } /** * Returns HTML for a wrapper for a navbar menu sub-tree. * * @param $variables * An associative array containing: * - tree: An HTML string containing the tree's items. * * @see template_preprocess_navbar_menu_tree() * @ingroup themeable */ function theme_navbar_menu_tree($variables) { return '<ul class="navbar-menu">' . $variables['tree'] . '</ul>'; } /** * Returns HTML for wrapping a navbar tab. * * Navbar tabs have a common styling and placement with the navbar's bar area. * * @param array $variables * An associative array containing: * - element: An associative array containing the properties and children of * the tray. Properties used: #children and #attributes. */ function theme_navbar_tab_wrapper(&$variables) { if (!empty($variables['element']['#children'])) { $element = $variables['element']; return '<div' . drupal_attributes($element['#wrapper_attributes']) . '>' . $element['#children'] . '</div>'; } } /** * Returns HTML for wrapping a navbar tray. * * Used in combination with theme_navbar_tab() to create an * association between a link tag in the administration bar and a tray. * * @param array $variables * An associative array containing: * - element: An associative array containing the properties and children of * the tray. Properties used: #children, #navbar_identifier and * #attributes. */ function theme_navbar_tray_wrapper(&$variables) { if (!empty($variables['element']['#children'])) { $element = $variables['element']; return '<div' . drupal_attributes($element['#wrapper_attributes']) . '><div class="navbar-lining clearfix">' . $element['#children'] . '</div></div>'; } } /** * Returns HTML for prepending a heading to a navbar tray. * * @param array $variables * An associative array containing: * - element: An associative array containing the properties and children of * the tray. Properties used: #children and #heading. */ function theme_navbar_tray_heading_wrapper(&$variables) { $element = $variables['element']; if (!empty($element['#children'])) { $heading = ''; if (!empty($element['#heading'])) { $heading = '<h3 class="navbar-tray-name element-invisible">' . $element['#heading'] . '</h3>'; } return $heading . $element['#children']; } } /** * Implements hook_system_info_alter(). * * Indicate that the 'page_top' region (in which the navbar will be displayed) * is an overlay supplemental region that should be refreshed whenever its * content is updated. * * This information is provided for any module that might need to use it, not * just the core Overlay module. */ function navbar_system_info_alter(&$info, $file, $type) { if ($type == 'theme') { $info['overlay_supplemental_regions'][] = 'page_top'; } } /** * Gets only the top level items below the 'admin' path. * * @return * An array containing a menu tree of top level items below the 'admin' path. */ function navbar_get_menu_tree() { $tree = array(); $admin_mlid = db_query('SELECT mlid FROM {menu_links} WHERE menu_name = :menu_name AND module = :module AND link_path = :path', array(':menu_name' => 'management', ':module' => 'system', ':path' => 'admin'))->fetchField(); if ($admin_mlid) { // Only return top 3 menu levels. $tree = menu_tree_all_data('management', NULL, 5); } // Return the sub-menus of the management menu root. foreach ($tree as $key => $menu) { if ($menu['link']['mlid'] == $admin_mlid) { return (!empty($menu['below'])) ? $menu['below'] : array(); } else { // Any extra menu link can break the navbar. Skip them. continue; } } return array(); } /** * Generates an array of links from a menu tree array. * * Based on menu_navigation_links(). Adds path based IDs and icon placeholders * to the links. * * @return * An array of links as defined above. */ function navbar_menu_navigation_links(&$tree) { foreach ($tree as $key => $item) { // Configure sub-items. if (!empty($item['below'])) { navbar_menu_navigation_links($tree[$key]['below']); } // Make sure we have a path specific ID in place, so we can attach icons // and behaviors to the items. $tree[$key]['link']['localized_options']['attributes'] = array( 'id' => 'navbar-link-' . str_replace(array('/', '<', '>'), array('-', '', ''), $item['link']['link_path']), 'class' => array( 'navbar-icon', 'navbar-icon-' . strtolower(str_replace(' ', '-', $item['link']['link_title'])), ), 'title' => check_plain($item['link']['description']), ); } } /** * Returns the rendered subtree of each top-level navbar link. */ function navbar_get_rendered_subtrees() { $subtrees = array(); $tree = navbar_get_menu_tree(); foreach ($tree as $tree_item) { $item = $tree_item['link']; if (!$item['hidden'] && $item['access']) { if ($item['has_children']) { $query = db_select('menu_links'); $query->addField('menu_links', 'mlid'); $query->condition('has_children', 1); for ($i = 1; $i <= $item['depth']; $i++) { $query->condition('p' . $i, $item['p' . $i]); } $parents = $query->execute()->fetchCol(); $subtree = menu_build_tree($item['menu_name'], array('expanded' => $parents, 'min_depth' => $item['depth'] + 1)); navbar_menu_navigation_links($subtree); $subtree = menu_tree_output($subtree); $subtree = drupal_render($subtree); } else { $subtree = ''; } $id = str_replace(array('/', '<', '>'), array('-', '', ''), $item['href']); $subtrees[$id] = $subtree; } } return $subtrees; } /** * Checks whether an item is in the active trail. * * Useful when using a menu generated by menu_tree_all_data() which does * not set the 'in_active_trail' flag on items. * * @return * TRUE when path is in the active trail, FALSE if not. * * @todo * Look at migrating to a menu system level function. */ function navbar_in_active_trail($path) { $active_paths = &drupal_static(__FUNCTION__); // Gather active paths. if (!isset($active_paths)) { $active_paths = array(); $trail = menu_get_active_trail(); foreach ($trail as $item) { if (!empty($item['href'])) { $active_paths[] = $item['href']; } } } return in_array($path, $active_paths); } /** * Implements hook_library(). */ function navbar_library() { $path = drupal_get_path('module', 'navbar'); $libraries['navbar'] = array( 'title' => 'Navbar', 'version' => VERSION, 'js' => array( $path . '/js/navbar.js' => array(), ), 'css' => array( $path . '/css/navbar.module.css', $path . '/css/navbar.theme.css', $path . '/css/navbar.icons.css', ), 'dependencies' => array( array('navbar', 'modernizr'), array('system', 'jquery'), array('navbar', 'underscore'), array('navbar', 'backbone'), array('navbar', 'navbar.matchmedia'), array('system', 'jquery.once'), array('navbar', 'navbar.debounce'), array('navbar', 'navbar.announce'), array('navbar', 'navbar.displace'), array('navbar', 'navbar.menu'), array('navbar', 'navbar.tableheader'), array('navbar', 'navbar.escape_admin'), ), ); // Only load navbar.overlay if overlay is enabled. if (module_exists('overlay')) { $libraries['navbar']['dependencies'][] = array('navbar', 'navbar.overlay'); } $libraries['navbar.escape_admin'] = array( 'title' => 'Provides a button to escape the administration area.', 'version' => VERSION, 'js' => array( $path . '/js/escape-admin.js' => array(), ), 'dependencies' => array( array('system', 'jquery'), array('system', 'jquery.once'), ), ); $libraries['navbar.menu'] = array( 'title' => 'Navbar nested accordion menus.', 'version' => VERSION, 'js' => array( $path . '/js/navbar.menu.js' => array(), ), 'css' => array( $path . '/css/navbar.menu.css', ), 'dependencies' => array( array('system', 'jquery'), array('system', 'jquery.once'), ), ); // Backport of D8 matchMedia polyfill. $libraries['navbar.matchmedia'] = array( 'title' => 'window.matchMedia polyfill', 'website' => 'http://drupal.org/node/1815602', 'version' => VERSION, 'js' => array( $path . '/js/matchmedia.js' => array(), ), ); // A utility function to limit calls to a function with a given time. $libraries['navbar.debounce'] = array( 'title' => 'Navbar debounce', 'version' => VERSION, 'js' => array( $path . '/js/debounce.js' => array('group' => JS_LIBRARY), ), ); // A utility function determine viewport offset distances. $libraries['navbar.displace'] = array( 'title' => 'Navbar displace', 'version' => VERSION, 'js' => array( $path . '/js/displace.js' => array('group' => JS_LIBRARY), ), 'dependencies' => array( array('system', 'jquery'), array('navbar', 'navbar.debounce'), ), ); // A utility for writing text to a common aria-live region. $libraries['navbar.announce'] = array( 'title' => 'Navbar announce', 'version' => VERSION, 'js' => array( $path . '/js/announce.js' => array('group' => JS_LIBRARY), ), 'dependencies' => array( array('navbar', 'navbar.debounce'), ), ); // Override Overlay methods to support displacement. $libraries['navbar.overlay'] = array( 'title' => 'Overlay method overrides to support D8 viewport displacement.', 'version' => VERSION, 'css' => array( $path . '/css/navbar-overlay.css', ), 'js' => array( // Load this file well after Overlay code has loaded. $path . '/js/navbar-overlay.js' => array('weight' => 100), ), 'dependencies' => array( array('system', 'jquery'), array('navbar', 'navbar.displace'), ), ); // Support Tableheader displacement. $libraries['navbar.tableheader'] = array( 'title' => 'Tableheader method to support D8 viewport displacement.', 'version' => VERSION, 'js' => array( // Load this file well after Overlay code has loaded. $path . '/js/navbar-tableheader.js' => array('weight' => 100), ), 'dependencies' => array( array('system', 'jquery'), array('navbar', 'navbar.displace'), ), ); // Ensure that each 3rd party library dependency has a default variant. // Convert Libraries module data structures to library data structures. // Modernizr $libraries['modernizr'] = _navbar_convert_libraries_to_library(libraries_detect('modernizr'), array( 'group' => JS_LIBRARY, 'weight' => -100, )); // Underscore $libraries['underscore'] = _navbar_convert_libraries_to_library(libraries_detect('underscore'), array( 'group' => JS_LIBRARY, 'weight' => -20, )); // Backbone $libraries['backbone'] = _navbar_convert_libraries_to_library(libraries_detect('backbone'), array( 'group' => JS_LIBRARY, 'weight' => -19, )); return $libraries; } /** * Implements hook_library_alter(). */ function navbar_library_alter(&$libraries, $module) { $jquery_version = &drupal_static(__FUNCTION__, NULL); if ($module == 'system') { $jquery_version = $libraries['jquery']['version']; } if ($jquery_version && $module == 'navbar') { $path = drupal_get_path('module', 'navbar'); // If the version of jQuery is old, we need to add `on` and `off`. if ($jquery_version < '1.7') { $libraries['navbar']['js'][$path . '/js/jquery/ducktape.events.js'] = array('group' => JS_LIBRARY); } } if ($module === 'overlay' && !empty($libraries)) { // Unset the child CSS file from Overlay and add our own. if (!empty($libraries['child']['css'])) { unset($libraries['child']['css']['modules/overlay/overlay-child.css']); } $libraries['child']['css'][drupal_get_path('module', 'navbar') . '/css/navbar-overlay-child.css'] = array(); } } /** * Implements hook_js_alter(). */ function navbar_js_alter(&$javascript) { // Only load the tableheader offset script if the core tableheader script // and the navbar js itself is loaded. if (isset($javascript['misc/tableheader.js']) && isset($javascript[drupal_get_path('module', 'navbar') . '/js/navbar.js'])) { drupal_add_js(array('tableHeaderOffset' => 'Drupal.navbar.height'), array('type' => 'setting')); } } /** * Implements hook_libraries_info(). * * @see Libraries module. */ function navbar_libraries_info() { $libraries = array(); $common = array( 'version callback' => '_navbar_libraries_get_version', 'variant order' => array('minified', 'source'), ); $libraries['modernizr'] = array( 'name' => 'Modernizr', 'vendor url' => 'https://github.com/Modernizr/Modernizr', 'download url' => 'http://modernizr.com/download/#-inputtypes-svg-touch-cssclasses-addtest-teststyles-prefixes-elem_details', 'version arguments' => array( 'variants' => array( 'source' => array( 'file' => 'modernizr.js', // @todo Document an actual example version string. 'pattern' => '#Modernizr\s+[Vv]?([0-9\.]+)#', ), 'minified' => array( 'file' => 'modernizr-min.js', 'pattern' => '#Modernizr\s+[Vv]?([0-9\.]+)#', ), ), ), 'versions' => array( // Means ">=2.6.2": matches 2.6.2, 2.7.1, etc. '2.6.2' => array( 'variants' => array( 'source' => array( 'files' => array( 'js' => array( 'modernizr.js', ), ), // Without a variant callback, the variant is assumed to be // installed. 'variant callback' => '_navbar_libraries_variant_exists', 'variant arguments' => array('modernizr.js') ), 'minified' => array( 'files' => array( 'js' => array( 'modernizr-min.js', ), ), // Without a variant callback, the variant is assumed to be // installed. 'variant callback' => '_navbar_libraries_variant_exists', 'variant arguments' => array('modernizr-min.js') ), ), ), ), ); $libraries['modernizr'] += $common; $libraries['underscore'] = array( 'name' => 'Underscore', 'vendor url' => 'http://documentcloud.github.io/backbone/', 'download url' => 'https://github.com/jashkenas/underscore/archive/1.5.2.zip', 'version arguments' => array( 'variants' => array( 'source' => array( 'file' => 'underscore.js', // @todo Document an actual example version string. 'pattern' => '#VERSION *\W *[\'\"]{1}(.*?)[\'\"]{1}#', // In the unminified Underscore.js 1.5.2, the version is defined on // line 68. 'lines' => 100, ), 'minified' => array( 'file' => 'underscore-min.js', 'pattern' => '#VERSION *\W *[\'\"]{1}(.*?)[\'\"]{1}#', ), ), ), 'versions' => array( // Means ">=1.5.0": matches 1.5.0, 1.5.2, etc. '1.5.0' => array( 'variants' => array( 'source' => array( 'files' => array( 'js' => array( 'underscore.js', ), ), // Without a variant callback, the variant is assumed to be // installed. 'variant callback' => '_navbar_libraries_variant_exists', 'variant arguments' => array('underscore.js'), ), 'minified' => array( 'files' => array( 'js' => array( 'underscore-min.js', ), ), // Without a variant callback, the variant is assumed to be // installed. 'variant callback' => '_navbar_libraries_variant_exists', 'variant arguments' => array('underscore-min.js'), ), ), ), ), ); $libraries['underscore'] += $common; $libraries['backbone'] = array( 'name' => 'Backbone', 'vendor url' => 'http://documentcloud.github.io/backbone/', 'download url' => 'https://github.com/jashkenas/backbone/archive/1.1.0.zip', 'version arguments' => array( 'variants' => array( 'source' => array( 'file' => 'backbone.js', // @todo Document an actual example version string. 'pattern' => '#VERSION *\W *[\'\"]{1}(.*?)[\'\"]{1}#', // In the unminified Backbone.js 1.1.0, the version is defined on line // 38. 'lines' => 50, ), 'minified' => array( 'file' => 'backbone-min.js', 'pattern' => '#VERSION *\W *[\'\"]{1}(.*?)[\'\"]{1}#', ), ), ), 'versions' => array( // Means ">=1.0.0": matches 1.0.0, 1.1.0, etc. '1.0.0' => array( 'variants' => array( 'source' => array( 'name' => 'Backbone', 'files' => array( 'js' => array( 'backbone.js', ), ), // Without a variant callback, the variant is assumed to be // installed. 'variant callback' => '_navbar_libraries_variant_exists', 'variant arguments' => array('backbone.js'), 'dependencies' => array('underscore (>=1.5.0)'), ), 'minified' => array( 'name' => 'Backbone', 'files' => array( 'js' => array( 'backbone-min.js', ), ), // Without a variant callback, the variant is assumed to be // installed. 'variant callback' => '_navbar_libraries_variant_exists', 'variant arguments' => array('backbone-min.js'), 'dependencies' => array('underscore (>=1.5.0)'), ), ), ), ), ); $libraries['backbone'] += $common; return $libraries; } /** * Determines the version of a navbar library. * * This is used in case different variants of the library are shipped separately * and, thus, different variants can contain different versions. * * @param array $library * An associative array containing all information about the library. The * library is assumed to have the following non-standard keys: * - variant order: An array of variant names, ordered from the most preferred * variant to the least preferred. * @param array $options * An associative array with the following keys: * - variants: An array of options for libraries_get_version() keyed by * variant name. * */ function _navbar_libraries_get_version(&$library, $options = array()) { $versions = array(); foreach ($library['variant order'] as $variant_name) { $variant = $library['version arguments']['variants'][$variant_name]; // Use the libraries get version function to determine the version string. $versions[$variant_name] = libraries_get_version($library, $variant); } // If no versions could be found for any of the variant, there is no version // to return. If different versions have been found, there is no way to // determine the correct one. We cannot use the information on the preferred // variants because we cannot guarantee that a less preferred variant will not // be loaded. Null values are fine. Either that variant file doesn't exist // or id doesn't contain version information. As long as the there is no // conflicting version information, the check should pass. $versions = array_filter($versions, '_navbar_libraries_filter_null_values'); $version = array_unique($versions); $vcount = count($version); if ($vcount == 1) { // A version number exists, so suppress any errors that any individual // variant might have raised. unset($library['error']); unset($library['error message']); return array_shift($version); } elseif ($vcount > 1) { $output = array(); foreach ($versions as $name => $v) { $output[] = t('@name (@v)', array( '@name' => $name, '@v' => $v, )); } $library['error'] = 'inconsistent versions'; $library['error message'] = t('The library\'s variants returned inconsistent versions: @variant_info', array( '@variant_info' => implode(', ', $output), )); } // If the version count is zero, then let the error from libraries_get_version // propagate through. } /** * Determines if an item is empty or not. * * @param string $item * A version number string. * @return boolean * Whether the $item's value is empty or not. */ function _navbar_libraries_filter_null_values($item) { return !empty($item); } /** * Libraries API variant callback. */ function _navbar_libraries_variant_exists($library, $variant_name, $required_file) { return file_exists($library['library path'] . '/' . $required_file);; } /** * Implements hook_cache_flush(). */ function navbar_cache_flush() { return array('navbar'); } /** * Returns the hash of the per-user rendered navbar subtrees. */ function _navbar_get_subtree_hash() { global $user; $cid = $user->uid . ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode; if ($cache = cache('navbar')->get($cid)) { $hash = $cache->data; } else { $subtrees = navbar_get_rendered_subtrees(); $hash = drupal_hash_base64(serialize($subtrees)); cache('navbar')->set($cid, $hash); } return $hash; } /** * Converts a libraries module array to a hook_library array. * * @todo Libraries API should automatically register all libraries in * hook_library(). See https://drupal.org/node/1386368 * * @return Array * Returns a standard Drupal library definition structure. */ function _navbar_convert_libraries_to_library($library, $options = array()) { // If the library wasn't installed, don't bother converting it. if (!$library['installed']) { return array(); } $converted = array(); $files = array(); // Get the library files from one of the installed variants. if ($name = _navbar_libraries_get_preferred_variant_name($library)) { $files = $library['variants'][$name]['files']; } // Define the library if files exist for it. if (!empty($files)) { // This is the basic structure expected by hook_library(). $converted = array( 'title' => $library['name'], 'website' => $library['vendor url'], 'version' => $library['version'], ); foreach ($files as $type => $paths) { foreach($paths as $filename => $data) { $converted[$type][$library['library path'] . '/' . $filename] = $options; } } } return $converted; } /** * Returns the variant that should be loaded based on order preference. * * @param array $library * A libraries module library definition array. * @return string * The name of the variant that should be loaded. */ function _navbar_libraries_get_preferred_variant_name($library) { if (!empty($library['variant order'])) { foreach ($library['variant order'] as $name) { if ($variant = $library['variants'][$name]) { if ($variant['installed']) { return $name; } } } } return NULL; } /** * Implementation of hook_suppress() * * Allows other modules to suppress display of Navbar * * This function should be called from within another module's page callback * (preferably using module_invoke()) when the navbar should not be displayed. * This is useful for modules that implement popup pages or other special * pages where the navbar would be distracting or break the layout. * * @param $set * Defaults to TRUE. If called before hook_footer(), the navbar will not be * displayed. If FALSE is passed, the suppression state is returned. **/ function navbar_suppress($set = TRUE) { static $suppress = FALSE; if (!empty($set) && $suppress === FALSE) { $suppress = TRUE; } return $suppress; } /** * Implements hook_modernizr_info(). */ function navbar_modernizr_info() { $tests = array(); // Feature tests $tests[] = 'inputtypes'; $tests[] = 'svg'; $tests[] = 'touch'; $tests[] = 'elem_details'; // Extensibility $tests[] = 'addtest'; $tests[] = 'teststyles'; $tests[] = 'prefixes'; return $tests; }