You are here

availability_calendar.styles.inc in Availability Calendars 7.4

File

availability_calendar.styles.inc
View source
<?php

/**
 * Availability Calendar: styles settings form code
 * - define form
 * - validate
 * - submit
 * - generate CSS
 *
 * This file uses many helper methods to ease the task of adding new style
 * settings, or enhancing a current style type (length, color, select field),
 * and remaining a standardized look & feel on the admin styles screen.
 *
 * Adding a property:
 * - Add the property to the correct fieldset in
 *   class AvailabilityCalendarStylesFormBuilder.
 * - If necessary, add validation of the property to
 *   class AvailabilityCalendarStylesFormValidator.
 * - Add the property to the CSS generator, i.e.
 *   class AvailabilityCalendarCssGenerator.
 * - Optionally, add a sensible default to the function
 *   availability_calendar_install() (in file availability_calendar.install).
 *   An update should normally not be necessary.
 * - If necessary, remove the property from the base css file
 *   availability_calendar.css in the module directory.
 * - Test
 * - Create an issue on the issue queue including a patch for the above changes.
 */
module_load_include('inc', 'availability_calendar', 'availability_calendar');

/**
 * Define form callback for the admin/config/availability-calendar/styles page.
 *
 * @return array
 *   The form.
 */
function availability_calendar_styles() {
  drupal_add_css(drupal_get_path('module', 'availability_calendar') . '/availability_calendar.admin.css');
  $form_builder = new AvailabilityCalendarStylesFormBuilder(availability_calendar_get_styles());
  $form = $form_builder
    ->exec();
  $form['#validate'][] = 'availability_calendar_styles_validate';
  $form = system_settings_form($form);

  // We want our #submit handler to run after the system submit handler, so the generator can simply use the variable API.
  $form['#submit'][] = 'availability_calendar_styles_generate';
  return $form;
}

/**
 * Callback to validate the form for the style settings.
 *
 * @param array $form
 * @param array $form_state
 */
function availability_calendar_styles_validate($form, &$form_state) {
  $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
  if ($op == t('Save configuration')) {
    $css_validator = new AvailabilityCalendarStylesFormValidator($form_state['values']);
    $css_validator
      ->exec();
  }
}

/**
 * Callback to process form submission for the styles form.
 */
function availability_calendar_styles_generate() {
  if (variable_get('availability_calendar_styles_generate', 1)) {
    $css_generator = new AvailabilityCalendarCssGenerator(availability_calendar_get_styles());
    $result = $css_generator
      ->exec();
    if ($result) {
      drupal_set_message(t('A custom CSS file for availability calendar fields has been successfully generated.'), 'status', FALSE);
    }
    else {
      drupal_set_message(t('An error occurred while saving the generated CSS file for availability calendar fields.'), 'error', FALSE);
    }
  }
}

/**
 * Returns the CSS styles defined for the calendars.
 *
 * @return array
 *   List of defined styles.
 */
function availability_calendar_get_styles() {
  $styles = variable_get('availability_calendar_styles', array());
  $styles['generate'] = variable_get('availability_calendar_styles_generate', 1);
  return $styles;
}
class AvailabilityCalendarStylesFormBuilder {

  /** @var array */
  protected $form = array();

  /** @var string */
  protected $currentFieldset = '';

  /** @var array */
  protected $styles = NULL;
  public function __construct($styles) {
    $this->styles = $styles;
  }
  public function exec() {
    $this
      ->fieldsetTable();
    $this
      ->fieldsetCaption();
    $this
      ->fieldsetHeader();
    $this
      ->fieldsetWeekNotes();
    $this
      ->fieldsetDays();
    $this
      ->fieldsetStates();

    // Place all fieldsets within another fieldset and add the generate checkbox.
    $this->form = array(
      'availability_calendar_styles_generate' => array(
        '#type' => 'checkbox',
        '#title' => t('Generate the css file.'),
        '#default_value' => $this->styles['generate'],
        '#description' => t("Check whether you want to generate a CSS file based on the styles on this page. If you don't check this, the CSS file won't be generated nor included. As a consequence, you will need to define all styling in your theme."),
      ),
      'availability_calendar_styles' => array(
        '#type' => 'fieldset',
        '#title' => t('CSS Styles'),
        '#tree' => TRUE,
        '#description' => t("Below you can fill in a number of basic styles that define how your calendar will be displayed. If a style is left empty (or @n for selects) that style won't be generated.", array(
          '@n' => '<none>',
        )),
      ) + $this->form,
    );
    return $this->form;
  }

  /**
   * Helper method to return the fieldset for the table styles.
   *
   * @return array
   *   An array with a number of form elements in a fieldset.
   */
  protected function fieldsetTable() {
    $this->currentFieldset = 'table';
    $this->form[$this->currentFieldset] = array(
      '#type' => 'fieldset',
      '#title' => t('Table'),
      '#description' => t('Styles that define how the table will be displayed.'),
    );
    $this
      ->selectField('font-size', array(
      'larger',
      'smaller',
      'xx-small',
      'x-small',
      'small',
      'medium',
      'large',
      'x-large',
      'xx-large',
    ));
    $this
      ->colorField('color');
    $this
      ->colorField('background-color');
    $this
      ->lengthField('border-width');
    $this
      ->colorField('border-color');
  }

  /**
   * Helper method to return the fieldset for the caption styles.
   *
   * @return array
   *   An array with a number of form elements in a fieldset.
   */
  protected function fieldsetCaption() {
    $this->currentFieldset = 'caption';
    $this->form[$this->currentFieldset] = array(
      '#type' => 'fieldset',
      '#title' => t('Caption'),
      '#description' => t('Styles that define how the name of the month will be displayed.'),
    );
    $this
      ->selectField('text-align', array(
      'left',
      'right',
      'center',
      'inherit',
    ));
    $this
      ->selectField('font-weight', array(
      'normal',
      'bold',
      'bolder',
      'lighter',
      'inherit',
    ));
    $this
      ->selectField('font-style', array(
      'normal',
      'italic',
      'oblique',
      'inherit',
    ));
    $this
      ->selectField('font-size', array(
      'larger',
      'smaller',
      'xx-small',
      'x-small',
      'small',
      'medium',
      'large',
      'x-large',
      'xx-large',
      'inherit',
    ));
  }

  /**
   * Helper method to return the fieldset for the table header styles.
   *
   * @return array
   *   An array with a number of form elements in a fieldset.
   */
  protected function fieldsetHeader() {
    $this->currentFieldset = 'header';
    $this->form[$this->currentFieldset] = array(
      '#type' => 'fieldset',
      '#title' => t('Day names'),
      '#description' => t('Styles that define how the names of the weekdays will be displayed.'),
    );
    $this
      ->lengthField('height');
    $this
      ->selectField('text-align', array(
      'left',
      'right',
      'center',
      'inherit',
    ));
    $this
      ->selectField('font-weight', array(
      'normal',
      'bold',
      'bolder',
      'lighter',
      'inherit',
    ));
    $this
      ->selectField('font-style', array(
      'normal',
      'italic',
      'oblique',
      'inherit',
    ));
    $this
      ->selectField('font-size', array(
      'larger',
      'smaller',
      'xx-small',
      'x-small',
      'small',
      'medium',
      'large',
      'x-large',
      'xx-large',
      'inherit',
    ));
  }

  /**
   * Helper method to return the fieldset for the week note styles.
   *
   * @return array
   *   An array with a number of form elements in a fieldset.
   */
  protected function fieldsetWeekNotes() {
    $this->currentFieldset = 'week_notes';
    $this->form[$this->currentFieldset] = array(
      '#type' => 'fieldset',
      '#title' => t('Week notes'),
      '#description' => t('Styles that define how a week note cell will be displayed.'),
    );
    $this
      ->lengthField('width');
  }

  /**
   * Helper method to return the fieldset for the day styles.
   *
   * @return array
   *   An array with a number of form elements in a fieldset.
   */
  protected function fieldsetDays() {
    $this->currentFieldset = 'days';
    $this->form[$this->currentFieldset] = array(
      '#type' => 'fieldset',
      '#title' => t('Days'),
      '#description' => t('Styles that define how a day cell will be displayed.'),
    );
    $this
      ->lengthField('width');
    $this
      ->lengthField('height');
    $this
      ->selectField('text-align', array(
      'left',
      'right',
      'center',
      'inherit',
    ));
    $this
      ->selectField('vertical-align', array(
      'baseline',
      'sub',
      'super',
      'top',
      'text-top',
      'middle',
      'bottom',
      'text-bottom',
    ));
  }

  /**
   * Helper method to return the fieldset for the states styles.
   *
   * @return array
   *   An array with a number of form elements in a fieldset
   */
  protected function fieldsetStates() {
    $this->currentFieldset = 'states';
    $this->form[$this->currentFieldset] = array(
      '#type' => 'fieldset',
      '#title' => t('States'),
      '#description' => t('Styles that define how the states will be displayed.'),
    );
    $this
      ->selectField('split-day', array(
      '/',
      '\\',
      '|',
      '―',
    ));

    // Translate the title 'Split day' but only after creating the form element
    // (otherwise the default value can't be retrieved from the styles array).
    $this->form[$this->currentFieldset]['split-day']['#title'] = t('How to render a split day');
    $states = availability_calendar_get_states();
    foreach ($states as $sid => $state) {
      $this
        ->colorField(array(
        $state['css_class'] => $state['label'],
      ));
    }
  }

  /**
   * Helper method to add a length field to a given fieldset.
   *
   * By extracting this, we get standardized settings for length fields. E.g:
   * for now only pixels are allowed, but this function could add a dropdown for
   * unit selection.
   *
   * @param string|array $cssProperty
   *   The name of the css color property to add. If this is not the same as the
   *   form field name, pass in an array with 1 element:
   *   <field name> => <css property>.
   */
  protected function lengthField($cssProperty) {
    if (is_array($cssProperty)) {
      list($fieldName, $cssProperty) = each($cssProperty);
    }
    else {
      $fieldName = $cssProperty;
    }
    $this->form[$this->currentFieldset][$fieldName] = array(
      '#type' => 'textfield',
      '#title' => $cssProperty,
      '#default_value' => $this
        ->getStyle($fieldName),
      '#size' => 8,
      '#maxlength' => 6,
      '#field_suffix' => 'px',
    );
  }

  /**
   * Helper method to add a color field to a given fieldset.
   *
   * By extracting this, we get standardized settings and handling for color
   * fields. E.g: for now only color codes are allowed, but this function could
   * add support for other notations
   * (http://www.w3.org/TR/CSS21/syndata.html#color-units) or a color picker.
   *
   * @param string|array $cssProperty
   *   The name of the css color property to add. If this is not the same as the
   *   form field name, pass in an array with 1 element:
   *   <field name> => <css property>.
   */
  protected function colorField($cssProperty) {
    if (is_array($cssProperty)) {
      list($fieldName, $cssProperty) = each($cssProperty);
    }
    else {
      $fieldName = $cssProperty;
    }
    $this->form[$this->currentFieldset][$fieldName] = array(
      '#type' => 'textfield',
      '#title' => $cssProperty,
      '#default_value' => $this
        ->getStyle($fieldName),
      '#size' => 8,
      '#maxlength' => 7,
      '#field_prefix' => '#',
    );
  }

  /**
   * Helper method to add a select field to a given fieldset.
   *
   * @param string|array $cssProperty
   *   The name of the css color property to add. If this is not the same as the
   *   form field name, pass in an array with 1 element:
   *   <field name> => <css property>.
   * @param array $options
   *   The options to present. An option <none> will be added in front of
   *   this list, to allow to select for not setting and thus not generating
   *   this property.
   */
  protected function selectField($cssProperty, $options) {
    if (is_array($cssProperty)) {
      list($fieldName, $cssProperty) = each($cssProperty);
    }
    else {
      $fieldName = $cssProperty;
    }

    // We only translate the label, not the key value that will be stored.
    array_unshift($options, '<none>');
    $options = array_combine($options, $options);
    $options['<none>'] = t('<none>');
    $this->form[$this->currentFieldset][$fieldName] = array(
      '#type' => 'select',
      '#title' => $cssProperty,
      '#default_value' => $this
        ->getStyle($fieldName),
      '#options' => $options,
    );
  }

  /**
   * Helper method to return 1 style setting.
   *
   * This prevents repeating taking care of values not being set, defaults, etc.
   *
   * @param string $name
   *   The style setting to retrieve.
   *
   * @return string
   *   The style value or the empty string if not set.
   */
  protected function getStyle($name) {
    $category = $this->currentFieldset;
    return isset($this->styles[$category][$name]) ? $this->styles[$category][$name] : '';
  }

}
class AvailabilityCalendarStylesFormValidator {

  /** @var array */
  protected $styles = NULL;

  /** @var string */
  protected $currentFieldset = '';
  public function __construct($styles) {
    $this->styles = $styles;
  }

  /**
   * Validates the styles settings.
   * Form errors are set on error.
   */
  public function exec() {
    $this
      ->fieldsetTable();
    $this
      ->fieldsetCaption();
    $this
      ->fieldsetHeader();
    $this
      ->fieldsetWeekNotes();
    $this
      ->fieldsetDays();
    $this
      ->fieldsetStates();
  }
  protected function fieldsetTable() {
    $this->currentFieldset = 'table';
    $this
      ->colorField('color');
    $this
      ->colorField('background-color');
    $this
      ->lengthField('border-width');
    $this
      ->colorField('border-color');
  }
  protected function fieldsetCaption() {
    $this->currentFieldset = 'caption';
  }
  protected function fieldsetHeader() {
    $this->currentFieldset = 'header';
    $this
      ->lengthField('height');
  }
  protected function fieldsetWeekNotes() {
    $this->currentFieldset = 'week_notes';
    $this
      ->lengthField('width');
  }
  protected function fieldsetDays() {
    $this->currentFieldset = 'days';
    $this
      ->lengthField('width');
    $this
      ->lengthField('height');
  }
  protected function fieldsetStates() {
    $this->currentFieldset = 'states';
    if ($this
      ->getStyle('split-day') != '') {

      // We need cell width and height for split days.
      if ($this
        ->getStyle('width', 'days') != '') {
        form_set_error('days][width', t('The day cell width is needed to generate styles for split days.'));
      }
      if ($this
        ->getStyle('height', 'days') != '') {
        form_set_error('days][height', t('The day cell height is needed to generate styles for split days.'));
      }
    }
    $states = availability_calendar_get_states();
    foreach ($states as $sid => $state) {
      $this
        ->colorField($state['css_class']);
    }
  }
  protected function lengthField($name) {
    $value = $this
      ->getStyle($name);
    if (!empty($value) && !$this
      ->validateLengthValue($value)) {
      form_set_error($this->currentFieldset . '][' . $name, t('Not a valid width or height specification.'));
    }
  }
  protected function colorField($name) {
    $value = $this
      ->getStyle($name);
    if (!empty($value) && !$this
      ->validateColorValue($value)) {
      form_set_error($this->currentFieldset . '][' . $name, t('Not a valid color code.'));
    }
  }

  /**
   * Check for a CSS length declaration:
   *
   * We check for a part of http://www.w3.org/TR/CSS21/syndata.html#numbers
   * and http://www.w3.org/TR/CSS21/syndata.html#length-units:
   * - Only non-signed pixel values are allowed.
   *
   * @param string $value
   *
   * @return boolean
   */
  protected function validateLengthValue($value) {
    $pattern = '/^(\\d*\\.)?\\d+(px)?$/';
    return preg_match($pattern, $value) === 1;
  }

  /**
   * Check for a CSS color declaration:
   *
   * We check for a part of http://www.w3.org/TR/CSS21/syndata.html#color-units
   * - Only hex code formats are allowed.
   *
   * @param string $value
   *
   * @return boolean
   */
  protected function validateColorValue($value) {
    $pattern = '/^#?[0-9a-fA-F]{3}[0-9a-fA-F]{3}?$/';
    return preg_match($pattern, $value) === 1;
  }

  /**
   * Helper method to return 1 style setting.
   *
   * This prevents repeating taking care of values not being set, defaults, etc.
   *
   * @param string $name
   *   The style setting to retrieve.
   * @param string|null $category
   *   The name of the category, null for the current fieldset.
   *
   * @return string
   *   The style value or the empty string if not set.
   */
  protected function getStyle($name, $category = NULL) {
    if ($category === NULL) {
      $category = $this->currentFieldset;
    }
    return isset($this->styles[$category][$name]) ? $this->styles[$category][$name] : '';
  }

}
class AvailabilityCalendarCssGenerator {

  /** @var array */
  protected $styles = NULL;
  public function __construct($styles) {
    $this->styles = $styles;
  }

  /**
   * Creates and writes the CSS file for availability calendar fields.
   *
   * @return boolean
   *   Whether the generation was successful.
   */
  public function exec() {
    $css = $this
      ->createCss();
    $result = $this
      ->writeCss($css);
    if ($result) {
      $css = $this
        ->createRtlCss();
      $result = $this
        ->writeRtlCss($css);
    }
    return $result;
  }

  /**
   * Creates the css.
   *
   * @return string
   *   The created css.
   */
  protected function createCss() {
    $css = '';
    $css .= $this
      ->createTableCss();
    $css .= $this
      ->createCaptionCss();
    $css .= $this
      ->createHeaderCss();
    $css .= $this
      ->createWeekNotesCss();
    $css .= $this
      ->createDaysCss();
    $css .= $this
      ->createStatesCss();
    return $css;
  }

  /**
   * Creates the RTL css.
   *
   * @return string
   *   The created css.
   */
  protected function createRtlCss() {
    $css = '';
    $css .= $this
      ->createCaptionRtlCss();
    $css .= $this
      ->createHeaderRtlCss();
    $css .= $this
      ->createDaysRtlCss();
    $css .= $this
      ->createStatesRtlCss();
    return $css;
  }

  /**
   * Writes the created CSS to a file.
   *
   * @param string $css
   * @return boolean
   *   Whether the generation was successful.
   */
  protected function writeCss($css) {
    return $this
      ->writeFile('public://availability_calendar', 'availability_calendar.css', $css);
  }

  /**
   * Writes the created RTL CSS to a file.
   *
   * @param string $css
   * @return boolean
   *   Whether the generation was successful.
   */
  protected function writeRtlCss($css) {
    return $this
      ->writeFile('public://availability_calendar', 'availability_calendar-rtl.css', $css);
  }

  /**
   * Writes the created RTL CSS to a file.
   *
   * @param string $path
   * @param string $file
   * @param string $css
   *
   * @return boolean
   *   Whether the generation was successful.
   */
  protected function writeFile($path, $file, $css) {
    $result = false;
    if ($result = file_prepare_directory($path, FILE_CREATE_DIRECTORY)) {
      $file = $path . '/' . $file;
      if ($result = file_save_data($css, $file, FILE_EXISTS_REPLACE) !== FALSE) {

        // Set file permissions for webserver generated files. I use 0666 as
        // often, the ftp account is not the webserver user nor in its group.
        drupal_chmod($file, 0666);
      }
    }
    return $result;
  }

  /**
   * @return string
   *   The CSS for the table.
   */
  protected function createTableCss() {
    $category = 'table';
    $css = '';
    $css .= "/* Month (table) */\n";
    $css .= $this
      ->cssSelector(".cal table");
    $css .= $this
      ->addCssDeclaration($category, 'font-size');
    $css .= $this
      ->addCssColorDeclaration($category, 'color');
    $css .= $this
      ->addCssColorDeclaration($category, 'background-color');
    $css .= $this
      ->addCssLengthDeclaration($category, 'border-width');
    $css .= $this
      ->addCssColorDeclaration($category, 'border-color');
    $css .= $this
      ->cssDeclaration('border-style', 'solid');
    $css .= "}\n";
    return $css;
  }

  /**
   * @return string
   *   The CSS for the caption.
   */
  protected function createCaptionCss() {
    $category = 'caption';
    $css = '';
    $css .= "/* Month name and year (caption) */\n";
    $css .= $this
      ->cssSelector(".cal caption");
    $css .= $this
      ->addCssDeclaration($category, 'text-align');
    $css .= $this
      ->addCssDeclaration($category, 'font-weight');
    $css .= $this
      ->addCssDeclaration($category, 'font-style');
    $css .= $this
      ->addCssDeclaration($category, 'font-size');
    $css .= $this
      ->cssSelectorEnd();
    return $css;
  }

  /**
   * @return string
   *   The RTL CSS for the caption.
   */
  protected function createCaptionRtlCss() {
    $category = 'caption';
    $css = '';
    $value = $this
      ->getRtlTextAlign($category, 'text-align');
    if (!empty($value)) {
      $css .= "/* Month name and year (caption) */\n";
      $css .= $this
        ->cssSelector(".cal caption");
      $css .= $this
        ->cssDeclaration('text-align', $value);
      $css .= $this
        ->cssSelectorEnd();
    }
    return $css;
  }

  /**
   * @return string
   *   The CSS for the header.
   */
  protected function createHeaderCss() {
    $category = 'header';
    $css = '';
    $css .= "/* Weekday names (header row) */\n";
    $css .= $this
      ->cssSelector(".cal thead th");
    $css .= $this
      ->addCssLengthDeclaration($category, 'height');
    $line_height = $this
      ->getStyle($category, 'height');

    // @todo: subtract border from line-height, but for that we need to add border as a style in this UI.
    $css .= $this
      ->cssDeclaration('line-height', $line_height . 'px');
    $css .= $this
      ->addCssDeclaration($category, 'text-align');
    $css .= $this
      ->addCssDeclaration($category, 'font-weight');
    $css .= $this
      ->addCssDeclaration($category, 'font-style');
    $css .= $this
      ->addCssDeclaration($category, 'font-size');

    // Add color also to th cells to override Bartik
    $css .= $this
      ->addCssColorDeclaration('table', 'color');

    // Add width to first row to assure that IE7 works fine.
    // Will be overridden for the column that contains the week number/notes.
    $css .= $this
      ->addCssLengthDeclaration('days', 'width');
    $css .= $this
      ->cssSelectorEnd();
    return $css;
  }

  /**
   * @return string
   *   The RTL CSS for the header.
   */
  protected function createHeaderRtlCss() {
    $category = 'header';
    $css = '';
    $value = $this
      ->getRtlTextAlign($category, 'text-align');
    if (!empty($value)) {
      $css .= "/* Weekday names (header row) */\n";
      $css .= $this
        ->cssSelector(".cal thead th");
      $css .= $this
        ->cssDeclaration('text-align', $value);
      $css .= $this
        ->cssSelectorEnd();
    }
    return $css;
  }

  /**
   * @return string
   *   The CSS for the WeekNotes.
   */
  protected function createWeekNotesCss() {
    $category = 'week_notes';
    $css = '';
    $css .= "/* Week notes (header column) */\n";
    $css .= $this
      ->cssSelector(".cal tbody th, .cal thead th.cal-weekno-header");
    $css .= $this
      ->addCssLengthDeclaration($category, 'width');

    // Add color also to th cells to override Bartik
    $css .= $this
      ->addCssColorDeclaration('table', 'color');
    $css .= $this
      ->cssSelectorEnd();
    return $css;
  }

  /**
   * @return string
   *   The CSS for the day cells.
   */
  protected function createDaysCss() {
    $category = 'days';
    $css = '';
    $css .= "/* Days (td) */\n";
    $css .= $this
      ->cssSelector(".cal td");
    $css .= $this
      ->addCssLengthDeclaration($category, 'width');
    $css .= $this
      ->addCssLengthDeclaration($category, 'height');
    $css .= $this
      ->addCssDeclaration($category, 'text-align');
    $css .= $this
      ->addCssDeclaration($category, 'vertical-align');

    // Add color also to td cells to override themes that give cells a color.
    $css .= $this
      ->addCssColorDeclaration('table', 'color');
    $css .= $this
      ->cssSelectorEnd();
    return $css;
  }

  /**
   * @return string
   *   The RTL CSS for the day cells.
   */
  protected function createDaysRtlCss() {
    $category = 'days';
    $css = '';
    $value = $this
      ->getRtlTextAlign($category, 'text-align');
    if (!empty($value)) {
      $css .= "/* Days (td) */\n";
      $css .= $this
        ->cssSelector(".cal td");
      $css .= $this
        ->cssDeclaration('text-align', $value);
      $css .= $this
        ->cssSelectorEnd();
    }
    return $css;
  }

  /**
   * @return string
   *   The CSS for the states.
   */
  protected function createStatesCss() {
    $category = 'states';
    $splitDay = $this
      ->getStyle($category, 'split-day');
    $states = availability_calendar_get_states();
    $css = '';
    $css .= "/* Whole day coloring */\n";
    foreach ($states as $sid => $state) {
      $class = $state['css_class'];

      // Whole day state.
      $selector = ".{$class}, .{$class} > div";
      $css .= $this
        ->cssSelector($selector);
      $css .= $this
        ->addCssColorDeclaration($category, array(
        $class => 'background-color',
      ));
      $css .= $this
        ->cssSelectorEnd();
    }

    // Get the width and heigth.
    $cellWidth = (int) $this
      ->getStyle('days', 'width');

    // We have to subtract pixels, so this value can only be in pixels.
    $cellWidthUnit = 'px';
    $cellHeight = (int) $this
      ->getStyle('days', 'height');
    $cellHeightUnit = 'px';
    if (!empty($splitDay)) {
      $css .= "/* Split day coloring */\n";

      // Styles for the state colors. The border color definitions for the pm
      // state should be superfluous as the background-color is the same.
      // However I did get some artifacts in at least FireFox.
      foreach ($states as $sid => $state) {
        $class = $state['css_class'];
        $css .= $this
          ->cssSelector("html[dir=ltr] .cal .{$class}-am > span");
        switch ($splitDay) {
          case '/':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-left-color',
            ));
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-top-color',
            ));
            break;
          case '\\':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-left-color',
            ));
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-bottom-color',
            ));
            break;
          case '|':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-left-color',
            ));
            break;
          case '―':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-top-color',
            ));
            break;
        }
        $css .= $this
          ->cssSelectorEnd();
        $css .= $this
          ->cssSelector("html[dir=ltr] .cal .{$class}-pm > span");
        switch ($splitDay) {
          case '/':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-right-color',
            ));
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-bottom-color',
            ));
            break;
          case '\\':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-right-color',
            ));
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-top-color',
            ));
            break;
          case '|':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-right-color',
            ));
            break;
          case '―':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-bottom-color',
            ));
            break;
        }
        $css .= $this
          ->cssSelectorEnd();
      }

      // Styles for the outer span that takes care of the background coloring of
      // split days.
      $css .= "/* Split day dimensioning and positioning */\n";
      $css .= $this
        ->cssSelector('.cal td > span');
      switch ($splitDay) {
        case '/':
          $css .= $this
            ->cssDeclaration('width', '0');
          $css .= $this
            ->cssDeclaration('height', '0');
          $css .= $this
            ->cssDeclaration('border-left-width', floor($cellWidth / 2) . $cellWidthUnit);
          $css .= $this
            ->cssDeclaration('border-top-width', floor($cellHeight / 2) . $cellHeightUnit);
          $css .= $this
            ->cssDeclaration('border-right-width', ceil($cellWidth / 2) . $cellWidthUnit);
          $css .= $this
            ->cssDeclaration('border-bottom-width', ceil($cellHeight / 2) . $cellHeightUnit);
          break;
        case '\\':
          $css .= $this
            ->cssDeclaration('width', '0');
          $css .= $this
            ->cssDeclaration('height', '0');
          $css .= $this
            ->cssDeclaration('border-left-width', floor($cellWidth / 2) . $cellWidthUnit);
          $css .= $this
            ->cssDeclaration('border-bottom-width', floor($cellHeight / 2) . $cellHeightUnit);
          $css .= $this
            ->cssDeclaration('border-right-width', ceil($cellWidth / 2) . $cellWidthUnit);
          $css .= $this
            ->cssDeclaration('border-top-width', ceil($cellHeight / 2) . $cellHeightUnit);
          break;
        case '|':
          $css .= $this
            ->cssDeclaration('width', '0');
          $css .= $this
            ->addCssLengthDeclaration('days', 'height');
          $css .= $this
            ->cssDeclaration('border-left-width', floor($cellWidth / 2) . $cellWidthUnit);
          $css .= $this
            ->cssDeclaration('border-right-width', ceil($cellWidth / 2) . $cellWidthUnit);
          break;
        case '―':
          $css .= $this
            ->addCssLengthDeclaration('days', 'width');
          $css .= $this
            ->cssDeclaration('height', '0');
          $css .= $this
            ->cssDeclaration('border-top-width', floor($cellHeight / 2) . $cellHeightUnit);
          $css .= $this
            ->cssDeclaration('border-bottom-width', ceil($cellHeight / 2) . $cellHeightUnit);
          break;
      }
      $css .= $this
        ->cssSelectorEnd();

      // Styles for the inner span within the td that contains the day number.
      $css .= $this
        ->cssSelector('.cal td > span > span');
      switch ($splitDay) {
        case '/':
          $css .= $this
            ->cssDeclaration('top', -floor($cellHeight / 2) . $cellHeightUnit);
          $css .= $this
            ->cssDeclaration('left', -floor($cellWidth / 2) . $cellWidthUnit);
          break;
        case '\\':
          $css .= $this
            ->cssDeclaration('top', -floor($cellHeight / 2) . $cellHeightUnit);
          $css .= $this
            ->cssDeclaration('left', -ceil($cellWidth / 2) . $cellWidthUnit);
          break;
        case '|':
          $css .= $this
            ->cssDeclaration('top', '0');
          $css .= $this
            ->cssDeclaration('left', -floor($cellWidth / 2) . $cellWidthUnit);
          break;
        case '―':
          $css .= $this
            ->cssDeclaration('top', -floor($cellHeight / 2) . $cellHeightUnit);
          $css .= $this
            ->cssDeclaration('left', '0');
          break;
      }
      $css .= $this
        ->cssSelectorEnd();
    }
    $css .= "/* Dimensions and other properties for element containing day number */\n";
    $css .= $this
      ->cssSelector('.cal td > div, .cal td > span > span');
    if ($cellWidth >= 2 && $cellHeight >= 2) {

      // Subtract border of 1px.
      $cellWidth -= 2;
      $cellHeight -= 2;
      $css .= $this
        ->cssDeclaration('width', $cellWidth . $cellWidthUnit);
      $css .= $this
        ->cssDeclaration('height', $cellHeight . $cellHeightUnit);
      $css .= $this
        ->cssDeclaration('line-height', $cellHeight . $cellHeightUnit);
    }
    $css .= $this
      ->addCssDeclaration('days', 'text-align');
    $css .= $this
      ->addCssDeclaration('days', 'vertical-align');
    $css .= $this
      ->cssSelectorEnd();
    return $css;
  }

  /**
   * @return string
   *   The RTL specific CSS for the states.
   */
  protected function createStatesRtlCss() {
    $category = 'states';
    $splitDay = $this
      ->getStyle($category, 'split-day');
    $states = availability_calendar_get_states();
    $css = '';

    // Get the width and height
    $cellWidth = (int) $this
      ->getStyle('days', 'width');

    // We have to subtract pixels, so this value can only be in pixels
    $cellWidthUnit = 'px';
    $cellHeight = (int) $this
      ->getStyle('days', 'height');
    $cellHeightUnit = 'px';
    if (!empty($splitDay)) {
      $css .= "/* Split day coloring */\n";

      // Styles for the state colors. The border color definitions for the pm
      // state should be superfluous as the background-color is the same.

      //However I did get some artifacts in at least FireFox.
      foreach ($states as $sid => $state) {
        $class = $state['css_class'];
        $css .= $this
          ->cssSelector("html[dir=rtl] .cal .{$class}-am > span");
        switch ($splitDay) {
          case '/':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-right-color',
            ));
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-top-color',
            ));
            break;
          case '\\':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-right-color',
            ));
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-bottom-color',
            ));
            break;
          case '|':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-right-color',
            ));
            break;
          case '―':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-top-color',
            ));
            break;
        }
        $css .= $this
          ->cssSelectorEnd();
        $css .= $this
          ->cssSelector("html[dir=rtl] .cal .{$class}-pm > span");
        switch ($splitDay) {
          case '/':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-left-color',
            ));
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-bottom-color',
            ));
            break;
          case '\\':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-left-color',
            ));
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-top-color',
            ));
            break;
          case '|':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-left-color',
            ));
            break;
          case '―':
            $css .= $this
              ->addCssColorDeclaration($category, array(
              $class => 'border-bottom-color',
            ));
            break;
        }
        $css .= $this
          ->cssSelectorEnd();
      }

      // Styles for the outer span that takes care of the background coloring of
      // split days.
      $css .= "/* Split day dimensioning and positioning */\n";
      $css .= $this
        ->cssSelector('.cal td > span');
      switch ($splitDay) {
        case '/':
          $css .= $this
            ->cssDeclaration('border-left-width', ceil($cellWidth / 2) . $cellWidthUnit);
          $css .= $this
            ->cssDeclaration('border-right-width', floor($cellWidth / 2) . $cellWidthUnit);
          break;
        case '\\':
          $css .= $this
            ->cssDeclaration('border-left-width', ceil($cellWidth / 2) . $cellWidthUnit);
          $css .= $this
            ->cssDeclaration('border-right-width', floor($cellWidth / 2) . $cellWidthUnit);
          break;
        case '|':
          $css .= $this
            ->cssDeclaration('border-left-width', ceil($cellWidth / 2) . $cellWidthUnit);
          $css .= $this
            ->cssDeclaration('border-right-width', floor($cellWidth / 2) . $cellWidthUnit);
          break;
        case '―':
          break;
      }
      $css .= $this
        ->cssSelectorEnd();

      // Styles for the inner span within the td that contains the day number.
      $css .= $this
        ->cssSelector('.cal td > span > span');
      switch ($splitDay) {
        case '/':
          $css .= $this
            ->cssDeclaration('left', 'auto');
          $css .= $this
            ->cssDeclaration('right', -floor($cellWidth / 2) . $cellWidthUnit);
          break;
        case '\\':
          $css .= $this
            ->cssDeclaration('left', 'auto');
          $css .= $this
            ->cssDeclaration('right', -ceil($cellWidth / 2) . $cellWidthUnit);
          break;
        case '|':
          $css .= $this
            ->cssDeclaration('left', 'auto');
          $css .= $this
            ->cssDeclaration('right', -floor($cellWidth / 2) . $cellWidthUnit);
          break;
        case '―':
          $css .= $this
            ->cssDeclaration('left', 'auto');
          $css .= $this
            ->cssDeclaration('right', '0');
          break;
      }
      $css .= $this
        ->cssSelectorEnd();
    }
    $value = $this
      ->getRtlTextAlign('days', 'text-align');
    if (!empty($value)) {
      $css .= "/* Dimensions and other properties for element containing day number */\n";
      $css .= $this
        ->cssSelector('.cal td > div, .cal td > span > span');
      $css .= $this
        ->cssDeclaration('text-align', $value);
      $css .= $this
        ->cssSelectorEnd();
    }
    return $css;
  }
  protected function getRtlTextAlign($category, $name) {
    $value = $this
      ->getStyle($category, $name);
    switch ($value) {
      case 'left':
        $value = 'right';
        break;
      case 'right':
        $value = 'left';
        break;
      default:
        $value = '';
        break;
    }
    return $value;
  }

  /**
   * Single place that specifies how to format selectors.
   */
  protected function cssSelector($selector) {
    return "{$selector} {\n";
  }

  /**
   * Single place that specifies how to format selectors ends.
   */
  protected function cssSelectorEnd() {
    return "}\n\n";
  }

  /**
   * Creates a CSS declaration for a length property.
   * @see addCssDeclaration for param and return info.
   */
  protected function addCssLengthDeclaration($category, $name) {
    $value = $this
      ->getStyle($category, $name);

    // Pixels are the default for numeric values
    if (!empty($value) && is_numeric($value)) {
      $value .= 'px';
    }
    return $this
      ->cssDeclaration($name, $value);
  }

  /**
   * Creates a CSS declaration for a color property.
   * @see addCssDeclaration for param and return info.
   */
  protected function addCssColorDeclaration($category, $name) {
    $value = $this
      ->getStyle($category, $name);

    // Color codes may be given without #
    if (!empty($value) && ctype_xdigit($value)) {
      $value = '#' . $value;
    }
    return $this
      ->cssDeclaration($name, $value);
  }

  /**
   * Creates a CSS declaration.
   *
   * @param string $category
   *   The name of the category in which the setting is stored.
   * @param string|array $name
   *   The name of the setting and property. If $name is an array it should
   *   be an array with 1 element of the form <setting name> => <property name>. Use this if the
   *   setting name is not the name of the property.
   * @return string
   *   The generated CSS declaration: "  property: value;"
   */
  protected function addCssDeclaration($category, $name) {
    $value = $this
      ->getStyle($category, $name);
    return $this
      ->cssDeclaration($name, $value);
  }

  /**
   * Helper method to output spaces and new lines.
   */
  protected function cssDeclaration($property, $value) {
    if (is_array($property)) {
      $property = current($property);
    }
    return $value != '' ? "  {$property}: {$value};\n" : '';
  }

  /**
   * Helper method to return 1 style setting.
   *
   * @param string $category
   * @param string|array $name
   * @param mixed $default
   *
   * @return string
   */
  protected function getStyle($category, $name, $default = '') {
    if (is_array($name)) {
      $name = key($name);
    }
    return isset($this->styles[$category][$name]) && $this->styles[$category][$name] !== '<none>' ? $this->styles[$category][$name] : $default;
  }

}

Functions

Namesort descending Description
availability_calendar_get_styles Returns the CSS styles defined for the calendars.
availability_calendar_styles Define form callback for the admin/config/availability-calendar/styles page.
availability_calendar_styles_generate Callback to process form submission for the styles form.
availability_calendar_styles_validate Callback to validate the form for the style settings.

Classes