Projet

Général

Profil

Paste
Télécharger (46,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / themes / bootstrap / includes / common.inc @ 7547bb19

1
<?php
2
/**
3
 * @file
4
 * List of common helper functions for use in Bootstrap based themes.
5
 */
6

    
7
define('BOOTSTRAP_VERSION_MAJOR', 3);
8
define('BOOTSTRAP_VERSION_MINOR', 3);
9
define('BOOTSTRAP_VERSION_PATCH', 5);
10
define('BOOTSTRAP_VERSION', BOOTSTRAP_VERSION_MAJOR . '.' . BOOTSTRAP_VERSION_MINOR . '.' . BOOTSTRAP_VERSION_PATCH);
11

    
12
/**
13
 * @defgroup subtheme_helper_functions Helper Functions
14
 * @ingroup subtheme
15
 *
16
 * List of common helper functions for use in Drupal Bootstrap based themes.
17
 *
18
 * @{
19
 */
20

    
21
/**
22
 * Converts an element description into a tooltip based on certain criteria.
23
 *
24
 * @param array $element
25
 *   An element render array, passed by reference.
26
 * @param array $target
27
 *   The target element render array the tooltip is to be attached to, passed
28
 *   by reference. If not set, it will default to the $element passed.
29
 * @param bool $input_only
30
 *   Toggle determining whether or not to only convert input elements.
31
 * @param int $length
32
 *   The length of characters to determine if description is "simple".
33
 */
34
function bootstrap_element_smart_description(&$element, &$target = NULL, $input_only = TRUE, $length = NULL) {
35
  // Determine if tooltips are enabled.
36
  static $enabled;
37
  if (!isset($enabled)) {
38
    $enabled = bootstrap_setting('tooltip_enabled') && bootstrap_setting('forms_smart_descriptions');
39
  }
40

    
41
  // Immediately return if "simple" tooltip descriptions are not enabled.
42
  if (!$enabled) {
43
    return;
44
  }
45

    
46
  // Allow a different element to attach the tooltip.
47
  if (!isset($target)) {
48
    $target = &$element;
49
  }
50

    
51
  // Retrieve the length limit for smart descriptions.
52
  if (!isset($length)) {
53
    $length = (int) bootstrap_setting('forms_smart_descriptions_limit');
54
    // Disable length checking by setting it to FALSE if empty.
55
    if (empty($length)) {
56
      $length = FALSE;
57
    }
58
  }
59

    
60
  // Retrieve the allowed tags for smart descriptions. This is primarily used
61
  // for display purposes only (i.e. non-UI/UX related elements that wouldn't
62
  // require a user to "click", like a link).
63
  $allowed_tags = array_filter(array_unique(array_map('trim', explode(',', bootstrap_setting('forms_smart_descriptions_allowed_tags') . ''))));
64

    
65
  // Disable length checking by setting it to FALSE if empty.
66
  if (empty($allowed_tags)) {
67
    $allowed_tags = FALSE;
68
  }
69

    
70
  $html = FALSE;
71
  $type = !empty($element['#type']) ? $element['#type'] : FALSE;
72

    
73
  // Return if element or target shouldn't have "simple" tooltip descriptions.
74
  if (($input_only && !isset($target['#input']))
75
    // Ignore text_format elements.
76
    // @see https://www.drupal.org/node/2478339
77
    || $type === 'text_format'
78

    
79
    // Ignore if the actual element has no #description set.
80
    || empty($element['#description'])
81

    
82
    // Ignore if the target element already has a "data-toggle" attribute set.
83
    || !empty($target['#attributes']['data-toggle'])
84

    
85
    // Ignore if the target element is #disabled.
86
    || isset($target['#disabled'])
87

    
88
    // Ignore if either the actual element or target element has an explicit
89
    // #smart_description property set to FALSE.
90
    || (isset($element['#smart_description']) && !$element['#smart_description'])
91
    || (isset($target['#smart_description']) && !$target['#smart_description'])
92

    
93
    // Ignore if the description is not "simple".
94
    || !_bootstrap_is_simple_string($element['#description'], $length, $allowed_tags, $html)
95
  ) {
96
    // Set the both the actual element and the target element
97
    // #smart_description property to FALSE.
98
    $element['#smart_description'] = FALSE;
99
    $target['#smart_description'] = FALSE;
100
    return;
101
  }
102

    
103
  // Default property (on the element itself).
104
  $property = 'attributes';
105

    
106
  // Add the tooltip to the #label_attributes property for 'checkbox'
107
  // and 'radio' elements.
108
  if ($type === 'checkbox' || $type === 'radio') {
109
    $property = 'label_attributes';
110
  }
111
  // Add the tooltip to the #wrapper_attributes property for 'checkboxes'
112
  // and 'radios' elements.
113
  elseif ($type === 'checkboxes' || $type === 'radios') {
114
    $property = 'wrapper_attributes';
115
  }
116
  // Add the tooltip to the #input_group_attributes property for elements
117
  // that have valid input groups set.
118
  elseif ((!empty($element['#field_prefix']) || !empty($element['#field_suffix'])) && (!empty($element['#input_group']) || !empty($element['#input_group_button']))) {
119
    $property = 'input_group_attributes';
120
  }
121

    
122
  // Retrieve the proper attributes array.
123
  $attributes = &_bootstrap_get_attributes($target, $property);
124

    
125
  // Set the tooltip attributes.
126
  $attributes['title'] = $allowed_tags !== FALSE ? filter_xss($element['#description'], $allowed_tags) : $element['#description'];
127
  $attributes['data-toggle'] = 'tooltip';
128
  if ($html || $allowed_tags === FALSE) {
129
    $attributes['data-html'] = 'true';
130
  }
131

    
132
  // Remove the element description so it isn't (re-)rendered later.
133
  unset($element['#description']);
134
}
135

    
136
/**
137
 * Retrieves CDN assets for the active provider, if any.
138
 *
139
 * @param string|array $type
140
 *   The type of asset to retrieve: "css" or "js", defaults to an array
141
 *   array containing both if not set.
142
 * @param string $provider
143
 *   The name of a specific CDN provider to use, defaults to the active provider
144
 *   set in the theme settings.
145
 * @param string $theme
146
 *   The name of a specific theme the settings should be retrieved from,
147
 *   defaults to the active theme.
148
 *
149
 * @return array
150
 *   If $type is a string or an array with only one (1) item in it, the assets
151
 *   are returned as an indexed array of files. Otherwise, an associative array
152
 *   is returned keyed by the type.
153
 */
154
function bootstrap_get_cdn_assets($type = NULL, $provider = NULL, $theme = NULL) {
155
  $original_type = $type;
156
  $assets = array();
157
  $types = array();
158

    
159
  // If no type is set, return all CSS and JS.
160
  if (!isset($type)) {
161
    $types = array('css', 'js');
162
  }
163
  elseif (isset($type)) {
164
    if (!is_array($type)) {
165
      $type = array($type);
166
    }
167
    $types = $type;
168
  }
169

    
170
  // Ensure default arrays exist for the requested types.
171
  foreach ($types as $type) {
172
    $assets[$type] = array();
173
  }
174

    
175
  // Retrieve the CDN provider from the theme.
176
  if (!isset($provider)) {
177
    $provider = bootstrap_setting('cdn_provider', $theme);
178
  }
179

    
180
  // Load the CDN provider data.
181
  bootstrap_include('bootstrap', 'includes/cdn.inc');
182
  if (!empty($provider) && ($data = bootstrap_cdn_provider($provider))) {
183
    // Alter the assets based on provider.
184
    $function = 'bootstrap_bootstrap_cdn_provider_' . $provider . '_assets_alter';
185
    if (function_exists($function)) {
186
      $function($data, $theme);
187
    }
188
    // @todo Use drupal_alter() once CDN is in bootstrap_core companion module.
189
    // drupal_alter('bootstrap_cdn_provider_' . $provider . '_assets', $data, $theme);
190

    
191
    // Iterate over each type.
192
    foreach ($types as $type) {
193
      if (variable_get("preprocess_$type", FALSE) && !empty($data['min'][$type])) {
194
        $assets[$type] = $data['min'][$type];
195
      }
196
      elseif (!empty($data[$type])) {
197
        $assets[$type] = $data[$type];
198
      }
199
    }
200
  }
201

    
202
  return is_string($original_type) ? $assets[$original_type] : $assets;
203
}
204

    
205
/**
206
 * Return information from the .info file of a theme (and possible base themes).
207
 *
208
 * @param string $theme_key
209
 *   The machine name of the theme.
210
 * @param string $key
211
 *   The key name of the item to return from the .info file. This value can
212
 *   include "][" to automatically attempt to traverse any arrays.
213
 * @param bool $base_themes
214
 *   Recursively search base themes, defaults to TRUE.
215
 *
216
 * @return string|array|false
217
 *   A string or array depending on the type of value and if a base theme also
218
 *   contains the same $key, FALSE if no $key is found.
219
 */
220
function bootstrap_get_theme_info($theme_key = NULL, $key = NULL, $base_themes = TRUE) {
221
  // If no $theme_key is given, use the current theme if we can determine it.
222
  if (!isset($theme_key)) {
223
    $theme_key = !empty($GLOBALS['theme_key']) ? $GLOBALS['theme_key'] : FALSE;
224
  }
225
  if ($theme_key) {
226
    $themes = list_themes();
227
    if (!empty($themes[$theme_key])) {
228
      $theme = $themes[$theme_key];
229
      // If a key name was specified, return just that array.
230
      if ($key) {
231
        $value = FALSE;
232
        // Recursively add base theme values.
233
        if ($base_themes && isset($theme->base_themes)) {
234
          foreach (array_keys($theme->base_themes) as $base_theme) {
235
            $value = bootstrap_get_theme_info($base_theme, $key);
236
          }
237
        }
238
        if (!empty($themes[$theme_key])) {
239
          $info = $themes[$theme_key]->info;
240
          // Allow array traversal.
241
          $keys = explode('][', $key);
242
          foreach ($keys as $parent) {
243
            if (isset($info[$parent])) {
244
              $info = $info[$parent];
245
            }
246
            else {
247
              $info = FALSE;
248
            }
249
          }
250
          if (is_array($value)) {
251
            if (!empty($info)) {
252
              if (!is_array($info)) {
253
                $info = array($info);
254
              }
255
              $value = drupal_array_merge_deep($value, $info);
256
            }
257
          }
258
          else {
259
            if (!empty($info)) {
260
              if (empty($value)) {
261
                $value = $info;
262
              }
263
              else {
264
                if (!is_array($value)) {
265
                  $value = array($value);
266
                }
267
                if (!is_array($info)) {
268
                  $info = array($info);
269
                }
270
                $value = drupal_array_merge_deep($value, $info);
271
              }
272
            }
273
          }
274
        }
275
        return $value;
276
      }
277
      // If no info $key was specified, just return the entire info array.
278
      return $theme->info;
279
    }
280
  }
281
  return FALSE;
282
}
283

    
284
/**
285
 * Includes a theme file.
286
 *
287
 * @param string $theme
288
 *   Name of the theme to use for base path.
289
 * @param string $path
290
 *   Path relative to $theme.
291
 */
292
function bootstrap_include($theme, $path) {
293
  static $themes = array();
294
  if (!isset($themes[$theme])) {
295
    $themes[$theme] = drupal_get_path('theme', $theme);
296
  }
297
  if ($themes[$theme] && ($file = DRUPAL_ROOT . '/' . $themes[$theme] . '/' . $path) && file_exists($file)) {
298
    include_once $file;
299
  }
300
}
301

    
302
/**
303
 * Retrieves a setting for the current theme or for a given theme.
304
 *
305
 * This is a wrapper for theme_get_setting(), ensuring to use deprecated
306
 * setting values instead.
307
 *
308
 * @param string $name
309
 *   The name of the setting to be retrieved.
310
 * @param string $theme
311
 *   The name of a given theme; defaults to the currently active theme.
312
 * @param string $prefix
313
 *   The prefix used on the $name of the setting, this will be appended with
314
 *   "_" automatically if set.
315
 *
316
 * @return mixed
317
 *   The value of the requested setting, NULL if the setting does not exist.
318
 *
319
 * @see theme_get_setting()
320
 *
321
 * @todo Refactor in 7.x-4.x and get rid of the deprecated settings.
322
 */
323
function bootstrap_setting($name, $theme = NULL, $prefix = 'bootstrap') {
324
  $prefix = !empty($prefix) ? $prefix . '_' : '';
325
  $setting = theme_get_setting($prefix . $name, $theme);
326
  switch ($prefix . $name) {
327
    case 'bootstrap_cdn_provider':
328
      $deprecated = theme_get_setting('bootstrap_cdn', $theme);
329
      if (isset($deprecated)) {
330
        $setting = empty($deprecated) ? '' : 'jsdelivr';
331
      }
332
      break;
333

    
334
    case 'bootstrap_cdn_jsdelivr_version':
335
      $deprecated = theme_get_setting('bootstrap_cdn', $theme);
336
      if (isset($deprecated)) {
337
        $setting = empty($deprecated) ? BOOTSTRAP_VERSION : $deprecated;
338
      }
339
      break;
340

    
341
    case 'bootstrap_cdn_jsdelivr_theme':
342
      $deprecated = theme_get_setting('bootstrap_bootswatch', $theme);
343
      if (isset($deprecated)) {
344
        $setting = empty($deprecated) ? 'bootstrap' : $deprecated;
345
      }
346
      break;
347

    
348
    case 'bootstrap_forms_smart_descriptions':
349
      $deprecated = theme_get_setting('bootstrap_tooltip_descriptions', $theme);
350
      if (isset($deprecated)) {
351
        $setting = (int) !empty($deprecated);
352
      }
353
      break;
354

    
355
    case 'bootstrap_forms_smart_descriptions_limit':
356
      $deprecated = theme_get_setting('bootstrap_tooltip_descriptions_length', $theme);
357
      if (isset($deprecated)) {
358
        $setting = (int) !empty($deprecated);
359
      }
360
      break;
361

    
362
  }
363
  return $setting;
364
}
365

    
366
/**
367
 * Retrieves an element's "attributes" array.
368
 *
369
 * @param array $element
370
 *   The individual renderable array element. It is possible to also pass the
371
 *   $variables parameter in [pre]process functions and it will logically
372
 *   determine the correct path to that particular theme hook's attribute array.
373
 *   Passed by reference.
374
 * @param string $property
375
 *   Determines which attributes array to retrieve. By default, this is the
376
 *   normal attributes, but can be "wrapper_attributes" or
377
 *   "input_group_attributes".
378
 *
379
 * @return array
380
 *   The attributes array. Passed by reference.
381
 */
382
function &_bootstrap_get_attributes(&$element, $property = 'attributes') {
383
  // Attempt to retrieve a renderable element attributes first.
384
  if (
385
    isset($element['#type']) ||
386
    isset($element['#theme']) ||
387
    isset($element['#pre_render']) ||
388
    isset($element['#markup']) ||
389
    isset($element['#theme_wrappers']) ||
390
    isset($element["#$property"])
391
  ) {
392
    if (!isset($element["#$property"])) {
393
      $element["#$property"] = array();
394
    }
395
    return $element["#$property"];
396
  }
397
  // Treat $element as if it were a [pre]process function $variables parameter
398
  // and look for a renderable "element".
399
  elseif (isset($element['element'])) {
400
    if (!isset($element['element']["#$property"])) {
401
      $element['element']["#$property"] = array();
402
    }
403
    return $element['element']["#$property"];
404
  }
405

    
406
  // If all else fails, create (if needed) a default "attributes" array. This
407
  // will, at the very least, either work or cause an error that can be tracked.
408
  if (!isset($element[$property])) {
409
    $element[$property] = array();
410
  }
411

    
412
  return $element[$property];
413
}
414

    
415
/**
416
 * Retrieves an element's "class" array.
417
 *
418
 * @param array $element
419
 *   The individual renderable array element. It is possible to also pass the
420
 *   $variables parameter in [pre]process functions and it will logically
421
 *   determine the correct path to that particular theme hook's classes array.
422
 *   Passed by reference.
423
 * @param string $property
424
 *   Determines which attributes array to retrieve. By default, this is the
425
 *   normal attributes, but can be "wrapper_attributes" or
426
 *   "input_group_attributes".
427
 *
428
 * @return array
429
 *   The classes array. Passed by reference.
430
 */
431
function &_bootstrap_get_classes(&$element, $property = 'attributes') {
432
  $attributes = &_bootstrap_get_attributes($element, $property);
433

    
434
  if (!isset($attributes['class'])) {
435
    $attributes['class'] = array();
436
  }
437
  // Contrib modules have a very bad habit of frequently adding classes as
438
  // strings, convert them to a proper array.
439
  // @see https://www.drupal.org/node/2269653
440
  elseif (!is_array($attributes['class'])) {
441
    $attributes['class'] = explode(' ', $attributes['class']);
442
  }
443

    
444
  // Ensure classes are not duplicated.
445
  $attributes['class'] = array_unique($attributes['class']);
446
  return $attributes['class'];
447
}
448

    
449
/**
450
 * Adds a class to an element's render array.
451
 *
452
 * @param string|array $class
453
 *   An individual class or an array of classes to add.
454
 * @param array $element
455
 *   The individual renderable array element. It is possible to also pass the
456
 *   $variables parameter in [pre]process functions and it will logically
457
 *   determine the correct path to that particular theme hook's classes array.
458
 *   Passed by reference.
459
 * @param string $property
460
 *   Determines which attributes array to retrieve. By default, this is the
461
 *   normal attributes, but can be "wrapper_attributes" or
462
 *   "input_group_attributes".
463
 */
464
function _bootstrap_add_class($class, &$element, $property = 'attributes') {
465
  // Retrieve the element's classes.
466
  $classes = &_bootstrap_get_classes($element, $property);
467

    
468
  // Convert the class to an array.
469
  if (!is_array($class)) {
470
    $class = array($class);
471
  }
472

    
473
  // Iterate over all classes to add.
474
  foreach ($class as $_class) {
475
    // Ensure the class to add does not yet already exist.
476
    if (!in_array($_class, $classes)) {
477
      $classes[] = $_class;
478
    }
479
  }
480
}
481

    
482
/**
483
 * Removes a class from an element's render array.
484
 *
485
 * @param string|array $class
486
 *   An individual class or an array of classes to remove.
487
 * @param array $element
488
 *   The individual renderable array element. It is possible to also pass the
489
 *   $variables parameter in [pre]process functions and it will logically
490
 *   determine the correct path to that particular theme hook's classes array.
491
 *   Passed by reference.
492
 * @param string $property
493
 *   Determines which attributes array to retrieve. By default, this is the
494
 *   normal attributes, but can be "wrapper_attributes" or
495
 *   "input_group_attributes".
496
 */
497
function _bootstrap_remove_class($class, &$element, $property = 'attributes') {
498
  // Retrieve the element's classes.
499
  $classes = &_bootstrap_get_classes($element, $property);
500

    
501
  // Convert the class to an array.
502
  if (!is_array($class)) {
503
    $class = array($class);
504
  }
505

    
506
  // Iterate over all classes to add.
507
  foreach ($class as $_class) {
508
    $key = array_search($_class, $classes);
509
    if ($key !== FALSE) {
510
      unset($classes[$key]);
511
    }
512
  }
513
}
514

    
515
/**
516
 * Returns a list of base themes for active or provided theme.
517
 *
518
 * @param string $theme_key
519
 *   The machine name of the theme to check, if not set the active theme name
520
 *   will be used.
521
 * @param bool $include_theme_key
522
 *   Whether to append the returned list with $theme_key.
523
 *
524
 * @return array
525
 *   An indexed array of base themes.
526
 */
527
function _bootstrap_get_base_themes($theme_key = NULL, $include_theme_key = FALSE) {
528
  static $themes;
529
  if (!isset($theme_key)) {
530
    $theme_key = $GLOBALS['theme_key'];
531
  }
532
  if (!isset($themes[$theme_key])) {
533
    $themes[$theme_key] = array_unique(array_filter((array) bootstrap_get_theme_info($theme_key, 'base theme')));
534
  }
535
  if ($include_theme_key) {
536
    $themes[$theme_key][] = $theme_key;
537
  }
538
  return $themes[$theme_key];
539
}
540

    
541
/**
542
 * Wrapper for the core file_scan_directory() function.
543
 *
544
 * Finds all files that match a given mask in a given directory and then caches
545
 * the results. A general site cache clear will force new scans to be initiated
546
 * for already cached directories.
547
 *
548
 * @param string $dir
549
 *   The base directory or URI to scan, without trailing slash.
550
 * @param string $mask
551
 *   The preg_match() regular expression of the files to find.
552
 * @param array $options
553
 *   Additional options to pass to file_scan_directory().
554
 *
555
 * @return array
556
 *   An associative array (keyed on the chosen key) of objects with 'uri',
557
 *   'filename', and 'name' members corresponding to the matching files.
558
 *
559
 * @see file_scan_directory()
560
 */
561
function _bootstrap_file_scan_directory($dir, $mask, array $options = array()) {
562
  // Retrieve cached data.
563
  $cid = 'theme_registry:bootstrap:files';
564
  $files = array();
565
  if ($cache = cache_get($cid)) {
566
    $files = $cache->data;
567
  }
568
  // Generate a unique hash for all parameters passed as a change in any of
569
  // them would return different results.
570
  $hash = drupal_hash_base64(serialize(func_get_args()));
571
  if (!isset($files[$hash])) {
572
    $files[$hash] = file_scan_directory($dir, $mask, $options);
573
    cache_set($cid, $files);
574
  }
575
  return $files[$hash];
576
}
577

    
578
/**
579
 * Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities.
580
 *
581
 * Very similar to core's filter_xss(). It does, however, include the addition
582
 * of the "span", "div" and "i" elements which are commonly used in Bootstrap.
583
 *
584
 * @param string $string
585
 *   The string with raw HTML in it. It will be stripped of everything that can
586
 *   cause an XSS attack.
587
 * @param array $allowed_tags
588
 *   An array of allowed tags.
589
 *
590
 * @return string
591
 *   An XSS safe version of $string, or an empty string if $string is not
592
 *   valid UTF-8.
593
 *
594
 * @see filter_xss()
595
 * @see filter_xss_admin()
596
 *
597
 * @deprecated Use filter_xss() or filter_xss_admin() instead.
598
 * Will be removed in a future release.
599
 */
600
function _bootstrap_filter_xss($string, array $allowed_tags = NULL) {
601
  if (is_null($allowed_tags)) {
602
    $allowed_tags = array(
603
      // Inline elements.
604
      'a',
605
      'cite',
606
      'em',
607
      'i',
608
      'span',
609
      'strong',
610

    
611
      // Block elements.
612
      'blockquote',
613
      'code',
614
      'div',
615
      'ul',
616
      'ol',
617
      'li',
618
      'dl',
619
      'dt',
620
      'dd',
621
    );
622
  }
623
  return filter_xss($string, $allowed_tags);
624
}
625

    
626
/**
627
 * Returns a list of available Bootstrap Glyphicons.
628
 *
629
 * @param string $version
630
 *   The specific version of glyphicons to return. If not set, the latest
631
 *   BOOTSTRAP_VERSION will be used.
632
 *
633
 * @return array
634
 *   An associative array of icons keyed by their classes.
635
 */
636
function _bootstrap_glyphicons($version = NULL) {
637
  static $versions;
638
  if (!isset($versions)) {
639
    $versions = array();
640
    $versions['3.0.0'] = array(
641
      // Class => Name.
642
      'glyphicon-adjust' => 'adjust',
643
      'glyphicon-align-center' => 'align-center',
644
      'glyphicon-align-justify' => 'align-justify',
645
      'glyphicon-align-left' => 'align-left',
646
      'glyphicon-align-right' => 'align-right',
647
      'glyphicon-arrow-down' => 'arrow-down',
648
      'glyphicon-arrow-left' => 'arrow-left',
649
      'glyphicon-arrow-right' => 'arrow-right',
650
      'glyphicon-arrow-up' => 'arrow-up',
651
      'glyphicon-asterisk' => 'asterisk',
652
      'glyphicon-backward' => 'backward',
653
      'glyphicon-ban-circle' => 'ban-circle',
654
      'glyphicon-barcode' => 'barcode',
655
      'glyphicon-bell' => 'bell',
656
      'glyphicon-bold' => 'bold',
657
      'glyphicon-book' => 'book',
658
      'glyphicon-bookmark' => 'bookmark',
659
      'glyphicon-briefcase' => 'briefcase',
660
      'glyphicon-bullhorn' => 'bullhorn',
661
      'glyphicon-calendar' => 'calendar',
662
      'glyphicon-camera' => 'camera',
663
      'glyphicon-certificate' => 'certificate',
664
      'glyphicon-check' => 'check',
665
      'glyphicon-chevron-down' => 'chevron-down',
666
      'glyphicon-chevron-left' => 'chevron-left',
667
      'glyphicon-chevron-right' => 'chevron-right',
668
      'glyphicon-chevron-up' => 'chevron-up',
669
      'glyphicon-circle-arrow-down' => 'circle-arrow-down',
670
      'glyphicon-circle-arrow-left' => 'circle-arrow-left',
671
      'glyphicon-circle-arrow-right' => 'circle-arrow-right',
672
      'glyphicon-circle-arrow-up' => 'circle-arrow-up',
673
      'glyphicon-cloud' => 'cloud',
674
      'glyphicon-cloud-download' => 'cloud-download',
675
      'glyphicon-cloud-upload' => 'cloud-upload',
676
      'glyphicon-cog' => 'cog',
677
      'glyphicon-collapse-down' => 'collapse-down',
678
      'glyphicon-collapse-up' => 'collapse-up',
679
      'glyphicon-comment' => 'comment',
680
      'glyphicon-compressed' => 'compressed',
681
      'glyphicon-copyright-mark' => 'copyright-mark',
682
      'glyphicon-credit-card' => 'credit-card',
683
      'glyphicon-cutlery' => 'cutlery',
684
      'glyphicon-dashboard' => 'dashboard',
685
      'glyphicon-download' => 'download',
686
      'glyphicon-download-alt' => 'download-alt',
687
      'glyphicon-earphone' => 'earphone',
688
      'glyphicon-edit' => 'edit',
689
      'glyphicon-eject' => 'eject',
690
      'glyphicon-envelope' => 'envelope',
691
      'glyphicon-euro' => 'euro',
692
      'glyphicon-exclamation-sign' => 'exclamation-sign',
693
      'glyphicon-expand' => 'expand',
694
      'glyphicon-export' => 'export',
695
      'glyphicon-eye-close' => 'eye-close',
696
      'glyphicon-eye-open' => 'eye-open',
697
      'glyphicon-facetime-video' => 'facetime-video',
698
      'glyphicon-fast-backward' => 'fast-backward',
699
      'glyphicon-fast-forward' => 'fast-forward',
700
      'glyphicon-file' => 'file',
701
      'glyphicon-film' => 'film',
702
      'glyphicon-filter' => 'filter',
703
      'glyphicon-fire' => 'fire',
704
      'glyphicon-flag' => 'flag',
705
      'glyphicon-flash' => 'flash',
706
      'glyphicon-floppy-disk' => 'floppy-disk',
707
      'glyphicon-floppy-open' => 'floppy-open',
708
      'glyphicon-floppy-remove' => 'floppy-remove',
709
      'glyphicon-floppy-save' => 'floppy-save',
710
      'glyphicon-floppy-saved' => 'floppy-saved',
711
      'glyphicon-folder-close' => 'folder-close',
712
      'glyphicon-folder-open' => 'folder-open',
713
      'glyphicon-font' => 'font',
714
      'glyphicon-forward' => 'forward',
715
      'glyphicon-fullscreen' => 'fullscreen',
716
      'glyphicon-gbp' => 'gbp',
717
      'glyphicon-gift' => 'gift',
718
      'glyphicon-glass' => 'glass',
719
      'glyphicon-globe' => 'globe',
720
      'glyphicon-hand-down' => 'hand-down',
721
      'glyphicon-hand-left' => 'hand-left',
722
      'glyphicon-hand-right' => 'hand-right',
723
      'glyphicon-hand-up' => 'hand-up',
724
      'glyphicon-hd-video' => 'hd-video',
725
      'glyphicon-hdd' => 'hdd',
726
      'glyphicon-header' => 'header',
727
      'glyphicon-headphones' => 'headphones',
728
      'glyphicon-heart' => 'heart',
729
      'glyphicon-heart-empty' => 'heart-empty',
730
      'glyphicon-home' => 'home',
731
      'glyphicon-import' => 'import',
732
      'glyphicon-inbox' => 'inbox',
733
      'glyphicon-indent-left' => 'indent-left',
734
      'glyphicon-indent-right' => 'indent-right',
735
      'glyphicon-info-sign' => 'info-sign',
736
      'glyphicon-italic' => 'italic',
737
      'glyphicon-leaf' => 'leaf',
738
      'glyphicon-link' => 'link',
739
      'glyphicon-list' => 'list',
740
      'glyphicon-list-alt' => 'list-alt',
741
      'glyphicon-lock' => 'lock',
742
      'glyphicon-log-in' => 'log-in',
743
      'glyphicon-log-out' => 'log-out',
744
      'glyphicon-magnet' => 'magnet',
745
      'glyphicon-map-marker' => 'map-marker',
746
      'glyphicon-minus' => 'minus',
747
      'glyphicon-minus-sign' => 'minus-sign',
748
      'glyphicon-move' => 'move',
749
      'glyphicon-music' => 'music',
750
      'glyphicon-new-window' => 'new-window',
751
      'glyphicon-off' => 'off',
752
      'glyphicon-ok' => 'ok',
753
      'glyphicon-ok-circle' => 'ok-circle',
754
      'glyphicon-ok-sign' => 'ok-sign',
755
      'glyphicon-open' => 'open',
756
      'glyphicon-paperclip' => 'paperclip',
757
      'glyphicon-pause' => 'pause',
758
      'glyphicon-pencil' => 'pencil',
759
      'glyphicon-phone' => 'phone',
760
      'glyphicon-phone-alt' => 'phone-alt',
761
      'glyphicon-picture' => 'picture',
762
      'glyphicon-plane' => 'plane',
763
      'glyphicon-play' => 'play',
764
      'glyphicon-play-circle' => 'play-circle',
765
      'glyphicon-plus' => 'plus',
766
      'glyphicon-plus-sign' => 'plus-sign',
767
      'glyphicon-print' => 'print',
768
      'glyphicon-pushpin' => 'pushpin',
769
      'glyphicon-qrcode' => 'qrcode',
770
      'glyphicon-question-sign' => 'question-sign',
771
      'glyphicon-random' => 'random',
772
      'glyphicon-record' => 'record',
773
      'glyphicon-refresh' => 'refresh',
774
      'glyphicon-registration-mark' => 'registration-mark',
775
      'glyphicon-remove' => 'remove',
776
      'glyphicon-remove-circle' => 'remove-circle',
777
      'glyphicon-remove-sign' => 'remove-sign',
778
      'glyphicon-repeat' => 'repeat',
779
      'glyphicon-resize-full' => 'resize-full',
780
      'glyphicon-resize-horizontal' => 'resize-horizontal',
781
      'glyphicon-resize-small' => 'resize-small',
782
      'glyphicon-resize-vertical' => 'resize-vertical',
783
      'glyphicon-retweet' => 'retweet',
784
      'glyphicon-road' => 'road',
785
      'glyphicon-save' => 'save',
786
      'glyphicon-saved' => 'saved',
787
      'glyphicon-screenshot' => 'screenshot',
788
      'glyphicon-sd-video' => 'sd-video',
789
      'glyphicon-search' => 'search',
790
      'glyphicon-send' => 'send',
791
      'glyphicon-share' => 'share',
792
      'glyphicon-share-alt' => 'share-alt',
793
      'glyphicon-shopping-cart' => 'shopping-cart',
794
      'glyphicon-signal' => 'signal',
795
      'glyphicon-sort' => 'sort',
796
      'glyphicon-sort-by-alphabet' => 'sort-by-alphabet',
797
      'glyphicon-sort-by-alphabet-alt' => 'sort-by-alphabet-alt',
798
      'glyphicon-sort-by-attributes' => 'sort-by-attributes',
799
      'glyphicon-sort-by-attributes-alt' => 'sort-by-attributes-alt',
800
      'glyphicon-sort-by-order' => 'sort-by-order',
801
      'glyphicon-sort-by-order-alt' => 'sort-by-order-alt',
802
      'glyphicon-sound-5-1' => 'sound-5-1',
803
      'glyphicon-sound-6-1' => 'sound-6-1',
804
      'glyphicon-sound-7-1' => 'sound-7-1',
805
      'glyphicon-sound-dolby' => 'sound-dolby',
806
      'glyphicon-sound-stereo' => 'sound-stereo',
807
      'glyphicon-star' => 'star',
808
      'glyphicon-star-empty' => 'star-empty',
809
      'glyphicon-stats' => 'stats',
810
      'glyphicon-step-backward' => 'step-backward',
811
      'glyphicon-step-forward' => 'step-forward',
812
      'glyphicon-stop' => 'stop',
813
      'glyphicon-subtitles' => 'subtitles',
814
      'glyphicon-tag' => 'tag',
815
      'glyphicon-tags' => 'tags',
816
      'glyphicon-tasks' => 'tasks',
817
      'glyphicon-text-height' => 'text-height',
818
      'glyphicon-text-width' => 'text-width',
819
      'glyphicon-th' => 'th',
820
      'glyphicon-th-large' => 'th-large',
821
      'glyphicon-th-list' => 'th-list',
822
      'glyphicon-thumbs-down' => 'thumbs-down',
823
      'glyphicon-thumbs-up' => 'thumbs-up',
824
      'glyphicon-time' => 'time',
825
      'glyphicon-tint' => 'tint',
826
      'glyphicon-tower' => 'tower',
827
      'glyphicon-transfer' => 'transfer',
828
      'glyphicon-trash' => 'trash',
829
      'glyphicon-tree-conifer' => 'tree-conifer',
830
      'glyphicon-tree-deciduous' => 'tree-deciduous',
831
      'glyphicon-unchecked' => 'unchecked',
832
      'glyphicon-upload' => 'upload',
833
      'glyphicon-usd' => 'usd',
834
      'glyphicon-user' => 'user',
835
      'glyphicon-volume-down' => 'volume-down',
836
      'glyphicon-volume-off' => 'volume-off',
837
      'glyphicon-volume-up' => 'volume-up',
838
      'glyphicon-warning-sign' => 'warning-sign',
839
      'glyphicon-wrench' => 'wrench',
840
      'glyphicon-zoom-in' => 'zoom-in',
841
      'glyphicon-zoom-out' => 'zoom-out',
842
    );
843
    $versions['3.0.1'] = $versions['3.0.0'];
844
    $versions['3.0.2'] = $versions['3.0.1'];
845
    $versions['3.0.3'] = $versions['3.0.2'];
846
    $versions['3.1.0'] = $versions['3.0.3'];
847
    $versions['3.1.1'] = $versions['3.1.0'];
848
    $versions['3.2.0'] = $versions['3.1.1'];
849
    $versions['3.3.0'] = array_merge($versions['3.2.0'], array(
850
      'glyphicon-eur' => 'eur',
851
    ));
852
    $versions['3.3.1'] = $versions['3.3.0'];
853
    $versions['3.3.2'] = array_merge($versions['3.3.1'], array(
854
      'glyphicon-alert' => 'alert',
855
      'glyphicon-apple' => 'apple',
856
      'glyphicon-baby-formula' => 'baby-formula',
857
      'glyphicon-bed' => 'bed',
858
      'glyphicon-bishop' => 'bishop',
859
      'glyphicon-bitcoin' => 'bitcoin',
860
      'glyphicon-blackboard' => 'blackboard',
861
      'glyphicon-cd' => 'cd',
862
      'glyphicon-console' => 'console',
863
      'glyphicon-copy' => 'copy',
864
      'glyphicon-duplicate' => 'duplicate',
865
      'glyphicon-education' => 'education',
866
      'glyphicon-equalizer' => 'equalizer',
867
      'glyphicon-erase' => 'erase',
868
      'glyphicon-grain' => 'grain',
869
      'glyphicon-hourglass' => 'hourglass',
870
      'glyphicon-ice-lolly' => 'ice-lolly',
871
      'glyphicon-ice-lolly-tasted' => 'ice-lolly-tasted',
872
      'glyphicon-king' => 'king',
873
      'glyphicon-knight' => 'knight',
874
      'glyphicon-lamp' => 'lamp',
875
      'glyphicon-level-up' => 'level-up',
876
      'glyphicon-menu-down' => 'menu-down',
877
      'glyphicon-menu-hamburger' => 'menu-hamburger',
878
      'glyphicon-menu-left' => 'menu-left',
879
      'glyphicon-menu-right' => 'menu-right',
880
      'glyphicon-menu-up' => 'menu-up',
881
      'glyphicon-modal-window' => 'modal-window',
882
      'glyphicon-object-align-bottom' => 'object-align-bottom',
883
      'glyphicon-object-align-horizontal' => 'object-align-horizontal',
884
      'glyphicon-object-align-left' => 'object-align-left',
885
      'glyphicon-object-align-right' => 'object-align-right',
886
      'glyphicon-object-align-top' => 'object-align-top',
887
      'glyphicon-object-align-vertical' => 'object-align-vertical',
888
      'glyphicon-oil' => 'oil',
889
      'glyphicon-open-file' => 'open-file',
890
      'glyphicon-option-horizontal' => 'option-horizontal',
891
      'glyphicon-option-vertical' => 'option-vertical',
892
      'glyphicon-paste' => 'paste',
893
      'glyphicon-pawn' => 'pawn',
894
      'glyphicon-piggy-bank' => 'piggy-bank',
895
      'glyphicon-queen' => 'queen',
896
      'glyphicon-ruble' => 'ruble',
897
      'glyphicon-save-file' => 'save-file',
898
      'glyphicon-scale' => 'scale',
899
      'glyphicon-scissors' => 'scissors',
900
      'glyphicon-subscript' => 'subscript',
901
      'glyphicon-sunglasses' => 'sunglasses',
902
      'glyphicon-superscript' => 'superscript',
903
      'glyphicon-tent' => 'tent',
904
      'glyphicon-text-background' => 'text-background',
905
      'glyphicon-text-color' => 'text-color',
906
      'glyphicon-text-size' => 'text-size',
907
      'glyphicon-triangle-bottom' => 'triangle-bottom',
908
      'glyphicon-triangle-left' => 'triangle-left',
909
      'glyphicon-triangle-right' => 'triangle-right',
910
      'glyphicon-triangle-top' => 'triangle-top',
911
      'glyphicon-yen' => 'yen',
912
    ));
913
    $versions['3.3.4'] = array_merge($versions['3.3.2'], array(
914
      'glyphicon-btc' => 'btc',
915
      'glyphicon-jpy' => 'jpy',
916
      'glyphicon-rub' => 'rub',
917
      'glyphicon-xbt' => 'xbt',
918
    ));
919
    $versions['3.3.5'] = $versions['3.3.4'];
920
    $versions['3.3.6'] = $versions['3.3.5'];
921
    $versions['3.3.7'] = $versions['3.3.6'];
922
  }
923

    
924
  // Return a specific versions icon set.
925
  if (isset($version) && isset($versions[$version])) {
926
    return $versions[$version];
927
  }
928

    
929
  // Return the latest version.
930
  return $versions[BOOTSTRAP_VERSION];
931
}
932

    
933
/**
934
 * Returns a specific Bootstrap Glyphicon.
935
 *
936
 * @param string $name
937
 *   The icon name, minus the "glyphicon-" prefix.
938
 * @param string $default
939
 *   (Optional) The default value to return.
940
 *
941
 * @return string
942
 *   The HTML markup containing the icon defined by $name, $default value if
943
 *   icon does not exist or returns empty output for whatever reason.
944
 */
945
function _bootstrap_icon($name, $default = NULL) {
946
  $output = NULL;
947
  // Ensure the icon specified is a valid Bootstrap Glyphicon.
948
  // @todo Supply a specific version to _bootstrap_glyphicons() when Icon API
949
  // supports versioning.
950
  if (_bootstrap_glyphicons_supported() && in_array($name, _bootstrap_glyphicons())) {
951
    // Attempt to use the Icon API module, if enabled and it generates output.
952
    if (module_exists('icon')) {
953
      $output = theme('icon', array('bundle' => 'bootstrap', 'icon' => 'glyphicon-' . $name));
954
    }
955
    if (empty($output)) {
956
      // Mimic the Icon API markup.
957
      $attributes = array(
958
        'class' => array('icon', 'glyphicon', 'glyphicon-' . $name),
959
        'aria-hidden' => 'true',
960
      );
961
      $output = '<span' . drupal_attributes($attributes) . '></span>';
962
    }
963
  }
964
  return empty($output) && isset($default) ? $default : $output;
965
}
966

    
967
/**
968
 * Determine whether or not Bootstrap Glyphicons can be used.
969
 */
970
function _bootstrap_glyphicons_supported() {
971
  // Use the advanced drupal_static() pattern, since this has the potential to
972
  // be called very often by _bootstrap_icon().
973
  static $drupal_static_fast;
974
  if (!isset($drupal_static_fast)) {
975
    $drupal_static_fast['supported'] = &drupal_static(__FUNCTION__);
976
    // Get the active theme.
977
    $drupal_static_fast['theme'] = variable_get('theme_default', $GLOBALS['theme']);
978
  }
979

    
980
  // Get static data.
981
  $supported = &$drupal_static_fast['supported'];
982
  $theme = &$drupal_static_fast['theme'];
983

    
984
  // Retrieve supported themes.
985
  if (!isset($supported)) {
986
    $supported = array();
987

    
988
    // Retrieve cached data.
989
    $cid = 'theme_registry:bootstrap:icon_support';
990
    if (($cache = cache_get($cid)) && !empty($cache->data)) {
991
      $supported = $cache->data;
992
    }
993

    
994
    if (!isset($supported[$theme])) {
995
      // Bootstrap based themes are enabled by default to use CDN. Check if
996
      // that is the case here so no file discovery is necessary. If the active
997
      // theme does not have this setting, it falls back to the base theme that
998
      // does.
999
      $supported[$theme] = !!bootstrap_get_cdn_assets('css', NULL, $theme);
1000

    
1001
      // CDN not used, iterate over all of the active (base) themes to determine
1002
      // if they contain glyphicon font files.
1003
      if (!$supported[$theme]) {
1004
        foreach (_bootstrap_get_base_themes($theme, TRUE) as $_theme) {
1005
          // Scan the theme for files.
1006
          $fonts = _bootstrap_file_scan_directory(drupal_get_path('theme', $_theme), '/glyphicons-halflings-regular\.(eot|svg|ttf|woff)$/');
1007

    
1008
          // Fonts found, stop the search.
1009
          if (!empty($fonts)) {
1010
            $supported[$theme] = TRUE;
1011
            break;
1012
          }
1013
        }
1014
      }
1015

    
1016
      // Cache all supported themes now that this theme is added to the array.
1017
      cache_set($cid, $supported);
1018
    }
1019
  }
1020
  return $supported[$theme];
1021
}
1022

    
1023
/**
1024
 * Determine whether a specific element is a button.
1025
 *
1026
 * @param array $element
1027
 *   A renderable element.
1028
 *
1029
 * @return bool
1030
 *   TRUE or FALSE.
1031
 */
1032
function _bootstrap_is_button($element) {
1033
  return
1034
    !empty($element['#type']) &&
1035
    !empty($element['#value']) && (
1036
      $element['#type'] === 'button' ||
1037
      $element['#type'] === 'submit' ||
1038
      $element['#type'] === 'image_button'
1039
    );
1040
}
1041

    
1042
/**
1043
 * Adds a specific Bootstrap class to color a button based on its text value.
1044
 *
1045
 * @param array $element
1046
 *   The form element, passed by reference.
1047
 */
1048
function _bootstrap_colorize_button(&$element) {
1049
  if (_bootstrap_is_button($element)) {
1050
    // Do not add the class if one is already present in the array.
1051
    $button_classes = array(
1052
      'btn-default',
1053
      'btn-primary',
1054
      'btn-success',
1055
      'btn-info',
1056
      'btn-warning',
1057
      'btn-danger',
1058
      'btn-link',
1059
    );
1060
    $class_intersection = array_intersect($button_classes, $element['#attributes']['class']);
1061
    if (empty($class_intersection)) {
1062
      // Get the matched class.
1063
      $class = bootstrap_setting('button_colorize') ? _bootstrap_colorize_text($element['#value']) : FALSE;
1064
      // If no particular class matched, use the default style.
1065
      if (!$class) {
1066
        $class = 'default';
1067
      }
1068
      $element['#attributes']['class'][] = 'btn-' . $class;
1069
    }
1070
  }
1071
}
1072

    
1073
/**
1074
 * Matches a Bootstrap class based on a string value.
1075
 *
1076
 * @param string $string
1077
 *   The string to match classes against.
1078
 * @param string $default
1079
 *   The default class to return if no match is found.
1080
 *
1081
 * @return string
1082
 *   The Bootstrap class matched against the value of $haystack or $default if
1083
 *   no match could be made.
1084
 */
1085
function _bootstrap_colorize_text($string, $default = '') {
1086
  static $texts;
1087
  if (!isset($texts)) {
1088
    $texts = array(
1089
      // Text that match these specific strings are checked first.
1090
      'matches' => array(
1091
        // Primary class.
1092
        t('Download feature')   => 'primary',
1093

    
1094
        // Success class.
1095
        t('Add effect')         => 'success',
1096
        t('Add and configure')  => 'success',
1097

    
1098
        // Info class.
1099
        t('Save and add')       => 'info',
1100
        t('Add another item')   => 'info',
1101
        t('Update style')       => 'info',
1102
      ),
1103

    
1104
      // Text that contain these words anywhere in the string are checked last.
1105
      'contains' => array(
1106
        // Primary class.
1107
        t('Confirm')            => 'primary',
1108
        t('Filter')             => 'primary',
1109
        t('Log in')             => 'primary',
1110
        t('Submit')             => 'primary',
1111
        t('Search')             => 'primary',
1112
        t('Upload')             => 'primary',
1113

    
1114
        // Success class.
1115
        t('Add')                => 'success',
1116
        t('Create')             => 'success',
1117
        t('Save')               => 'success',
1118
        t('Write')              => 'success',
1119

    
1120
        // Warning class.
1121
        t('Export')             => 'warning',
1122
        t('Import')             => 'warning',
1123
        t('Restore')            => 'warning',
1124
        t('Rebuild')            => 'warning',
1125

    
1126
        // Info class.
1127
        t('Apply')              => 'info',
1128
        t('Update')             => 'info',
1129

    
1130
        // Danger class.
1131
        t('Delete')             => 'danger',
1132
        t('Remove')             => 'danger',
1133
      ),
1134
    );
1135

    
1136
    // Allow sub-themes to alter this array of patterns.
1137
    drupal_alter('bootstrap_colorize_text', $texts);
1138
  }
1139

    
1140
  // Iterate over the array.
1141
  foreach ($texts as $pattern => $strings) {
1142
    foreach ($strings as $value => $class) {
1143
      switch ($pattern) {
1144
        case 'matches':
1145
          if ($string === $value) {
1146
            return $class;
1147
          }
1148
          break;
1149

    
1150
        case 'contains':
1151
          if (strpos(drupal_strtolower($string), drupal_strtolower($value)) !== FALSE) {
1152
            return $class;
1153
          }
1154
          break;
1155
      }
1156
    }
1157
  }
1158

    
1159
  // Return the default if nothing was matched.
1160
  return $default;
1161
}
1162

    
1163
/**
1164
 * Adds an icon to button element based on its text value.
1165
 *
1166
 * @param array $element
1167
 *   The form element, passed by reference.
1168
 */
1169
function _bootstrap_iconize_button(&$element) {
1170
  if (bootstrap_setting('button_iconize') && _bootstrap_is_button($element) && ($icon = _bootstrap_iconize_text($element['#value']))) {
1171
    $element['#icon'] = $icon;
1172
  }
1173
}
1174

    
1175
/**
1176
 * Matches a Bootstrap Glyphicon based on a string value.
1177
 *
1178
 * @param string $string
1179
 *   The string to match classes against.
1180
 * @param string $default
1181
 *   The default icon to return if no match is found.
1182
 *
1183
 * @return string
1184
 *   The Bootstrap icon matched against the value of $haystack or $default if
1185
 *   no match could be made.
1186
 */
1187
function _bootstrap_iconize_text($string, $default = '') {
1188
  static $texts;
1189
  if (!isset($texts)) {
1190
    $texts = array(
1191
      // Text that match these specific strings are checked first.
1192
      'matches' => array(),
1193

    
1194
      // Text that contain these words anywhere in the string are checked last.
1195
      'contains' => array(
1196
        t('Manage')     => 'cog',
1197
        t('Configure')  => 'cog',
1198
        t('Download')   => 'download',
1199
        t('Export')     => 'export',
1200
        t('Filter')     => 'filter',
1201
        t('Import')     => 'import',
1202
        t('Save')       => 'ok',
1203
        t('Update')     => 'ok',
1204
        t('Edit')       => 'pencil',
1205
        t('Add')        => 'plus',
1206
        t('Write')      => 'plus',
1207
        t('Cancel')     => 'remove',
1208
        t('Delete')     => 'trash',
1209
        t('Remove')     => 'trash',
1210
        t('Upload')     => 'upload',
1211
        t('Log In')     => 'log-in',
1212
      ),
1213
    );
1214

    
1215
    // Allow sub-themes to alter this array of patterns.
1216
    drupal_alter('bootstrap_iconize_text', $texts);
1217
  }
1218

    
1219
  // Iterate over the array.
1220
  foreach ($texts as $pattern => $strings) {
1221
    foreach ($strings as $value => $icon) {
1222
      switch ($pattern) {
1223
        case 'matches':
1224
          if ($string === $value) {
1225
            return _bootstrap_icon($icon, $default);
1226
          }
1227
          break;
1228

    
1229
        case 'contains':
1230
          if (strpos(drupal_strtolower($string), drupal_strtolower($value)) !== FALSE) {
1231
            return _bootstrap_icon($icon, $default);
1232
          }
1233
          break;
1234
      }
1235
    }
1236
  }
1237

    
1238
  // Return a default icon if nothing was matched.
1239
  return _bootstrap_icon($default);
1240
}
1241

    
1242
/**
1243
 * Invokes a specific suggestion's preprocess functions.
1244
 *
1245
 * @param array $variables
1246
 *   The theme implementation variables array.
1247
 */
1248
function _bootstrap_preprocess_theme_suggestion(&$variables) {
1249
  $registry = theme_get_registry();
1250
  if (!empty($variables['theme_hook_suggestion']) && !empty($registry[$variables['theme_hook_suggestion']]['preprocess functions'])) {
1251
    // Save the suggestion as the hook to pass to the function.
1252
    $hook = $variables['theme_hook_suggestion'];
1253

    
1254
    // Iterate over the preprocess functions.
1255
    foreach ($registry[$hook]['preprocess functions'] as $function) {
1256
      // Ensure that the function is not this one (recursive) and exists.
1257
      if ($function !== __FUNCTION__ && function_exists($function)) {
1258
        // Invoke theme hook suggestion preprocess function.
1259
        $function($variables, $hook);
1260

    
1261
        // Unset the theme_hook_suggestion so the suggestion's preprocess
1262
        // functions can provide theme_hook_suggestions if needed.
1263
        if (!empty($variables['theme_hook_suggestions'])) {
1264
          unset($variables['theme_hook_suggestion']);
1265
        }
1266
      }
1267
    }
1268
  }
1269
}
1270

    
1271
/**
1272
 * Invokes a specific suggestion's process functions.
1273
 *
1274
 * @param array $variables
1275
 *   The theme implementation variables array.
1276
 */
1277
function _bootstrap_process_theme_suggestion(&$variables) {
1278
  $registry = theme_get_registry();
1279
  if (!empty($variables['theme_hook_suggestion']) && !empty($registry[$variables['theme_hook_suggestion']]['process functions'])) {
1280
    // Save the suggestion as the hook to pass to the function.
1281
    $hook = $variables['theme_hook_suggestion'];
1282

    
1283
    // Iterate over the process functions.
1284
    foreach ($registry[$hook]['process functions'] as $function) {
1285
      if (function_exists($function)) {
1286
        // Invoke theme hook suggestion process function.
1287
        $function($variables, $hook);
1288

    
1289
        // Unset the theme_hook_suggestion so the suggestion's preprocess
1290
        // functions can provide theme_hook_suggestions if needed.
1291
        if (!empty($variables['theme_hook_suggestions'])) {
1292
          unset($variables['theme_hook_suggestion']);
1293
        }
1294
      }
1295
    }
1296
  }
1297
}
1298

    
1299
/**
1300
 * Determines if a string of text is considered "simple".
1301
 *
1302
 * @param string $string
1303
 *   The string of text to check "simple" criteria on.
1304
 * @param int|FALSE $length
1305
 *   The length of characters used to determine whether or not $string is
1306
 *   considered "simple". Set explicitly to FALSE to disable this criteria.
1307
 * @param array|FALSE $allowed_tags
1308
 *   An array of allowed tag elements. Set explicitly to FALSE to disable this
1309
 *   criteria.
1310
 * @param bool $html
1311
 *   A variable, passed by reference, that indicates whether or not the
1312
 *   string contains HTML.
1313
 *
1314
 * @return bool
1315
 *   Returns TRUE if the $string is considered "simple", FALSE otherwise.
1316
 */
1317
function _bootstrap_is_simple_string($string, $length = 250, $allowed_tags = NULL, &$html = FALSE) {
1318
  // Use the advanced drupal_static() pattern, since this is called very often.
1319
  static $drupal_static_fast;
1320
  if (!isset($drupal_static_fast)) {
1321
    $drupal_static_fast['strings'] = &drupal_static(__FUNCTION__);
1322
  }
1323
  $strings = &$drupal_static_fast['strings'];
1324
  if (!isset($strings[$string])) {
1325
    $plain_string = strip_tags($string);
1326
    $simple = TRUE;
1327
    if ($allowed_tags !== FALSE) {
1328
      $filtered_string = filter_xss($string, $allowed_tags);
1329
      $html = $filtered_string !== $plain_string;
1330
      $simple = $simple && $string === $filtered_string;
1331
    }
1332
    if ($length !== FALSE) {
1333
      $simple = $simple && strlen($plain_string) <= intval($length);
1334
    }
1335
    $strings[$string] = $simple;
1336
  }
1337
  return $strings[$string];
1338
}
1339

    
1340
/**
1341
 * Determines if the Path Breadcrumbs module theme function should be used.
1342
 *
1343
 * @param string $theme
1344
 *   The machine name of a specific theme to determine status if the Path
1345
 *   Breadcrumbs module has been configured to only use its internal function
1346
 *   on a specific list of themes.
1347
 *
1348
 * @return bool
1349
 *   TRUE or FALSE
1350
 */
1351
function _bootstrap_use_path_breadcrumbs($theme = NULL) {
1352
  static $path_breadcrumbs;
1353

    
1354
  if (!isset($path_breadcrumbs)) {
1355
    $path_breadcrumbs = FALSE;
1356

    
1357
    // Use active theme as the theme key if not explicitly set.
1358
    if (!isset($theme)) {
1359
      $theme = $GLOBALS['theme_key'];
1360
    }
1361

    
1362
    // Determine whether or not the internal Path Breadcrumbs theme function
1363
    // should be used or not.
1364
    if (function_exists('path_breadcrumbs_breadcrumb') && module_exists('path_breadcrumbs')) {
1365
      $internal_render = variable_get('path_breadcrumbs_internal_render', 1);
1366
      $themes = variable_get('path_breadcrumbs_internal_render_themes', array());
1367
      $path_breadcrumbs = ($internal_render && (empty($themes) || in_array($theme, $themes)));
1368
    }
1369
  }
1370

    
1371
  return $path_breadcrumbs;
1372
}
1373

    
1374
/**
1375
 * @} End of "defgroup subtheme_helper_functions".
1376
 */