alterFrameworkLibrary($framework); $assets = array_map(function ($data) { $assets = array(); foreach ($data as $info) { if (isset($info['data'])) { $assets[] = $info['data']; } } return $assets; }, array_intersect_key($framework, array_flip($types))); } // Otherwise, use the legacy CDN provider code. else { if (!empty($provider) && ($data = bootstrap_cdn_provider($provider))) { // Alter the assets based on provider. $function = 'bootstrap_bootstrap_cdn_provider_' . $provider . '_assets_alter'; if (function_exists($function)) { $function($data, $theme); } // Iterate over each type. foreach ($types as $type) { if (variable_get("preprocess_$type", FALSE) && !empty($data['min'][$type])) { $assets[$type] = $data['min'][$type]; } elseif (!empty($data[$type])) { $assets[$type] = $data[$type]; } } } } return is_string($original_type) ? $assets[$original_type] : $assets; } /** * Return information from the .info file of a theme (and possible base themes). * * @param string $theme_key * The machine name of the theme. * @param string $key * The key name of the item to return from the .info file. This value can * include "][" to automatically attempt to traverse any arrays. * @param bool $base_themes * Recursively search base themes, defaults to TRUE. * * @return string|array|false * A string or array depending on the type of value and if a base theme also * contains the same $key, FALSE if no $key is found. */ function bootstrap_get_theme_info($theme_key = NULL, $key = NULL, $base_themes = TRUE) { // If no $theme_key is given, use the current theme if we can determine it. if (!isset($theme_key)) { $theme_key = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : FALSE; } if ($theme_key) { $themes = list_themes(); if (!empty($themes[$theme_key])) { $theme = $themes[$theme_key]; // If a key name was specified, return just that array. if ($key) { $value = FALSE; // Recursively add base theme values. if ($base_themes && isset($theme->base_themes)) { foreach (array_keys($theme->base_themes) as $base_theme) { $value = bootstrap_get_theme_info($base_theme, $key); } } if (!empty($themes[$theme_key])) { $info = $themes[$theme_key]->info; // Allow array traversal. $keys = explode('][', $key); foreach ($keys as $parent) { if (isset($info[$parent])) { $info = $info[$parent]; } else { $info = FALSE; } } if (is_array($value)) { if (!empty($info)) { if (!is_array($info)) { $info = array($info); } $value = drupal_array_merge_deep($value, $info); } } else { if (!empty($info)) { if (empty($value)) { $value = $info; } else { if (!is_array($value)) { $value = array($value); } if (!is_array($info)) { $info = array($info); } $value = drupal_array_merge_deep($value, $info); } } } } return $value; } // If no info $key was specified, just return the entire info array. return $theme->info; } } return FALSE; } /** * Includes a theme file. * * @param string $theme * Name of the theme to use for base path. * @param string $path * Path relative to $theme. * * @return string|false * The absolute path to the include file or FALSE if it doesn't exist. */ function bootstrap_include($theme, $path) { static $included = array(); static $themes = array(); if (!isset($themes[$theme])) { $themes[$theme] = drupal_get_path('theme', $theme); } if (!isset($included["$theme:$path"])) { $file = DRUPAL_ROOT . '/' . $themes[$theme] . '/' . $path; if (file_exists($file)) { include_once $file; } else { $file = FALSE; } $included["$theme:$path"] = $file; } return $included["$theme:$path"]; } /** * Retrieves a setting for the current theme or for a given theme. * * This is a wrapper for theme_get_setting(), ensuring to use deprecated * setting values instead. * * @param string $name * The name of the setting to be retrieved. * @param string $theme * The name of a given theme; defaults to the currently active theme. * @param string $prefix * The prefix used on the $name of the setting, this will be appended with * "_" automatically if set. * @param mixed $default * The default value to return if setting doesn't exist or is not set. * * @return mixed * The value of the requested setting, NULL if the setting does not exist. * * @see theme_get_setting() * * @todo Refactor in 7.x-4.x and get rid of the deprecated settings. */ function bootstrap_setting($name, $theme = NULL, $prefix = 'bootstrap', $default = NULL) { $prefix = !empty($prefix) ? $prefix . '_' : ''; $setting = theme_get_setting($prefix . $name, $theme); switch ($prefix . $name) { case 'bootstrap_cdn_provider': $deprecated = theme_get_setting('bootstrap_cdn', $theme); if (isset($deprecated)) { $setting = empty($deprecated) ? '' : 'jsdelivr'; } break; case 'bootstrap_cdn_jsdelivr_version': $deprecated = theme_get_setting('bootstrap_cdn', $theme); if (isset($deprecated)) { $setting = empty($deprecated) ? BOOTSTRAP_VERSION : $deprecated; } break; case 'bootstrap_cdn_jsdelivr_theme': $deprecated = theme_get_setting('bootstrap_bootswatch', $theme); if (isset($deprecated)) { $setting = empty($deprecated) ? 'bootstrap' : $deprecated; } break; case 'bootstrap_forms_smart_descriptions': $deprecated = theme_get_setting('bootstrap_tooltip_descriptions', $theme); if (isset($deprecated)) { $setting = (int) !empty($deprecated); } break; case 'bootstrap_forms_smart_descriptions_limit': $deprecated = theme_get_setting('bootstrap_tooltip_descriptions_length', $theme); if (isset($deprecated)) { $setting = (int) !empty($deprecated); } break; } return isset($setting) ? $setting : $default; } /** * Sets a theme setting. * * @param string $name * The name of the setting to set. * @param mixed $value * The value of the setting to set. * @param string $prefix * The prefix used on the $name of the setting, this will be appended with * "_" automatically if set. * @param string $theme * The name of a given theme; defaults to the currently active theme. */ function _bootstrap_set_setting($name, $value, $prefix = 'bootstrap', $theme = NULL) { // If no key is given, use the current theme if we can determine it. if (!isset($theme)) { $theme = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : ''; } // Trigger at least once so it can populate defaults. theme_get_setting('', $theme); // Retrieve the cached settings. $cache = &drupal_static('theme_get_setting', array()); $settings = $cache[$theme]; // Set the value. $prefix = !empty($prefix) ? $prefix . '_' : ''; $settings["$prefix$name"] = $value; // Set the settings. variable_set('theme_' . $theme . '_settings', $settings); } /** * Retrieves an element's "attributes" array. * * @param array $element * The individual renderable array element. It is possible to also pass the * $variables parameter in [pre]process functions and it will logically * determine the correct path to that particular theme hook's attribute array. * Passed by reference. * @param string $property * Determines which attributes array to retrieve. By default, this is the * normal attributes, but can be "wrapper_attributes" or * "input_group_attributes". * * @return array * The attributes array. Passed by reference. */ function &_bootstrap_get_attributes(array &$element, $property = 'attributes') { // Attempt to retrieve a renderable element attributes first. if ( isset($element['#type']) || isset($element['#theme']) || isset($element['#pre_render']) || isset($element['#markup']) || isset($element['#theme_wrappers']) || isset($element["#$property"]) ) { if (!isset($element["#$property"])) { $element["#$property"] = array(); } return $element["#$property"]; } // Treat $element as if it were a [pre]process function $variables parameter // and look for a renderable "element". elseif (isset($element['element'])) { if (!isset($element['element']["#$property"])) { $element['element']["#$property"] = array(); } return $element['element']["#$property"]; } // If all else fails, create (if needed) a default "attributes" array. This // will, at the very least, either work or cause an error that can be tracked. if (!isset($element[$property])) { $element[$property] = array(); } return $element[$property]; } /** * Retrieves an element's "class" array. * * @param array $element * The individual renderable array element. It is possible to also pass the * $variables parameter in [pre]process functions and it will logically * determine the correct path to that particular theme hook's classes array. * Passed by reference. * @param string $property * Determines which attributes array to retrieve. By default, this is the * normal attributes, but can be "wrapper_attributes" or * "input_group_attributes". * * @return array * The classes array. Passed by reference. */ function &_bootstrap_get_classes(array &$element, $property = 'attributes') { $attributes = &_bootstrap_get_attributes($element, $property); if (!isset($attributes['class'])) { $attributes['class'] = array(); } // Contrib modules have a very bad habit of frequently adding classes as // strings, convert them to a proper array. // @see https://www.drupal.org/node/2269653 elseif (!is_array($attributes['class'])) { $attributes['class'] = explode(' ', $attributes['class']); } // Ensure classes are not duplicated. $attributes['class'] = array_unique($attributes['class']); return $attributes['class']; } /** * Adds a class to an element's render array. * * @param string|array $class * An individual class or an array of classes to add. * @param array $element * The individual renderable array element. It is possible to also pass the * $variables parameter in [pre]process functions and it will logically * determine the correct path to that particular theme hook's classes array. * Passed by reference. * @param string $property * Determines which attributes array to retrieve. By default, this is the * normal attributes, but can be "wrapper_attributes" or * "input_group_attributes". */ function _bootstrap_add_class($class, array &$element, $property = 'attributes') { // Retrieve the element's classes. $classes = &_bootstrap_get_classes($element, $property); // Convert the class to an array. if (!is_array($class)) { $class = array($class); } // Iterate over all classes to add. foreach ($class as $_class) { // Ensure the class to add does not yet already exist. if (!in_array($_class, $classes)) { $classes[] = $_class; } } } /** * Removes a class from an element's render array. * * @param string|array $class * An individual class or an array of classes to remove. * @param array $element * The individual renderable array element. It is possible to also pass the * $variables parameter in [pre]process functions and it will logically * determine the correct path to that particular theme hook's classes array. * Passed by reference. * @param string $property * Determines which attributes array to retrieve. By default, this is the * normal attributes, but can be "wrapper_attributes" or * "input_group_attributes". */ function _bootstrap_remove_class($class, array &$element, $property = 'attributes') { // Retrieve the element's classes. $classes = &_bootstrap_get_classes($element, $property); // Convert the class to an array. if (!is_array($class)) { $class = array($class); } // Iterate over all classes to add. foreach ($class as $_class) { $key = array_search($_class, $classes); if ($key !== FALSE) { unset($classes[$key]); } } } /** * Returns a list of base themes for active or provided theme. * * @param string $theme_key * The machine name of the theme to check, if not set the active theme name * will be used. * @param bool $include_theme_key * Whether to append the returned list with $theme_key. * * @return array * An indexed array of base themes. */ function _bootstrap_get_base_themes($theme_key = NULL, $include_theme_key = FALSE) { static $themes; if (!isset($theme_key)) { $theme_key = $GLOBALS['theme_key']; } if (!isset($themes[$theme_key])) { $themes[$theme_key] = array_unique(array_filter((array) bootstrap_get_theme_info($theme_key, 'base theme'))); } if ($include_theme_key) { $themes[$theme_key][] = $theme_key; } return $themes[$theme_key]; } /** * Retrieves the full base/sub-theme ancestry of a theme. * * @param string $theme_key * The machine name of the theme to check, if not set the active theme name * will be used. * @param bool $reverse * Whether or not to return the array of themes in reverse order, where the * active theme is the first entry. * * @return string[] * An array of theme names. */ function _bootstrap_get_ancestry($theme_key = NULL, $reverse = FALSE) { $ancestry = _bootstrap_get_base_themes($theme_key, TRUE); return $reverse ? array_reverse($ancestry) : $ancestry; } /** * Wrapper for the core file_scan_directory() function. * * Finds all files that match a given mask in a given directory and then caches * the results. A general site cache clear will force new scans to be initiated * for already cached directories. * * @param string $dir * The base directory or URI to scan, without trailing slash. * @param string $mask * The preg_match() regular expression of the files to find. * @param array $options * Additional options to pass to file_scan_directory(). * * @return array * An associative array (keyed on the chosen key) of objects with 'uri', * 'filename', and 'name' members corresponding to the matching files. * * @see file_scan_directory() */ function _bootstrap_file_scan_directory($dir, $mask, array $options = array()) { $files = &drupal_static(__FUNCTION__, array()); // Generate a unique cache identifier for all parameters passed as a change // in any of them would return different results. $cid = 'theme_registry:bootstrap:files:' . drupal_hash_base64(serialize(func_get_args())); // Load from DB cache or scan filesystem if files are not statically cached. if (!isset($files[$cid])) { if (($cache = cache_get($cid)) && isset($cache->data)) { $files[$cid] = $cache->data; } else { $files[$cid] = file_scan_directory($dir, $mask, $options); cache_set($cid, $files[$cid]); } } return $files[$cid]; } /** * Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities. * * Very similar to core's filter_xss(). It does, however, include the addition * of the "span", "div" and "i" elements which are commonly used in Bootstrap. * * @param string $string * The string with raw HTML in it. It will be stripped of everything that can * cause an XSS attack. * @param array $allowed_tags * An array of allowed tags. * * @return string * An XSS safe version of $string, or an empty string if $string is not * valid UTF-8. * * @see filter_xss() * @see filter_xss_admin() * * @deprecated Use filter_xss() or filter_xss_admin() instead. * Will be removed in a future release. */ function _bootstrap_filter_xss($string, array $allowed_tags = NULL) { if (is_null($allowed_tags)) { $allowed_tags = array( // Inline elements. 'a', 'cite', 'em', 'i', 'span', 'strong', // Block elements. 'blockquote', 'code', 'div', 'ul', 'ol', 'li', 'dl', 'dt', 'dd', ); } return filter_xss($string, $allowed_tags); } /** * Returns a list of available Bootstrap Glyphicons. * * @param string $version * The specific version of glyphicons to return. If not set, the latest * BOOTSTRAP_VERSION will be used. * * @return array * An associative array of icons keyed by their classes. */ function _bootstrap_glyphicons($version = NULL) { static $versions; if (!isset($versions)) { $versions = array(); $versions['3.0.0'] = array( // Class => Name. 'glyphicon-adjust' => 'adjust', 'glyphicon-align-center' => 'align-center', 'glyphicon-align-justify' => 'align-justify', 'glyphicon-align-left' => 'align-left', 'glyphicon-align-right' => 'align-right', 'glyphicon-arrow-down' => 'arrow-down', 'glyphicon-arrow-left' => 'arrow-left', 'glyphicon-arrow-right' => 'arrow-right', 'glyphicon-arrow-up' => 'arrow-up', 'glyphicon-asterisk' => 'asterisk', 'glyphicon-backward' => 'backward', 'glyphicon-ban-circle' => 'ban-circle', 'glyphicon-barcode' => 'barcode', 'glyphicon-bell' => 'bell', 'glyphicon-bold' => 'bold', 'glyphicon-book' => 'book', 'glyphicon-bookmark' => 'bookmark', 'glyphicon-briefcase' => 'briefcase', 'glyphicon-bullhorn' => 'bullhorn', 'glyphicon-calendar' => 'calendar', 'glyphicon-camera' => 'camera', 'glyphicon-certificate' => 'certificate', 'glyphicon-check' => 'check', 'glyphicon-chevron-down' => 'chevron-down', 'glyphicon-chevron-left' => 'chevron-left', 'glyphicon-chevron-right' => 'chevron-right', 'glyphicon-chevron-up' => 'chevron-up', 'glyphicon-circle-arrow-down' => 'circle-arrow-down', 'glyphicon-circle-arrow-left' => 'circle-arrow-left', 'glyphicon-circle-arrow-right' => 'circle-arrow-right', 'glyphicon-circle-arrow-up' => 'circle-arrow-up', 'glyphicon-cloud' => 'cloud', 'glyphicon-cloud-download' => 'cloud-download', 'glyphicon-cloud-upload' => 'cloud-upload', 'glyphicon-cog' => 'cog', 'glyphicon-collapse-down' => 'collapse-down', 'glyphicon-collapse-up' => 'collapse-up', 'glyphicon-comment' => 'comment', 'glyphicon-compressed' => 'compressed', 'glyphicon-copyright-mark' => 'copyright-mark', 'glyphicon-credit-card' => 'credit-card', 'glyphicon-cutlery' => 'cutlery', 'glyphicon-dashboard' => 'dashboard', 'glyphicon-download' => 'download', 'glyphicon-download-alt' => 'download-alt', 'glyphicon-earphone' => 'earphone', 'glyphicon-edit' => 'edit', 'glyphicon-eject' => 'eject', 'glyphicon-envelope' => 'envelope', 'glyphicon-euro' => 'euro', 'glyphicon-exclamation-sign' => 'exclamation-sign', 'glyphicon-expand' => 'expand', 'glyphicon-export' => 'export', 'glyphicon-eye-close' => 'eye-close', 'glyphicon-eye-open' => 'eye-open', 'glyphicon-facetime-video' => 'facetime-video', 'glyphicon-fast-backward' => 'fast-backward', 'glyphicon-fast-forward' => 'fast-forward', 'glyphicon-file' => 'file', 'glyphicon-film' => 'film', 'glyphicon-filter' => 'filter', 'glyphicon-fire' => 'fire', 'glyphicon-flag' => 'flag', 'glyphicon-flash' => 'flash', 'glyphicon-floppy-disk' => 'floppy-disk', 'glyphicon-floppy-open' => 'floppy-open', 'glyphicon-floppy-remove' => 'floppy-remove', 'glyphicon-floppy-save' => 'floppy-save', 'glyphicon-floppy-saved' => 'floppy-saved', 'glyphicon-folder-close' => 'folder-close', 'glyphicon-folder-open' => 'folder-open', 'glyphicon-font' => 'font', 'glyphicon-forward' => 'forward', 'glyphicon-fullscreen' => 'fullscreen', 'glyphicon-gbp' => 'gbp', 'glyphicon-gift' => 'gift', 'glyphicon-glass' => 'glass', 'glyphicon-globe' => 'globe', 'glyphicon-hand-down' => 'hand-down', 'glyphicon-hand-left' => 'hand-left', 'glyphicon-hand-right' => 'hand-right', 'glyphicon-hand-up' => 'hand-up', 'glyphicon-hd-video' => 'hd-video', 'glyphicon-hdd' => 'hdd', 'glyphicon-header' => 'header', 'glyphicon-headphones' => 'headphones', 'glyphicon-heart' => 'heart', 'glyphicon-heart-empty' => 'heart-empty', 'glyphicon-home' => 'home', 'glyphicon-import' => 'import', 'glyphicon-inbox' => 'inbox', 'glyphicon-indent-left' => 'indent-left', 'glyphicon-indent-right' => 'indent-right', 'glyphicon-info-sign' => 'info-sign', 'glyphicon-italic' => 'italic', 'glyphicon-leaf' => 'leaf', 'glyphicon-link' => 'link', 'glyphicon-list' => 'list', 'glyphicon-list-alt' => 'list-alt', 'glyphicon-lock' => 'lock', 'glyphicon-log-in' => 'log-in', 'glyphicon-log-out' => 'log-out', 'glyphicon-magnet' => 'magnet', 'glyphicon-map-marker' => 'map-marker', 'glyphicon-minus' => 'minus', 'glyphicon-minus-sign' => 'minus-sign', 'glyphicon-move' => 'move', 'glyphicon-music' => 'music', 'glyphicon-new-window' => 'new-window', 'glyphicon-off' => 'off', 'glyphicon-ok' => 'ok', 'glyphicon-ok-circle' => 'ok-circle', 'glyphicon-ok-sign' => 'ok-sign', 'glyphicon-open' => 'open', 'glyphicon-paperclip' => 'paperclip', 'glyphicon-pause' => 'pause', 'glyphicon-pencil' => 'pencil', 'glyphicon-phone' => 'phone', 'glyphicon-phone-alt' => 'phone-alt', 'glyphicon-picture' => 'picture', 'glyphicon-plane' => 'plane', 'glyphicon-play' => 'play', 'glyphicon-play-circle' => 'play-circle', 'glyphicon-plus' => 'plus', 'glyphicon-plus-sign' => 'plus-sign', 'glyphicon-print' => 'print', 'glyphicon-pushpin' => 'pushpin', 'glyphicon-qrcode' => 'qrcode', 'glyphicon-question-sign' => 'question-sign', 'glyphicon-random' => 'random', 'glyphicon-record' => 'record', 'glyphicon-refresh' => 'refresh', 'glyphicon-registration-mark' => 'registration-mark', 'glyphicon-remove' => 'remove', 'glyphicon-remove-circle' => 'remove-circle', 'glyphicon-remove-sign' => 'remove-sign', 'glyphicon-repeat' => 'repeat', 'glyphicon-resize-full' => 'resize-full', 'glyphicon-resize-horizontal' => 'resize-horizontal', 'glyphicon-resize-small' => 'resize-small', 'glyphicon-resize-vertical' => 'resize-vertical', 'glyphicon-retweet' => 'retweet', 'glyphicon-road' => 'road', 'glyphicon-save' => 'save', 'glyphicon-saved' => 'saved', 'glyphicon-screenshot' => 'screenshot', 'glyphicon-sd-video' => 'sd-video', 'glyphicon-search' => 'search', 'glyphicon-send' => 'send', 'glyphicon-share' => 'share', 'glyphicon-share-alt' => 'share-alt', 'glyphicon-shopping-cart' => 'shopping-cart', 'glyphicon-signal' => 'signal', 'glyphicon-sort' => 'sort', 'glyphicon-sort-by-alphabet' => 'sort-by-alphabet', 'glyphicon-sort-by-alphabet-alt' => 'sort-by-alphabet-alt', 'glyphicon-sort-by-attributes' => 'sort-by-attributes', 'glyphicon-sort-by-attributes-alt' => 'sort-by-attributes-alt', 'glyphicon-sort-by-order' => 'sort-by-order', 'glyphicon-sort-by-order-alt' => 'sort-by-order-alt', 'glyphicon-sound-5-1' => 'sound-5-1', 'glyphicon-sound-6-1' => 'sound-6-1', 'glyphicon-sound-7-1' => 'sound-7-1', 'glyphicon-sound-dolby' => 'sound-dolby', 'glyphicon-sound-stereo' => 'sound-stereo', 'glyphicon-star' => 'star', 'glyphicon-star-empty' => 'star-empty', 'glyphicon-stats' => 'stats', 'glyphicon-step-backward' => 'step-backward', 'glyphicon-step-forward' => 'step-forward', 'glyphicon-stop' => 'stop', 'glyphicon-subtitles' => 'subtitles', 'glyphicon-tag' => 'tag', 'glyphicon-tags' => 'tags', 'glyphicon-tasks' => 'tasks', 'glyphicon-text-height' => 'text-height', 'glyphicon-text-width' => 'text-width', 'glyphicon-th' => 'th', 'glyphicon-th-large' => 'th-large', 'glyphicon-th-list' => 'th-list', 'glyphicon-thumbs-down' => 'thumbs-down', 'glyphicon-thumbs-up' => 'thumbs-up', 'glyphicon-time' => 'time', 'glyphicon-tint' => 'tint', 'glyphicon-tower' => 'tower', 'glyphicon-transfer' => 'transfer', 'glyphicon-trash' => 'trash', 'glyphicon-tree-conifer' => 'tree-conifer', 'glyphicon-tree-deciduous' => 'tree-deciduous', 'glyphicon-unchecked' => 'unchecked', 'glyphicon-upload' => 'upload', 'glyphicon-usd' => 'usd', 'glyphicon-user' => 'user', 'glyphicon-volume-down' => 'volume-down', 'glyphicon-volume-off' => 'volume-off', 'glyphicon-volume-up' => 'volume-up', 'glyphicon-warning-sign' => 'warning-sign', 'glyphicon-wrench' => 'wrench', 'glyphicon-zoom-in' => 'zoom-in', 'glyphicon-zoom-out' => 'zoom-out', ); $versions['3.0.1'] = $versions['3.0.0']; $versions['3.0.2'] = $versions['3.0.1']; $versions['3.0.3'] = $versions['3.0.2']; $versions['3.1.0'] = $versions['3.0.3']; $versions['3.1.1'] = $versions['3.1.0']; $versions['3.2.0'] = $versions['3.1.1']; $versions['3.3.0'] = array_merge($versions['3.2.0'], array( 'glyphicon-eur' => 'eur', )); $versions['3.3.1'] = $versions['3.3.0']; $versions['3.3.2'] = array_merge($versions['3.3.1'], array( 'glyphicon-alert' => 'alert', 'glyphicon-apple' => 'apple', 'glyphicon-baby-formula' => 'baby-formula', 'glyphicon-bed' => 'bed', 'glyphicon-bishop' => 'bishop', 'glyphicon-bitcoin' => 'bitcoin', 'glyphicon-blackboard' => 'blackboard', 'glyphicon-cd' => 'cd', 'glyphicon-console' => 'console', 'glyphicon-copy' => 'copy', 'glyphicon-duplicate' => 'duplicate', 'glyphicon-education' => 'education', 'glyphicon-equalizer' => 'equalizer', 'glyphicon-erase' => 'erase', 'glyphicon-grain' => 'grain', 'glyphicon-hourglass' => 'hourglass', 'glyphicon-ice-lolly' => 'ice-lolly', 'glyphicon-ice-lolly-tasted' => 'ice-lolly-tasted', 'glyphicon-king' => 'king', 'glyphicon-knight' => 'knight', 'glyphicon-lamp' => 'lamp', 'glyphicon-level-up' => 'level-up', 'glyphicon-menu-down' => 'menu-down', 'glyphicon-menu-hamburger' => 'menu-hamburger', 'glyphicon-menu-left' => 'menu-left', 'glyphicon-menu-right' => 'menu-right', 'glyphicon-menu-up' => 'menu-up', 'glyphicon-modal-window' => 'modal-window', 'glyphicon-object-align-bottom' => 'object-align-bottom', 'glyphicon-object-align-horizontal' => 'object-align-horizontal', 'glyphicon-object-align-left' => 'object-align-left', 'glyphicon-object-align-right' => 'object-align-right', 'glyphicon-object-align-top' => 'object-align-top', 'glyphicon-object-align-vertical' => 'object-align-vertical', 'glyphicon-oil' => 'oil', 'glyphicon-open-file' => 'open-file', 'glyphicon-option-horizontal' => 'option-horizontal', 'glyphicon-option-vertical' => 'option-vertical', 'glyphicon-paste' => 'paste', 'glyphicon-pawn' => 'pawn', 'glyphicon-piggy-bank' => 'piggy-bank', 'glyphicon-queen' => 'queen', 'glyphicon-ruble' => 'ruble', 'glyphicon-save-file' => 'save-file', 'glyphicon-scale' => 'scale', 'glyphicon-scissors' => 'scissors', 'glyphicon-subscript' => 'subscript', 'glyphicon-sunglasses' => 'sunglasses', 'glyphicon-superscript' => 'superscript', 'glyphicon-tent' => 'tent', 'glyphicon-text-background' => 'text-background', 'glyphicon-text-color' => 'text-color', 'glyphicon-text-size' => 'text-size', 'glyphicon-triangle-bottom' => 'triangle-bottom', 'glyphicon-triangle-left' => 'triangle-left', 'glyphicon-triangle-right' => 'triangle-right', 'glyphicon-triangle-top' => 'triangle-top', 'glyphicon-yen' => 'yen', )); $versions['3.3.4'] = array_merge($versions['3.3.2'], array( 'glyphicon-btc' => 'btc', 'glyphicon-jpy' => 'jpy', 'glyphicon-rub' => 'rub', 'glyphicon-xbt' => 'xbt', )); $versions['3.3.5'] = $versions['3.3.4']; $versions['3.3.6'] = $versions['3.3.5']; $versions['3.3.7'] = $versions['3.3.6']; $versions['3.4.0'] = $versions['3.3.7']; $versions['3.4.1'] = $versions['3.4.0']; } // Return a specific versions icon set. if (isset($version) && isset($versions[$version])) { return $versions[$version]; } // Return the latest version. return $versions[BOOTSTRAP_VERSION]; } /** * Returns a specific Bootstrap Glyphicon as rendered HTML markup. * * @param string $name * The icon name, minus the "glyphicon-" prefix. * @param string $default * (Optional) The default value to return. * @param array $attributes * (Optional) Additional attributes to merge onto the icon. * * @return string * The HTML markup containing the icon defined by $name, $default value if * icon does not exist or returns empty output for whatever reason. */ function _bootstrap_icon($name, $default = NULL, array $attributes = array()) { $icon = _bootstrap_glyphicon($name, $default, $attributes); return render($icon); } /** * Returns a specific Bootstrap Glyphicon as a render array. * * Note: This function was added to keep BC with the former _bootstrap_icon() * implementation since it didn't return a render array. It is basically a * backport of 8.x-3.x code so the added $attributes parameter can be more * easily dealt with. * * @param string $name * The icon name, minus the "glyphicon-" prefix. * @param array|string $default * (Optional) The default render array to use if $name is not available. * @param array $attributes * (Optional) Additional attributes to merge onto the icon. * * @see https://www.drupal.org/project/bootstrap/issues/2844885 * * @return array * The render containing the icon defined by $name, $default value if * icon does not exist or returns NULL if no icon could be rendered. */ function _bootstrap_glyphicon($name, $default = array(), array $attributes = array()) { $icon = array(); // Ensure the icon specified is a valid Bootstrap Glyphicon. if (_bootstrap_glyphicons_supported() && in_array($name, _bootstrap_glyphicons())) { // Attempt to use the Icon API module, if enabled and it generates output. if (module_exists('icon')) { $icon = array( '#theme' => 'icon', '#bundle' => 'bootstrap', '#icon' => 'glyphicon-' . $name, '#attributes' => $attributes, ); } else { $icon = array( '#theme' => 'html_tag', '#tag' => 'span', '#value' => '', '#attributes' => $attributes, ); // Unlike 8.x-3.x, it will be easier to add the classes and aria-hidden // attribute afterwards since there is a passed default $attributes // parameter and drupal_array_merge_deep() is notorious for not properly // merging attributes, especially when dealing with classes. _bootstrap_add_class(array('icon', 'glyphicon', 'glyphicon-' . $name), $icon); $icon['#attributes']['aria-hidden'] = 'true'; } } // Return the icon. if (!empty($icon)) { return $icon; } // _bootstrap_icon() may pass NULL as $default. If so, return an empty array. return isset($default) ? $default : array(); } /** * Determine whether or not Bootstrap Glyphicons can be used. */ function _bootstrap_glyphicons_supported() { global $theme; // Use the advanced drupal_static() pattern, since this has the potential to // be called very often by _bootstrap_icon(). static $drupal_static_fast; if (!isset($drupal_static_fast)) { $drupal_static_fast['supported'] = &drupal_static(__FUNCTION__); } // Get static data. $supported = &$drupal_static_fast['supported']; // Retrieve supported themes. if (!isset($supported)) { $supported = array(); // Retrieve cached data. $cid = 'theme_registry:bootstrap:icon_support'; if (($cache = cache_get($cid)) && !empty($cache->data)) { $supported = $cache->data; } } // Determine active theme support if not yet set. if (!isset($supported[$theme])) { // Bootstrap based themes are enabled by default to use CDN. Check if // that is the case here so no file discovery is necessary. If the active // theme does not have this setting, it falls back to the base theme that // does. $supported[$theme] = !!bootstrap_get_cdn_assets('css', NULL, $theme); // CDN not used, iterate over all of the active (base) themes to determine // if they contain glyphicon font files. if (!$supported[$theme]) { foreach (_bootstrap_get_base_themes($theme, TRUE) as $_theme) { // Scan the theme for files. $fonts = _bootstrap_file_scan_directory(drupal_get_path('theme', $_theme), '/glyphicons-halflings-regular\.(eot|svg|ttf|woff)$/'); // Fonts found, stop the search. if (!empty($fonts)) { $supported[$theme] = TRUE; break; } } } // Cache all supported themes now that this theme is added to the array. cache_set($cid, $supported); } return $supported[$theme]; } /** * Determine whether a specific element is a button. * * @param array $element * A renderable element. * * @return bool * TRUE or FALSE. */ function _bootstrap_is_button(array $element) { return !empty($element['#type']) && !empty($element['#value']) && ( $element['#type'] === 'button' || $element['#type'] === 'submit' || $element['#type'] === 'image_button' ); } /** * Adds a specific Bootstrap class to color a button based on its text value. * * @param array $element * The form element, passed by reference. */ function _bootstrap_colorize_button(array &$element) { if (_bootstrap_is_button($element)) { // Do not add the class if one is already present in the array. $button_classes = array( 'btn-default', 'btn-primary', 'btn-success', 'btn-info', 'btn-warning', 'btn-danger', 'btn-link', ); $class_intersection = array_intersect($button_classes, $element['#attributes']['class']); if (empty($class_intersection)) { // Get the matched class. $class = bootstrap_setting('button_colorize') ? _bootstrap_colorize_text($element['#value']) : FALSE; // If no particular class matched, use the default style. if (!$class) { $class = 'default'; } $element['#attributes']['class'][] = 'btn-' . $class; } } } /** * Matches a Bootstrap class based on a string value. * * @param string $string * The string to match classes against. * @param string $default * The default class to return if no match is found. * * @return string * The Bootstrap class matched against the value of $haystack or $default if * no match could be made. */ function _bootstrap_colorize_text($string, $default = '') { static $texts; if (!isset($texts)) { $texts = array( // Text that match these specific strings are checked first. 'matches' => array( // Primary class. t('Download feature') => 'primary', // Success class. t('Add effect') => 'success', t('Add and configure') => 'success', // Info class. t('Save and add') => 'info', t('Add another item') => 'info', t('Update style') => 'info', ), // Text that contain these words anywhere in the string are checked last. 'contains' => array( // Primary class. t('Confirm') => 'primary', t('Filter') => 'primary', t('Log in') => 'primary', t('Submit') => 'primary', t('Search') => 'primary', t('Upload') => 'primary', // Success class. t('Add') => 'success', t('Create') => 'success', t('Save') => 'success', t('Write') => 'success', // Warning class. t('Export') => 'warning', t('Import') => 'warning', t('Restore') => 'warning', t('Rebuild') => 'warning', // Info class. t('Apply') => 'info', t('Update') => 'info', // Danger class. t('Delete') => 'danger', t('Remove') => 'danger', ), ); // Allow sub-themes to alter this array of patterns. drupal_alter('bootstrap_colorize_text', $texts); } // Iterate over the array. foreach ($texts as $pattern => $strings) { foreach ($strings as $value => $class) { switch ($pattern) { case 'matches': if ($string === $value) { return $class; } break; case 'contains': if (strpos(drupal_strtolower($string), drupal_strtolower($value)) !== FALSE) { return $class; } break; } } } // Return the default if nothing was matched. return $default; } /** * Adds an icon to button element based on its text value. * * @param array $element * The form element, passed by reference. */ function _bootstrap_iconize_button(array &$element) { if (bootstrap_setting('button_iconize') && _bootstrap_is_button($element) && ($icon = _bootstrap_iconize_text($element['#value']))) { $element['#icon'] = $icon; } } /** * Matches a Bootstrap Glyphicon based on a string value. * * @param string $string * The string to match classes against. * @param string $default * The default icon to return if no match is found. * * @return string * The Bootstrap icon matched against the value of $haystack or $default if * no match could be made. */ function _bootstrap_iconize_text($string, $default = '') { static $texts; if (!isset($texts)) { $texts = array( // Text that match these specific strings are checked first. 'matches' => array(), // Text that contain these words anywhere in the string are checked last. 'contains' => array( t('Manage') => 'cog', t('Configure') => 'cog', t('Download') => 'download', t('Export') => 'export', t('Filter') => 'filter', t('Import') => 'import', t('Save') => 'ok', t('Update') => 'ok', t('Edit') => 'pencil', t('Add') => 'plus', t('Write') => 'plus', t('Cancel') => 'remove', t('Delete') => 'trash', t('Remove') => 'trash', t('Upload') => 'upload', t('Log In') => 'log-in', ), ); // Allow sub-themes to alter this array of patterns. drupal_alter('bootstrap_iconize_text', $texts); } // Iterate over the array. foreach ($texts as $pattern => $strings) { foreach ($strings as $value => $icon) { switch ($pattern) { case 'matches': if ($string === $value) { return _bootstrap_icon($icon, $default); } break; case 'contains': if (strpos(drupal_strtolower($string), drupal_strtolower($value)) !== FALSE) { return _bootstrap_icon($icon, $default); } break; } } } // Return a default icon if nothing was matched. return _bootstrap_icon($default); } /** * Invokes a specific suggestion's preprocess functions. * * @param array $variables * The theme implementation variables array. */ function _bootstrap_preprocess_theme_suggestion(array &$variables) { $registry = theme_get_registry(); if (!empty($variables['theme_hook_suggestion']) && !empty($registry[$variables['theme_hook_suggestion']]['preprocess functions'])) { // Save the suggestion as the hook to pass to the function. $hook = $variables['theme_hook_suggestion']; // Iterate over the preprocess functions. foreach ($registry[$hook]['preprocess functions'] as $function) { // Ensure that the function is not this one (recursive) and exists. if ($function !== __FUNCTION__ && function_exists($function)) { // Invoke theme hook suggestion preprocess function. $function($variables, $hook); // Unset the theme_hook_suggestion so the suggestion's preprocess // functions can provide theme_hook_suggestions if needed. if (!empty($variables['theme_hook_suggestions'])) { unset($variables['theme_hook_suggestion']); } } } } } /** * Invokes a specific suggestion's process functions. * * @param array $variables * The theme implementation variables array. */ function _bootstrap_process_theme_suggestion(array &$variables) { $registry = theme_get_registry(); if (!empty($variables['theme_hook_suggestion']) && !empty($registry[$variables['theme_hook_suggestion']]['process functions'])) { // Save the suggestion as the hook to pass to the function. $hook = $variables['theme_hook_suggestion']; // Iterate over the process functions. foreach ($registry[$hook]['process functions'] as $function) { if (function_exists($function)) { // Invoke theme hook suggestion process function. $function($variables, $hook); // Unset the theme_hook_suggestion so the suggestion's preprocess // functions can provide theme_hook_suggestions if needed. if (!empty($variables['theme_hook_suggestions'])) { unset($variables['theme_hook_suggestion']); } } } } } /** * Determines if a string of text is considered "simple". * * @param string $string * The string of text to check "simple" criteria on. * @param int|false $length * The length of characters used to determine whether or not $string is * considered "simple". Set explicitly to FALSE to disable this criteria. * @param array|false $allowed_tags * An array of allowed tag elements. Set explicitly to FALSE to disable this * criteria. * @param bool $html * A variable, passed by reference, that indicates whether or not the * string contains HTML. * * @return bool * Returns TRUE if the $string is considered "simple", FALSE otherwise. */ function _bootstrap_is_simple_string($string, $length = 250, $allowed_tags = NULL, &$html = FALSE) { // Use the advanced drupal_static() pattern, since this is called very often. static $drupal_static_fast; if (!isset($drupal_static_fast)) { $drupal_static_fast['strings'] = &drupal_static(__FUNCTION__); } $strings = &$drupal_static_fast['strings']; if (!isset($strings[$string])) { $plain_string = strip_tags($string); $simple = TRUE; if ($allowed_tags !== FALSE) { $filtered_string = filter_xss($string, $allowed_tags); $html = $filtered_string !== $plain_string; $simple = $simple && $string === $filtered_string; } if ($length !== FALSE) { $simple = $simple && strlen($plain_string) <= intval($length); } $strings[$string] = $simple; } return $strings[$string]; } /** * Determines if the Path Breadcrumbs module theme function should be used. * * @param string $theme * The machine name of a specific theme to determine status if the Path * Breadcrumbs module has been configured to only use its internal function * on a specific list of themes. * * @return bool * TRUE or FALSE */ function _bootstrap_use_path_breadcrumbs($theme = NULL) { static $path_breadcrumbs; if (!isset($path_breadcrumbs)) { $path_breadcrumbs = FALSE; // Use active theme as the theme key if not explicitly set. if (!isset($theme)) { $theme = $GLOBALS['theme_key']; } // Determine whether or not the internal Path Breadcrumbs theme function // should be used or not. if (function_exists('path_breadcrumbs_breadcrumb') && module_exists('path_breadcrumbs')) { $internal_render = variable_get('path_breadcrumbs_internal_render', 1); $themes = variable_get('path_breadcrumbs_internal_render_themes', array()); $path_breadcrumbs = ($internal_render && (empty($themes) || in_array($theme, $themes))); } } return $path_breadcrumbs; } /** * @} End of "ingroup utility". */