First commit
This commit is contained in:
commit
c6e2478c40
13918 changed files with 2303184 additions and 0 deletions
48
modules/color/color-rtl.css
Normal file
48
modules/color/color-rtl.css
Normal file
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* @file
|
||||
* Right-to-left specific stylesheet for the Color module.
|
||||
*/
|
||||
|
||||
#placeholder {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
/* Palette */
|
||||
.color-form .form-item {
|
||||
padding-left: 0;
|
||||
padding-right: 1em;
|
||||
}
|
||||
.color-form label {
|
||||
float: right;
|
||||
clear: right;
|
||||
}
|
||||
.color-form .form-text,
|
||||
.color-form .form-select {
|
||||
float: right;
|
||||
}
|
||||
.color-form .form-text {
|
||||
margin-right: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
#palette .hook {
|
||||
float: right;
|
||||
}
|
||||
#palette .down,
|
||||
#palette .up,
|
||||
#palette .both {
|
||||
background: url(images/hook-rtl.png) no-repeat 0 0;
|
||||
}
|
||||
#palette .up {
|
||||
background-position: 0 -27px;
|
||||
}
|
||||
#palette .both {
|
||||
background-position: 0 -54px;
|
||||
}
|
||||
#palette .lock {
|
||||
float: right;
|
||||
right: -10px;
|
||||
}
|
||||
html.js #preview {
|
||||
float: right;
|
||||
}
|
85
modules/color/color.css
Normal file
85
modules/color/color.css
Normal file
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @file
|
||||
* Stylesheet for the administration pages of the Color module.
|
||||
*/
|
||||
|
||||
/* Farbtastic placement */
|
||||
.color-form {
|
||||
max-width: 50em;
|
||||
position: relative;
|
||||
}
|
||||
#placeholder {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0; /* LTR */
|
||||
}
|
||||
|
||||
/* Palette */
|
||||
.color-form .form-item {
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
padding-left: 1em; /* LTR */
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.color-form label {
|
||||
float: left; /* LTR */
|
||||
clear: left; /* LTR */
|
||||
width: 10em;
|
||||
}
|
||||
.color-form .form-text,
|
||||
.color-form .form-select {
|
||||
float: left; /* LTR */
|
||||
}
|
||||
.color-form .form-text {
|
||||
text-align: center;
|
||||
margin-right: 5px; /* LTR */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#palette .hook {
|
||||
float: left; /* LTR */
|
||||
margin-top: 3px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
#palette .down,
|
||||
#palette .up,
|
||||
#palette .both {
|
||||
background: url(images/hook.png) no-repeat 100% 0; /* LTR */
|
||||
}
|
||||
#palette .up {
|
||||
background-position: 100% -27px; /* LTR */
|
||||
}
|
||||
#palette .both {
|
||||
background-position: 100% -54px; /* LTR */
|
||||
}
|
||||
|
||||
#palette .lock {
|
||||
float: left; /* LTR */
|
||||
position: relative;
|
||||
top: -1.4em;
|
||||
left: -10px; /* LTR */
|
||||
width: 20px;
|
||||
height: 25px;
|
||||
background: url(images/lock.png) no-repeat 50% 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#palette .unlocked {
|
||||
background-position: 50% -22px;
|
||||
}
|
||||
#palette .form-item {
|
||||
width: 20em;
|
||||
}
|
||||
#palette .item-selected {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
/* Preview */
|
||||
#preview {
|
||||
display: none;
|
||||
}
|
||||
html.js #preview {
|
||||
display: block;
|
||||
position: relative;
|
||||
float: left; /* LTR */
|
||||
}
|
12
modules/color/color.info
Normal file
12
modules/color/color.info
Normal file
|
@ -0,0 +1,12 @@
|
|||
name = Color
|
||||
description = Allows administrators to change the color scheme of compatible themes.
|
||||
package = Core
|
||||
version = VERSION
|
||||
core = 7.x
|
||||
files[] = color.test
|
||||
|
||||
; Information added by Drupal.org packaging script on 2017-06-21
|
||||
version = "7.56"
|
||||
project = "drupal"
|
||||
datestamp = "1498069849"
|
||||
|
66
modules/color/color.install
Normal file
66
modules/color/color.install
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Install, update and uninstall functions for the color module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
function color_requirements($phase) {
|
||||
$requirements = array();
|
||||
|
||||
if ($phase == 'runtime') {
|
||||
// Check for the PHP GD library.
|
||||
if (function_exists('imagegd2')) {
|
||||
$info = gd_info();
|
||||
$requirements['color_gd'] = array(
|
||||
'value' => $info['GD Version'],
|
||||
);
|
||||
|
||||
// Check for PNG support.
|
||||
if (function_exists('imagecreatefrompng')) {
|
||||
$requirements['color_gd']['severity'] = REQUIREMENT_OK;
|
||||
}
|
||||
else {
|
||||
$requirements['color_gd']['severity'] = REQUIREMENT_WARNING;
|
||||
$requirements['color_gd']['description'] = t('The GD library for PHP is enabled, but was compiled without PNG support. Check the <a href="@url">PHP image documentation</a> for information on how to correct this.', array('@url' => 'http://www.php.net/manual/ref.image.php'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$requirements['color_gd'] = array(
|
||||
'value' => t('Not installed'),
|
||||
'severity' => REQUIREMENT_ERROR,
|
||||
'description' => t('The GD library for PHP is missing or outdated. Check the <a href="@url">PHP image documentation</a> for information on how to correct this.', array('@url' => 'http://www.php.net/manual/book.image.php')),
|
||||
);
|
||||
}
|
||||
$requirements['color_gd']['title'] = t('GD library PNG support');
|
||||
}
|
||||
|
||||
return $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @addtogroup updates-7.x-extra
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Warn site administrator if unsafe CSS color codes are found in the database.
|
||||
*/
|
||||
function color_update_7001() {
|
||||
$theme_palettes = db_query("SELECT name FROM {variable} WHERE name LIKE 'color_%_palette'")->fetchCol();
|
||||
foreach ($theme_palettes as $name) {
|
||||
$palette = variable_get($name, array());
|
||||
foreach ($palette as $key => $color) {
|
||||
if (!preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color)) {
|
||||
drupal_set_message('Some of the custom CSS color codes specified via the color module are invalid. Please examine the themes which are making use of the color module at the <a href="'. url('admin/appearance/settings') .'">Appearance settings</a> page to verify their CSS color values.', 'warning');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup updates-7.x-extra".
|
||||
*/
|
250
modules/color/color.js
Normal file
250
modules/color/color.js
Normal file
|
@ -0,0 +1,250 @@
|
|||
/**
|
||||
* @file
|
||||
* Attaches the behaviors for the Color module.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
Drupal.behaviors.color = {
|
||||
attach: function (context, settings) {
|
||||
var i, j, colors, field_name;
|
||||
// This behavior attaches by ID, so is only valid once on a page.
|
||||
var form = $('#system-theme-settings .color-form', context).once('color');
|
||||
if (form.length == 0) {
|
||||
return;
|
||||
}
|
||||
var inputs = [];
|
||||
var hooks = [];
|
||||
var locks = [];
|
||||
var focused = null;
|
||||
|
||||
// Add Farbtastic.
|
||||
$(form).prepend('<div id="placeholder"></div>').addClass('color-processed');
|
||||
var farb = $.farbtastic('#placeholder');
|
||||
|
||||
// Decode reference colors to HSL.
|
||||
var reference = settings.color.reference;
|
||||
for (i in reference) {
|
||||
reference[i] = farb.RGBToHSL(farb.unpack(reference[i]));
|
||||
}
|
||||
|
||||
// Build a preview.
|
||||
var height = [];
|
||||
var width = [];
|
||||
// Loop through all defined gradients.
|
||||
for (i in settings.gradients) {
|
||||
// Add element to display the gradient.
|
||||
$('#preview').once('color').append('<div id="gradient-' + i + '"></div>');
|
||||
var gradient = $('#preview #gradient-' + i);
|
||||
// Add height of current gradient to the list (divided by 10).
|
||||
height.push(parseInt(gradient.css('height'), 10) / 10);
|
||||
// Add width of current gradient to the list (divided by 10).
|
||||
width.push(parseInt(gradient.css('width'), 10) / 10);
|
||||
// Add rows (or columns for horizontal gradients).
|
||||
// Each gradient line should have a height (or width for horizontal
|
||||
// gradients) of 10px (because we divided the height/width by 10 above).
|
||||
for (j = 0; j < (settings.gradients[i]['direction'] == 'vertical' ? height[i] : width[i]); ++j) {
|
||||
gradient.append('<div class="gradient-line"></div>');
|
||||
}
|
||||
}
|
||||
|
||||
// Fix preview background in IE6.
|
||||
if (navigator.appVersion.match(/MSIE [0-6]\./)) {
|
||||
var e = $('#preview #img')[0];
|
||||
var image = e.currentStyle.backgroundImage;
|
||||
e.style.backgroundImage = 'none';
|
||||
e.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image.substring(5, image.length - 2) + "')";
|
||||
}
|
||||
|
||||
// Set up colorScheme selector.
|
||||
$('#edit-scheme', form).change(function () {
|
||||
var schemes = settings.color.schemes, colorScheme = this.options[this.selectedIndex].value;
|
||||
if (colorScheme != '' && schemes[colorScheme]) {
|
||||
// Get colors of active scheme.
|
||||
colors = schemes[colorScheme];
|
||||
for (field_name in colors) {
|
||||
callback($('#edit-palette-' + field_name), colors[field_name], false, true);
|
||||
}
|
||||
preview();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Renders the preview.
|
||||
*/
|
||||
function preview() {
|
||||
Drupal.color.callback(context, settings, form, farb, height, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts a given color, using a reference pair (ref in HSL).
|
||||
*
|
||||
* This algorithm ensures relative ordering on the saturation and luminance
|
||||
* axes is preserved, and performs a simple hue shift.
|
||||
*
|
||||
* It is also symmetrical. If: shift_color(c, a, b) == d, then
|
||||
* shift_color(d, b, a) == c.
|
||||
*/
|
||||
function shift_color(given, ref1, ref2) {
|
||||
// Convert to HSL.
|
||||
given = farb.RGBToHSL(farb.unpack(given));
|
||||
|
||||
// Hue: apply delta.
|
||||
given[0] += ref2[0] - ref1[0];
|
||||
|
||||
// Saturation: interpolate.
|
||||
if (ref1[1] == 0 || ref2[1] == 0) {
|
||||
given[1] = ref2[1];
|
||||
}
|
||||
else {
|
||||
var d = ref1[1] / ref2[1];
|
||||
if (d > 1) {
|
||||
given[1] /= d;
|
||||
}
|
||||
else {
|
||||
given[1] = 1 - (1 - given[1]) * d;
|
||||
}
|
||||
}
|
||||
|
||||
// Luminance: interpolate.
|
||||
if (ref1[2] == 0 || ref2[2] == 0) {
|
||||
given[2] = ref2[2];
|
||||
}
|
||||
else {
|
||||
var d = ref1[2] / ref2[2];
|
||||
if (d > 1) {
|
||||
given[2] /= d;
|
||||
}
|
||||
else {
|
||||
given[2] = 1 - (1 - given[2]) * d;
|
||||
}
|
||||
}
|
||||
|
||||
return farb.pack(farb.HSLToRGB(given));
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for Farbtastic when a new color is chosen.
|
||||
*/
|
||||
function callback(input, color, propagate, colorScheme) {
|
||||
var matched;
|
||||
// Set background/foreground colors.
|
||||
$(input).css({
|
||||
backgroundColor: color,
|
||||
'color': farb.RGBToHSL(farb.unpack(color))[2] > 0.5 ? '#000' : '#fff'
|
||||
});
|
||||
|
||||
// Change input value.
|
||||
if ($(input).val() && $(input).val() != color) {
|
||||
$(input).val(color);
|
||||
|
||||
// Update locked values.
|
||||
if (propagate) {
|
||||
i = input.i;
|
||||
for (j = i + 1; ; ++j) {
|
||||
if (!locks[j - 1] || $(locks[j - 1]).is('.unlocked')) break;
|
||||
matched = shift_color(color, reference[input.key], reference[inputs[j].key]);
|
||||
callback(inputs[j], matched, false);
|
||||
}
|
||||
for (j = i - 1; ; --j) {
|
||||
if (!locks[j] || $(locks[j]).is('.unlocked')) break;
|
||||
matched = shift_color(color, reference[input.key], reference[inputs[j].key]);
|
||||
callback(inputs[j], matched, false);
|
||||
}
|
||||
|
||||
// Update preview.
|
||||
preview();
|
||||
}
|
||||
|
||||
// Reset colorScheme selector.
|
||||
if (!colorScheme) {
|
||||
resetScheme();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the color scheme selector.
|
||||
*/
|
||||
function resetScheme() {
|
||||
$('#edit-scheme', form).each(function () {
|
||||
this.selectedIndex = this.options.length - 1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Focuses Farbtastic on a particular field.
|
||||
*/
|
||||
function focus() {
|
||||
var input = this;
|
||||
// Remove old bindings.
|
||||
focused && $(focused).unbind('keyup', farb.updateValue)
|
||||
.unbind('keyup', preview).unbind('keyup', resetScheme)
|
||||
.parent().removeClass('item-selected');
|
||||
|
||||
// Add new bindings.
|
||||
focused = this;
|
||||
farb.linkTo(function (color) { callback(input, color, true, false); });
|
||||
farb.setColor(this.value);
|
||||
$(focused).keyup(farb.updateValue).keyup(preview).keyup(resetScheme)
|
||||
.parent().addClass('item-selected');
|
||||
}
|
||||
|
||||
// Initialize color fields.
|
||||
$('#palette input.form-text', form)
|
||||
.each(function () {
|
||||
// Extract palette field name
|
||||
this.key = this.id.substring(13);
|
||||
|
||||
// Link to color picker temporarily to initialize.
|
||||
farb.linkTo(function () {}).setColor('#000').linkTo(this);
|
||||
|
||||
// Add lock.
|
||||
var i = inputs.length;
|
||||
if (inputs.length) {
|
||||
var lock = $('<div class="lock"></div>').toggle(
|
||||
function () {
|
||||
$(this).addClass('unlocked');
|
||||
$(hooks[i - 1]).attr('class',
|
||||
locks[i - 2] && $(locks[i - 2]).is(':not(.unlocked)') ? 'hook up' : 'hook'
|
||||
);
|
||||
$(hooks[i]).attr('class',
|
||||
locks[i] && $(locks[i]).is(':not(.unlocked)') ? 'hook down' : 'hook'
|
||||
);
|
||||
},
|
||||
function () {
|
||||
$(this).removeClass('unlocked');
|
||||
$(hooks[i - 1]).attr('class',
|
||||
locks[i - 2] && $(locks[i - 2]).is(':not(.unlocked)') ? 'hook both' : 'hook down'
|
||||
);
|
||||
$(hooks[i]).attr('class',
|
||||
locks[i] && $(locks[i]).is(':not(.unlocked)') ? 'hook both' : 'hook up'
|
||||
);
|
||||
}
|
||||
);
|
||||
$(this).after(lock);
|
||||
locks.push(lock);
|
||||
};
|
||||
|
||||
// Add hook.
|
||||
var hook = $('<div class="hook"></div>');
|
||||
$(this).after(hook);
|
||||
hooks.push(hook);
|
||||
|
||||
$(this).parent().find('.lock').click();
|
||||
this.i = i;
|
||||
inputs.push(this);
|
||||
})
|
||||
.focus(focus);
|
||||
|
||||
$('#palette label', form);
|
||||
|
||||
// Focus first color.
|
||||
focus.call(inputs[0]);
|
||||
|
||||
// Render preview.
|
||||
preview();
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery);
|
815
modules/color/color.module
Normal file
815
modules/color/color.module
Normal file
|
@ -0,0 +1,815 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Allows users to change the color scheme of themes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_help().
|
||||
*/
|
||||
function color_help($path, $arg) {
|
||||
switch ($path) {
|
||||
case 'admin/help#color':
|
||||
$output = '<h3>' . t('About') . '</h3>';
|
||||
$output .= '<p>' . t('The Color module allows users with the <em>Administer site configuration</em> permission to quickly and easily change the color scheme of themes that have been built to be compatible with it. For more information, see the online handbook entry for <a href="@color">Color module</a>.', array('@color' => 'http://drupal.org/documentation/modules/color')) . '</p>';
|
||||
$output .= '<h3>' . t('Uses') . '</h3>';
|
||||
$output .= '<dl>';
|
||||
$output .= '<dt>' . t('Changing colors') . '</dt>';
|
||||
$output .= '<dd>' . t("Using the Color module allows you to easily change the color of links, backgrounds, text, and other theme elements. To change the color settings for a compatible theme, select the <em>Settings</em> link for your theme on the <a href='@configure'>Themes administration page</a>. If you don't see a color picker on that page, then your theme is not compatible with the color module. If you are sure that the theme does indeed support the color module, but the color picker does not appear, then <a href='@troubleshoot'>follow these troubleshooting procedures</a>.", array('@configure' => url('admin/appearance'), '@troubleshoot' => 'http://drupal.org/node/109457')) . '</dd>';
|
||||
$output .= '<dd>' . t("The Color module saves a modified copy of the theme's specified stylesheets in the files directory. This means that if you make any manual changes to your theme's stylesheet, <em>you must save your color settings again, even if they haven't changed</em>. This step is required because the module stylesheets (in the files directory) need to be recreated to include your changes.") . '</dd>';
|
||||
$output .= '</dl>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_theme().
|
||||
*/
|
||||
function color_theme() {
|
||||
return array(
|
||||
'color_scheme_form' => array(
|
||||
'render element' => 'form',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*/
|
||||
function color_form_system_theme_settings_alter(&$form, &$form_state) {
|
||||
if (isset($form_state['build_info']['args'][0]) && ($theme = $form_state['build_info']['args'][0]) && color_get_info($theme) && function_exists('gd_info')) {
|
||||
$form['color'] = array(
|
||||
'#type' => 'fieldset',
|
||||
'#title' => t('Color scheme'),
|
||||
'#weight' => -1,
|
||||
'#attributes' => array('id' => 'color_scheme_form'),
|
||||
'#theme' => 'color_scheme_form',
|
||||
);
|
||||
$form['color'] += color_scheme_form($form, $form_state, $theme);
|
||||
$form['#validate'][] = 'color_scheme_form_validate';
|
||||
$form['#submit'][] = 'color_scheme_form_submit';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter().
|
||||
*/
|
||||
function color_form_system_themes_alter(&$form, &$form_state) {
|
||||
_color_theme_select_form_alter($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for hook_form_FORM_ID_alter() implementations.
|
||||
*/
|
||||
function _color_theme_select_form_alter(&$form, &$form_state) {
|
||||
// Use the generated screenshot in the theme list.
|
||||
$themes = list_themes();
|
||||
foreach (element_children($form) as $theme) {
|
||||
if ($screenshot = variable_get('color_' . $theme . '_screenshot')) {
|
||||
if (isset($form[$theme]['screenshot'])) {
|
||||
$form[$theme]['screenshot']['#markup'] = theme('image', array('path' => $screenshot, 'title' => '', 'attributes' => array('class' => array('screenshot'))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces style sheets with color-altered style sheets.
|
||||
*
|
||||
* A theme that supports the color module should call this function from its
|
||||
* THEME_process_html() function, so that the correct style sheets are
|
||||
* included when html.tpl.php is rendered.
|
||||
*
|
||||
* @see theme()
|
||||
*/
|
||||
function _color_html_alter(&$vars) {
|
||||
global $theme_key;
|
||||
$themes = list_themes();
|
||||
|
||||
// Override stylesheets.
|
||||
$color_paths = variable_get('color_' . $theme_key . '_stylesheets', array());
|
||||
if (!empty($color_paths)) {
|
||||
|
||||
foreach ($themes[$theme_key]->stylesheets['all'] as $base_filename => $old_path) {
|
||||
// Loop over the path array with recolored CSS files to find matching
|
||||
// paths which could replace the non-recolored paths.
|
||||
foreach ($color_paths as $color_path) {
|
||||
// Color module currently requires unique file names to be used,
|
||||
// which allows us to compare different file paths.
|
||||
if (drupal_basename($old_path) == drupal_basename($color_path)) {
|
||||
// Replace the path to the new css file.
|
||||
// This keeps the order of the stylesheets intact.
|
||||
$vars['css'][$old_path]['data'] = $color_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$vars['styles'] = drupal_get_css($vars['css']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the logo with a color-altered logo.
|
||||
*
|
||||
* A theme that supports the color module should call this function from its
|
||||
* THEME_process_page() function, so that the correct logo is included when
|
||||
* page.tpl.php is rendered.
|
||||
*
|
||||
* @see theme()
|
||||
*/
|
||||
function _color_page_alter(&$vars) {
|
||||
global $theme_key;
|
||||
|
||||
// Override logo.
|
||||
$logo = variable_get('color_' . $theme_key . '_logo');
|
||||
if ($logo && $vars['logo'] && preg_match('!' . $theme_key . '/logo.png$!', $vars['logo'])) {
|
||||
$vars['logo'] = file_create_url($logo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Color module information for a particular theme.
|
||||
*/
|
||||
function color_get_info($theme) {
|
||||
static $theme_info = array();
|
||||
|
||||
if (isset($theme_info[$theme])) {
|
||||
return $theme_info[$theme];
|
||||
}
|
||||
|
||||
$path = drupal_get_path('theme', $theme);
|
||||
$file = DRUPAL_ROOT . '/' . $path . '/color/color.inc';
|
||||
if ($path && file_exists($file)) {
|
||||
include $file;
|
||||
$theme_info[$theme] = $info;
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the color palette for a particular theme.
|
||||
*/
|
||||
function color_get_palette($theme, $default = FALSE) {
|
||||
// Fetch and expand default palette.
|
||||
$info = color_get_info($theme);
|
||||
$palette = $info['schemes']['default']['colors'];
|
||||
|
||||
// Load variable.
|
||||
return $default ? $palette : variable_get('color_' . $theme . '_palette', $palette);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form constructor for the color configuration form for a particular theme.
|
||||
*
|
||||
* @param $theme
|
||||
* The machine name of the theme whose color settings are being configured.
|
||||
*
|
||||
* @see color_scheme_form_validate()
|
||||
* @see color_scheme_form_submit()
|
||||
* @ingroup forms
|
||||
*/
|
||||
function color_scheme_form($complete_form, &$form_state, $theme) {
|
||||
$base = drupal_get_path('module', 'color');
|
||||
$info = color_get_info($theme);
|
||||
|
||||
$info['schemes'][''] = array('title' => t('Custom'), 'colors' => array());
|
||||
$color_sets = array();
|
||||
$schemes = array();
|
||||
foreach ($info['schemes'] as $key => $scheme) {
|
||||
$color_sets[$key] = $scheme['title'];
|
||||
$schemes[$key] = $scheme['colors'];
|
||||
$schemes[$key] += $info['schemes']['default']['colors'];
|
||||
}
|
||||
|
||||
// See if we're using a predefined scheme.
|
||||
// Note: we use the original theme when the default scheme is chosen.
|
||||
$current_scheme = variable_get('color_' . $theme . '_palette', array());
|
||||
foreach ($schemes as $key => $scheme) {
|
||||
if ($current_scheme == $scheme) {
|
||||
$scheme_name = $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (empty($scheme_name)) {
|
||||
if (empty($current_scheme)) {
|
||||
$scheme_name = 'default';
|
||||
}
|
||||
else {
|
||||
$scheme_name = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Add scheme selector.
|
||||
$form['scheme'] = array(
|
||||
'#type' => 'select',
|
||||
'#title' => t('Color set'),
|
||||
'#options' => $color_sets,
|
||||
'#default_value' => $scheme_name,
|
||||
'#attached' => array(
|
||||
// Add Farbtastic color picker.
|
||||
'library' => array(
|
||||
array('system', 'farbtastic'),
|
||||
),
|
||||
// Add custom CSS.
|
||||
'css' => array(
|
||||
$base . '/color.css' => array(),
|
||||
),
|
||||
// Add custom JavaScript.
|
||||
'js' => array(
|
||||
$base . '/color.js',
|
||||
array(
|
||||
'data' => array(
|
||||
'color' => array(
|
||||
'reference' => color_get_palette($theme, TRUE),
|
||||
'schemes' => $schemes,
|
||||
),
|
||||
'gradients' => $info['gradients'],
|
||||
),
|
||||
'type' => 'setting',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Add palette fields.
|
||||
$palette = color_get_palette($theme);
|
||||
$names = $info['fields'];
|
||||
$form['palette']['#tree'] = TRUE;
|
||||
foreach ($palette as $name => $value) {
|
||||
if (isset($names[$name])) {
|
||||
$form['palette'][$name] = array(
|
||||
'#type' => 'textfield',
|
||||
'#title' => check_plain($names[$name]),
|
||||
'#value_callback' => 'color_palette_color_value',
|
||||
'#default_value' => $value,
|
||||
'#size' => 8,
|
||||
);
|
||||
}
|
||||
}
|
||||
$form['theme'] = array('#type' => 'value', '#value' => $theme);
|
||||
$form['info'] = array('#type' => 'value', '#value' => $info);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns HTML for a theme's color form.
|
||||
*
|
||||
* @param $variables
|
||||
* An associative array containing:
|
||||
* - form: A render element representing the form.
|
||||
*
|
||||
* @ingroup themeable
|
||||
*/
|
||||
function theme_color_scheme_form($variables) {
|
||||
$form = $variables['form'];
|
||||
|
||||
$theme = $form['theme']['#value'];
|
||||
$info = $form['info']['#value'];
|
||||
$path = drupal_get_path('theme', $theme) . '/';
|
||||
drupal_add_css($path . $info['preview_css']);
|
||||
|
||||
$preview_js_path = isset($info['preview_js']) ? $path . $info['preview_js'] : drupal_get_path('module', 'color') . '/' . 'preview.js';
|
||||
// Add the JS at a weight below color.js.
|
||||
drupal_add_js($preview_js_path, array('weight' => -1));
|
||||
|
||||
$output = '';
|
||||
$output .= '<div class="color-form clearfix">';
|
||||
// Color schemes
|
||||
$output .= drupal_render($form['scheme']);
|
||||
// Palette
|
||||
$output .= '<div id="palette" class="clearfix">';
|
||||
foreach (element_children($form['palette']) as $name) {
|
||||
$output .= drupal_render($form['palette'][$name]);
|
||||
}
|
||||
$output .= '</div>';
|
||||
// Preview
|
||||
$output .= drupal_render_children($form);
|
||||
$output .= '<h2>' . t('Preview') . '</h2>';
|
||||
// Attempt to load preview HTML if the theme provides it.
|
||||
$preview_html_path = DRUPAL_ROOT . '/' . (isset($info['preview_html']) ? drupal_get_path('theme', $theme) . '/' . $info['preview_html'] : drupal_get_path('module', 'color') . '/preview.html');
|
||||
$output .= file_get_contents($preview_html_path);
|
||||
// Close the wrapper div.
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the value for a palette color field.
|
||||
*
|
||||
* @param $element
|
||||
* The form element whose value is being populated.
|
||||
* @param $input
|
||||
* The incoming input to populate the form element. If this is FALSE,
|
||||
* the element's default value should be returned.
|
||||
* @param $form_state
|
||||
* A keyed array containing the current state of the form.
|
||||
*
|
||||
* @return
|
||||
* The data that will appear in the $form_state['values'] collection for this
|
||||
* element. Return nothing to use the default.
|
||||
*/
|
||||
function color_palette_color_value($element, $input = FALSE, $form_state = array()) {
|
||||
// If we suspect a possible cross-site request forgery attack, only accept
|
||||
// hexadecimal CSS color strings from user input, to avoid problems when this
|
||||
// value is used in the JavaScript preview.
|
||||
if ($input !== FALSE) {
|
||||
// Start with the provided value for this textfield, and validate that if
|
||||
// necessary, falling back on the default value.
|
||||
$value = form_type_textfield_value($element, $input, $form_state);
|
||||
if (!$value || !isset($form_state['complete form']['#token']) || color_valid_hexadecimal_string($value) || drupal_valid_token($form_state['values']['form_token'], $form_state['complete form']['#token'])) {
|
||||
return $value;
|
||||
}
|
||||
else {
|
||||
return $element['#default_value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a hexadecimal CSS color string is valid.
|
||||
*
|
||||
* @param $color
|
||||
* The string to check.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the string is a valid hexadecimal CSS color string, or FALSE if it
|
||||
* isn't.
|
||||
*/
|
||||
function color_valid_hexadecimal_string($color) {
|
||||
return preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Form validation handler for color_scheme_form().
|
||||
*
|
||||
* @see color_scheme_form_submit()
|
||||
*/
|
||||
function color_scheme_form_validate($form, &$form_state) {
|
||||
// Only accept hexadecimal CSS color strings to avoid XSS upon use.
|
||||
foreach ($form_state['values']['palette'] as $key => $color) {
|
||||
if (!color_valid_hexadecimal_string($color)) {
|
||||
form_set_error('palette][' . $key, t('%name must be a valid hexadecimal CSS color value.', array('%name' => $form['color']['palette'][$key]['#title'])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form submission handler for color_scheme_form().
|
||||
*
|
||||
* @see color_scheme_form_validate()
|
||||
*/
|
||||
function color_scheme_form_submit($form, &$form_state) {
|
||||
// Get theme coloring info.
|
||||
if (!isset($form_state['values']['info'])) {
|
||||
return;
|
||||
}
|
||||
$theme = $form_state['values']['theme'];
|
||||
$info = $form_state['values']['info'];
|
||||
|
||||
// Resolve palette.
|
||||
$palette = $form_state['values']['palette'];
|
||||
if ($form_state['values']['scheme'] != '') {
|
||||
foreach ($palette as $key => $color) {
|
||||
if (isset($info['schemes'][$form_state['values']['scheme']]['colors'][$key])) {
|
||||
$palette[$key] = $info['schemes'][$form_state['values']['scheme']]['colors'][$key];
|
||||
}
|
||||
}
|
||||
$palette += $info['schemes']['default']['colors'];
|
||||
}
|
||||
|
||||
// Make sure enough memory is available, if PHP's memory limit is compiled in.
|
||||
if (function_exists('memory_get_usage')) {
|
||||
// Fetch source image dimensions.
|
||||
$source = drupal_get_path('theme', $theme) . '/' . $info['base_image'];
|
||||
list($width, $height) = getimagesize($source);
|
||||
|
||||
// We need at least a copy of the source and a target buffer of the same
|
||||
// size (both at 32bpp).
|
||||
$required = $width * $height * 8;
|
||||
// We intend to prevent color scheme changes if there isn't enough memory
|
||||
// available. memory_get_usage(TRUE) returns a more accurate number than
|
||||
// memory_get_usage(), therefore we won't inadvertently reject a color
|
||||
// scheme change based on a faulty memory calculation.
|
||||
$usage = memory_get_usage(TRUE);
|
||||
$memory_limit = ini_get('memory_limit');
|
||||
$size = parse_size($memory_limit);
|
||||
if (!drupal_check_memory_limit($usage + $required, $memory_limit)) {
|
||||
drupal_set_message(t('There is not enough memory available to PHP to change this theme\'s color scheme. You need at least %size more. Check the <a href="@url">PHP documentation</a> for more information.', array('%size' => format_size($usage + $required - $size), '@url' => 'http://www.php.net/manual/ini.core.php#ini.sect.resource-limits')), 'error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete old files.
|
||||
foreach (variable_get('color_' . $theme . '_files', array()) as $file) {
|
||||
@drupal_unlink($file);
|
||||
}
|
||||
if (isset($file) && $file = dirname($file)) {
|
||||
@drupal_rmdir($file);
|
||||
}
|
||||
|
||||
// Don't render the default colorscheme, use the standard theme instead.
|
||||
if (implode(',', color_get_palette($theme, TRUE)) == implode(',', $palette)) {
|
||||
variable_del('color_' . $theme . '_palette');
|
||||
variable_del('color_' . $theme . '_stylesheets');
|
||||
variable_del('color_' . $theme . '_logo');
|
||||
variable_del('color_' . $theme . '_files');
|
||||
variable_del('color_' . $theme . '_screenshot');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare target locations for generated files.
|
||||
$id = $theme . '-' . substr(hash('sha256', serialize($palette) . microtime()), 0, 8);
|
||||
$paths['color'] = 'public://color';
|
||||
$paths['target'] = $paths['color'] . '/' . $id;
|
||||
foreach ($paths as $path) {
|
||||
file_prepare_directory($path, FILE_CREATE_DIRECTORY);
|
||||
}
|
||||
$paths['target'] = $paths['target'] . '/';
|
||||
$paths['id'] = $id;
|
||||
$paths['source'] = drupal_get_path('theme', $theme) . '/';
|
||||
$paths['files'] = $paths['map'] = array();
|
||||
|
||||
// Save palette and logo location.
|
||||
variable_set('color_' . $theme . '_palette', $palette);
|
||||
variable_set('color_' . $theme . '_logo', $paths['target'] . 'logo.png');
|
||||
|
||||
// Copy over neutral images.
|
||||
foreach ($info['copy'] as $file) {
|
||||
$base = drupal_basename($file);
|
||||
$source = $paths['source'] . $file;
|
||||
$filepath = file_unmanaged_copy($source, $paths['target'] . $base);
|
||||
$paths['map'][$file] = $base;
|
||||
$paths['files'][] = $filepath;
|
||||
}
|
||||
|
||||
// Render new images, if image has been provided.
|
||||
if ($info['base_image']) {
|
||||
_color_render_images($theme, $info, $paths, $palette);
|
||||
}
|
||||
|
||||
// Rewrite theme stylesheets.
|
||||
$css = array();
|
||||
foreach ($info['css'] as $stylesheet) {
|
||||
// Build a temporary array with LTR and RTL files.
|
||||
$files = array();
|
||||
if (file_exists($paths['source'] . $stylesheet)) {
|
||||
$files[] = $stylesheet;
|
||||
|
||||
$rtl_file = str_replace('.css', '-rtl.css', $stylesheet);
|
||||
if (file_exists($paths['source'] . $rtl_file)) {
|
||||
$files[] = $rtl_file;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($files as $file) {
|
||||
// Aggregate @imports recursively for each configured top level CSS file
|
||||
// without optimization. Aggregation and optimization will be
|
||||
// handled by drupal_build_css_cache() only.
|
||||
$style = drupal_load_stylesheet($paths['source'] . $file, FALSE);
|
||||
|
||||
// Return the path to where this CSS file originated from, stripping
|
||||
// off the name of the file at the end of the path.
|
||||
$base = base_path() . dirname($paths['source'] . $file) . '/';
|
||||
_drupal_build_css_path(NULL, $base);
|
||||
|
||||
// Prefix all paths within this CSS file, ignoring absolute paths.
|
||||
$style = preg_replace_callback('/url\([\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\)/i', '_drupal_build_css_path', $style);
|
||||
|
||||
// Rewrite stylesheet with new colors.
|
||||
$style = _color_rewrite_stylesheet($theme, $info, $paths, $palette, $style);
|
||||
$base_file = drupal_basename($file);
|
||||
$css[] = $paths['target'] . $base_file;
|
||||
_color_save_stylesheet($paths['target'] . $base_file, $style, $paths);
|
||||
}
|
||||
}
|
||||
|
||||
// Maintain list of files.
|
||||
variable_set('color_' . $theme . '_stylesheets', $css);
|
||||
variable_set('color_' . $theme . '_files', $paths['files']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites the stylesheet to match the colors in the palette.
|
||||
*/
|
||||
function _color_rewrite_stylesheet($theme, &$info, &$paths, $palette, $style) {
|
||||
$themes = list_themes();
|
||||
// Prepare color conversion table.
|
||||
$conversion = $palette;
|
||||
foreach ($conversion as $k => $v) {
|
||||
$conversion[$k] = drupal_strtolower($v);
|
||||
}
|
||||
$default = color_get_palette($theme, TRUE);
|
||||
|
||||
// Split off the "Don't touch" section of the stylesheet.
|
||||
$split = "Color Module: Don't touch";
|
||||
if (strpos($style, $split) !== FALSE) {
|
||||
list($style, $fixed) = explode($split, $style);
|
||||
}
|
||||
|
||||
// Find all colors in the stylesheet and the chunks in between.
|
||||
$style = preg_split('/(#[0-9a-f]{6}|#[0-9a-f]{3})/i', $style, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$is_color = FALSE;
|
||||
$output = '';
|
||||
$base = 'base';
|
||||
|
||||
// Iterate over all the parts.
|
||||
foreach ($style as $chunk) {
|
||||
if ($is_color) {
|
||||
$chunk = drupal_strtolower($chunk);
|
||||
// Check if this is one of the colors in the default palette.
|
||||
if ($key = array_search($chunk, $default)) {
|
||||
$chunk = $conversion[$key];
|
||||
}
|
||||
// Not a pre-set color. Extrapolate from the base.
|
||||
else {
|
||||
$chunk = _color_shift($palette[$base], $default[$base], $chunk, $info['blend_target']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Determine the most suitable base color for the next color.
|
||||
|
||||
// 'a' declarations. Use link.
|
||||
if (preg_match('@[^a-z0-9_-](a)[^a-z0-9_-][^/{]*{[^{]+$@i', $chunk)) {
|
||||
$base = 'link';
|
||||
}
|
||||
// 'color:' styles. Use text.
|
||||
elseif (preg_match('/(?<!-)color[^{:]*:[^{#]*$/i', $chunk)) {
|
||||
$base = 'text';
|
||||
}
|
||||
// Reset back to base.
|
||||
else {
|
||||
$base = 'base';
|
||||
}
|
||||
}
|
||||
$output .= $chunk;
|
||||
$is_color = !$is_color;
|
||||
}
|
||||
// Append fixed colors segment.
|
||||
if (isset($fixed)) {
|
||||
$output .= $fixed;
|
||||
}
|
||||
|
||||
// Replace paths to images.
|
||||
foreach ($paths['map'] as $before => $after) {
|
||||
$before = base_path() . $paths['source'] . $before;
|
||||
$before = preg_replace('`(^|/)(?!../)([^/]+)/../`', '$1', $before);
|
||||
$output = str_replace($before, $after, $output);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the rewritten stylesheet to disk.
|
||||
*/
|
||||
function _color_save_stylesheet($file, $style, &$paths) {
|
||||
$filepath = file_unmanaged_save_data($style, $file, FILE_EXISTS_REPLACE);
|
||||
$paths['files'][] = $filepath;
|
||||
|
||||
// Set standard file permissions for webserver-generated files.
|
||||
drupal_chmod($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders images that match a given palette.
|
||||
*/
|
||||
function _color_render_images($theme, &$info, &$paths, $palette) {
|
||||
// Prepare template image.
|
||||
$source = $paths['source'] . '/' . $info['base_image'];
|
||||
$source = imagecreatefrompng($source);
|
||||
$width = imagesx($source);
|
||||
$height = imagesy($source);
|
||||
|
||||
// Prepare target buffer.
|
||||
$target = imagecreatetruecolor($width, $height);
|
||||
imagealphablending($target, TRUE);
|
||||
|
||||
// Fill regions of solid color.
|
||||
foreach ($info['fill'] as $color => $fill) {
|
||||
imagefilledrectangle($target, $fill[0], $fill[1], $fill[0] + $fill[2], $fill[1] + $fill[3], _color_gd($target, $palette[$color]));
|
||||
}
|
||||
|
||||
// Render gradients.
|
||||
foreach ($info['gradients'] as $gradient) {
|
||||
// Get direction of the gradient.
|
||||
if (isset($gradient['direction']) && $gradient['direction'] == 'horizontal') {
|
||||
// Horizontal gradient.
|
||||
for ($x = 0; $x < $gradient['dimension'][2]; $x++) {
|
||||
$color = _color_blend($target, $palette[$gradient['colors'][0]], $palette[$gradient['colors'][1]], $x / ($gradient['dimension'][2] - 1));
|
||||
imagefilledrectangle($target, ($gradient['dimension'][0] + $x), $gradient['dimension'][1], ($gradient['dimension'][0] + $x + 1), ($gradient['dimension'][1] + $gradient['dimension'][3]), $color);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Vertical gradient.
|
||||
for ($y = 0; $y < $gradient['dimension'][3]; $y++) {
|
||||
$color = _color_blend($target, $palette[$gradient['colors'][0]], $palette[$gradient['colors'][1]], $y / ($gradient['dimension'][3] - 1));
|
||||
imagefilledrectangle($target, $gradient['dimension'][0], $gradient['dimension'][1] + $y, $gradient['dimension'][0] + $gradient['dimension'][2], $gradient['dimension'][1] + $y + 1, $color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Blend over template.
|
||||
imagecopy($target, $source, 0, 0, 0, 0, $width, $height);
|
||||
|
||||
// Clean up template image.
|
||||
imagedestroy($source);
|
||||
|
||||
// Cut out slices.
|
||||
foreach ($info['slices'] as $file => $coord) {
|
||||
list($x, $y, $width, $height) = $coord;
|
||||
$base = drupal_basename($file);
|
||||
$image = drupal_realpath($paths['target'] . $base);
|
||||
|
||||
// Cut out slice.
|
||||
if ($file == 'screenshot.png') {
|
||||
$slice = imagecreatetruecolor(150, 90);
|
||||
imagecopyresampled($slice, $target, 0, 0, $x, $y, 150, 90, $width, $height);
|
||||
variable_set('color_' . $theme . '_screenshot', $image);
|
||||
}
|
||||
else {
|
||||
$slice = imagecreatetruecolor($width, $height);
|
||||
imagecopy($slice, $target, 0, 0, $x, $y, $width, $height);
|
||||
}
|
||||
|
||||
// Save image.
|
||||
imagepng($slice, $image);
|
||||
imagedestroy($slice);
|
||||
$paths['files'][] = $image;
|
||||
|
||||
// Set standard file permissions for webserver-generated files
|
||||
drupal_chmod($image);
|
||||
|
||||
// Build before/after map of image paths.
|
||||
$paths['map'][$file] = $base;
|
||||
}
|
||||
|
||||
// Clean up target buffer.
|
||||
imagedestroy($target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts a given color, using a reference pair and a target blend color.
|
||||
*
|
||||
* Note: this function is significantly different from the JS version, as it
|
||||
* is written to match the blended images perfectly.
|
||||
*
|
||||
* Constraint: if (ref2 == target + (ref1 - target) * delta) for some fraction
|
||||
* delta then (return == target + (given - target) * delta).
|
||||
*
|
||||
* Loose constraint: Preserve relative positions in saturation and luminance
|
||||
* space.
|
||||
*/
|
||||
function _color_shift($given, $ref1, $ref2, $target) {
|
||||
// We assume that ref2 is a blend of ref1 and target and find
|
||||
// delta based on the length of the difference vectors.
|
||||
|
||||
// delta = 1 - |ref2 - ref1| / |white - ref1|
|
||||
$target = _color_unpack($target, TRUE);
|
||||
$ref1 = _color_unpack($ref1, TRUE);
|
||||
$ref2 = _color_unpack($ref2, TRUE);
|
||||
$numerator = 0;
|
||||
$denominator = 0;
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
$numerator += ($ref2[$i] - $ref1[$i]) * ($ref2[$i] - $ref1[$i]);
|
||||
$denominator += ($target[$i] - $ref1[$i]) * ($target[$i] - $ref1[$i]);
|
||||
}
|
||||
$delta = ($denominator > 0) ? (1 - sqrt($numerator / $denominator)) : 0;
|
||||
|
||||
// Calculate the color that ref2 would be if the assumption was true.
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
$ref3[$i] = $target[$i] + ($ref1[$i] - $target[$i]) * $delta;
|
||||
}
|
||||
|
||||
// If the assumption is not true, there is a difference between ref2 and ref3.
|
||||
// We measure this in HSL space. Notation: x' = hsl(x).
|
||||
$ref2 = _color_rgb2hsl($ref2);
|
||||
$ref3 = _color_rgb2hsl($ref3);
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
$shift[$i] = $ref2[$i] - $ref3[$i];
|
||||
}
|
||||
|
||||
// Take the given color, and blend it towards the target.
|
||||
$given = _color_unpack($given, TRUE);
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
$result[$i] = $target[$i] + ($given[$i] - $target[$i]) * $delta;
|
||||
}
|
||||
|
||||
// Finally, we apply the extra shift in HSL space.
|
||||
// Note: if ref2 is a pure blend of ref1 and target, then |shift| = 0.
|
||||
$result = _color_rgb2hsl($result);
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
$result[$i] = min(1, max(0, $result[$i] + $shift[$i]));
|
||||
}
|
||||
$result = _color_hsl2rgb($result);
|
||||
|
||||
// Return hex color.
|
||||
return _color_pack($result, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a hex triplet into a GD color.
|
||||
*/
|
||||
function _color_gd($img, $hex) {
|
||||
$c = array_merge(array($img), _color_unpack($hex));
|
||||
return call_user_func_array('imagecolorallocate', $c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blends two hex colors and returns the GD color.
|
||||
*/
|
||||
function _color_blend($img, $hex1, $hex2, $alpha) {
|
||||
$in1 = _color_unpack($hex1);
|
||||
$in2 = _color_unpack($hex2);
|
||||
$out = array($img);
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
$out[] = $in1[$i] + ($in2[$i] - $in1[$i]) * $alpha;
|
||||
}
|
||||
|
||||
return call_user_func_array('imagecolorallocate', $out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a hex color into an RGB triplet.
|
||||
*/
|
||||
function _color_unpack($hex, $normalize = FALSE) {
|
||||
if (strlen($hex) == 4) {
|
||||
$hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
|
||||
}
|
||||
$c = hexdec($hex);
|
||||
for ($i = 16; $i >= 0; $i -= 8) {
|
||||
$out[] = (($c >> $i) & 0xFF) / ($normalize ? 255 : 1);
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an RGB triplet to a hex color.
|
||||
*/
|
||||
function _color_pack($rgb, $normalize = FALSE) {
|
||||
$out = 0;
|
||||
foreach ($rgb as $k => $v) {
|
||||
$out |= (($v * ($normalize ? 255 : 1)) << (16 - $k * 8));
|
||||
}
|
||||
|
||||
return '#' . str_pad(dechex($out), 6, 0, STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an HSL triplet into RGB.
|
||||
*/
|
||||
function _color_hsl2rgb($hsl) {
|
||||
$h = $hsl[0];
|
||||
$s = $hsl[1];
|
||||
$l = $hsl[2];
|
||||
$m2 = ($l <= 0.5) ? $l * ($s + 1) : $l + $s - $l*$s;
|
||||
$m1 = $l * 2 - $m2;
|
||||
|
||||
return array(
|
||||
_color_hue2rgb($m1, $m2, $h + 0.33333),
|
||||
_color_hue2rgb($m1, $m2, $h),
|
||||
_color_hue2rgb($m1, $m2, $h - 0.33333),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for _color_hsl2rgb().
|
||||
*/
|
||||
function _color_hue2rgb($m1, $m2, $h) {
|
||||
$h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h);
|
||||
if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
|
||||
if ($h * 2 < 1) return $m2;
|
||||
if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (0.66666 - $h) * 6;
|
||||
|
||||
return $m1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an RGB triplet to HSL.
|
||||
*/
|
||||
function _color_rgb2hsl($rgb) {
|
||||
$r = $rgb[0];
|
||||
$g = $rgb[1];
|
||||
$b = $rgb[2];
|
||||
$min = min($r, min($g, $b));
|
||||
$max = max($r, max($g, $b));
|
||||
$delta = $max - $min;
|
||||
$l = ($min + $max) / 2;
|
||||
$s = 0;
|
||||
|
||||
if ($l > 0 && $l < 1) {
|
||||
$s = $delta / ($l < 0.5 ? (2 * $l) : (2 - 2 * $l));
|
||||
}
|
||||
|
||||
$h = 0;
|
||||
if ($delta > 0) {
|
||||
if ($max == $r && $max != $g) $h += ($g - $b) / $delta;
|
||||
if ($max == $g && $max != $b) $h += (2 + ($b - $r) / $delta);
|
||||
if ($max == $b && $max != $r) $h += (4 + ($r - $g) / $delta);
|
||||
$h /= 6;
|
||||
}
|
||||
|
||||
return array($h, $s, $l);
|
||||
}
|
133
modules/color/color.test
Normal file
133
modules/color/color.test
Normal file
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Tests for color module.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests the Color module functionality.
|
||||
*/
|
||||
class ColorTestCase extends DrupalWebTestCase {
|
||||
protected $big_user;
|
||||
protected $themes;
|
||||
protected $colorTests;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Color functionality',
|
||||
'description' => 'Modify the Bartik and Garland theme colors and make sure the changes are reflected on the frontend',
|
||||
'group' => 'Color',
|
||||
);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
parent::setUp('color');
|
||||
|
||||
// Create users.
|
||||
$this->big_user = $this->drupalCreateUser(array('administer themes'));
|
||||
|
||||
// This tests the color module in both Bartik and Garland.
|
||||
$this->themes = array(
|
||||
'bartik' => array(
|
||||
'palette_input' => 'palette[bg]',
|
||||
'scheme' => 'slate',
|
||||
'scheme_color' => '#3b3b3b',
|
||||
),
|
||||
'garland' => array(
|
||||
'palette_input' => 'palette[link]',
|
||||
'scheme' => 'greenbeam',
|
||||
'scheme_color' => '#0c7a00',
|
||||
),
|
||||
);
|
||||
theme_enable(array_keys($this->themes));
|
||||
|
||||
// Array filled with valid and not valid color values
|
||||
$this->colorTests = array(
|
||||
'#000' => TRUE,
|
||||
'#123456' => TRUE,
|
||||
'#abcdef' => TRUE,
|
||||
'#0' => FALSE,
|
||||
'#00' => FALSE,
|
||||
'#0000' => FALSE,
|
||||
'#00000' => FALSE,
|
||||
'123456' => FALSE,
|
||||
'#00000g' => FALSE,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the Color module functionality.
|
||||
*/
|
||||
function testColor() {
|
||||
foreach ($this->themes as $theme => $test_values) {
|
||||
$this->_testColor($theme, $test_values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the Color module functionality using the given theme.
|
||||
*/
|
||||
function _testColor($theme, $test_values) {
|
||||
variable_set('theme_default', $theme);
|
||||
$settings_path = 'admin/appearance/settings/' . $theme;
|
||||
|
||||
$this->drupalLogin($this->big_user);
|
||||
$this->drupalGet($settings_path);
|
||||
$this->assertResponse(200);
|
||||
$edit['scheme'] = '';
|
||||
$edit[$test_values['palette_input']] = '#123456';
|
||||
$this->drupalPost($settings_path, $edit, t('Save configuration'));
|
||||
|
||||
$this->drupalGet('<front>');
|
||||
$stylesheets = variable_get('color_' . $theme . '_stylesheets', array());
|
||||
$this->assertPattern('|' . file_create_url($stylesheets[0]) . '|', 'Make sure the color stylesheet is included in the content. (' . $theme . ')');
|
||||
|
||||
$stylesheet_content = join("\n", file($stylesheets[0]));
|
||||
$this->assertTrue(strpos($stylesheet_content, 'color: #123456') !== FALSE, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')');
|
||||
|
||||
$this->drupalGet($settings_path);
|
||||
$this->assertResponse(200);
|
||||
$edit['scheme'] = $test_values['scheme'];
|
||||
$this->drupalPost($settings_path, $edit, t('Save configuration'));
|
||||
|
||||
$this->drupalGet('<front>');
|
||||
$stylesheets = variable_get('color_' . $theme . '_stylesheets', array());
|
||||
$stylesheet_content = join("\n", file($stylesheets[0]));
|
||||
$this->assertTrue(strpos($stylesheet_content, 'color: ' . $test_values['scheme_color']) !== FALSE, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')');
|
||||
|
||||
// Test with aggregated CSS turned on.
|
||||
variable_set('preprocess_css', 1);
|
||||
$this->drupalGet('<front>');
|
||||
$stylesheets = variable_get('drupal_css_cache_files', array());
|
||||
$stylesheet_content = '';
|
||||
foreach ($stylesheets as $key => $uri) {
|
||||
$stylesheet_content .= join("\n", file(drupal_realpath($uri)));
|
||||
}
|
||||
$this->assertTrue(strpos($stylesheet_content, 'public://') === FALSE, 'Make sure the color paths have been translated to local paths. (' . $theme . ')');
|
||||
variable_set('preprocess_css', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the provided color is valid.
|
||||
*/
|
||||
function testValidColor() {
|
||||
variable_set('theme_default', 'bartik');
|
||||
$settings_path = 'admin/appearance/settings/bartik';
|
||||
|
||||
$this->drupalLogin($this->big_user);
|
||||
$edit['scheme'] = '';
|
||||
|
||||
foreach ($this->colorTests as $color => $is_valid) {
|
||||
$edit['palette[bg]'] = $color;
|
||||
$this->drupalPost($settings_path, $edit, t('Save configuration'));
|
||||
|
||||
if ($is_valid) {
|
||||
$this->assertText('The configuration options have been saved.');
|
||||
}
|
||||
else {
|
||||
$this->assertText('Main background must be a valid hexadecimal CSS color value.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
modules/color/images/hook-rtl.png
Normal file
BIN
modules/color/images/hook-rtl.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 116 B |
BIN
modules/color/images/hook.png
Normal file
BIN
modules/color/images/hook.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 116 B |
BIN
modules/color/images/lock.png
Normal file
BIN
modules/color/images/lock.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 230 B |
7
modules/color/preview.html
Normal file
7
modules/color/preview.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
<div id="preview">
|
||||
<div id="text">
|
||||
<h2>Lorem ipsum dolor</h2>
|
||||
<p>Sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud <a href="#">exercitation ullamco</a> laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
|
||||
</div>
|
||||
<div id="img"></div>
|
||||
</div>
|
38
modules/color/preview.js
Normal file
38
modules/color/preview.js
Normal file
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* @file
|
||||
* Attaches preview-related behavior for the Color module.
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
Drupal.color = {
|
||||
callback: function(context, settings, form, farb, height, width) {
|
||||
// Solid background.
|
||||
$('#preview', form).css('backgroundColor', $('#palette input[name="palette[base]"]', form).val());
|
||||
|
||||
// Text preview
|
||||
$('#text', form).css('color', $('#palette input[name="palette[text]"]', form).val());
|
||||
$('#text a, #text h2', form).css('color', $('#palette input[name="palette[link]"]', form).val());
|
||||
|
||||
// Set up gradients if there are some.
|
||||
var color_start, color_end;
|
||||
for (i in settings.gradients) {
|
||||
color_start = farb.unpack($('#palette input[name="palette[' + settings.gradients[i]['colors'][0] + ']"]', form).val());
|
||||
color_end = farb.unpack($('#palette input[name="palette[' + settings.gradients[i]['colors'][1] + ']"]', form).val());
|
||||
if (color_start && color_end) {
|
||||
var delta = [];
|
||||
for (j in color_start) {
|
||||
delta[j] = (color_end[j] - color_start[j]) / (settings.gradients[i]['vertical'] ? height[i] : width[i]);
|
||||
}
|
||||
var accum = color_start;
|
||||
// Render gradient lines.
|
||||
$('#gradient-' + i + ' > div', form).each(function () {
|
||||
for (j in accum) {
|
||||
accum[j] += delta[j];
|
||||
}
|
||||
this.style.backgroundColor = farb.pack(accum);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
Loading…
Add table
Add a link
Reference in a new issue