Index: fivestar-admin.css =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/fivestar/Attic/fivestar-admin.css,v retrieving revision 1.1.2.1 diff -u -r1.1.2.1 fivestar-admin.css --- fivestar-admin.css 10 Mar 2008 01:44:41 -0000 1.1.2.1 +++ fivestar-admin.css 12 Mar 2008 05:36:08 -0000 @@ -1,5 +1,5 @@ /* Fivestar Settings Preview */ -iframe.fivestar-preview { +div.fivestar-star-preview { border: none; width: 200px; height: 32px; @@ -25,3 +25,71 @@ border: 1px solid #CCC; padding: 10px; } + +/* Farbtastic placement */ +.color-form { + max-width: 50em; + position: relative; +} +#placeholder { + position: absolute; + top: 0; + right: 0; +} + +/* Palette */ +.color-form .form-item { + height: 2em; + line-height: 2em; + padding-left: 1em; + margin: 0.5em 0; +} +.color-form label { + float: left; + clear: left; + width: 10em; +} +.color-form .form-text, .color-form .form-select { + float: left; +} +.color-form .form-text { + text-align: center; + margin-right: 5px; + cursor: pointer; +} + +#palette .hook { + float: left; + margin-top: 3px; + width: 16px; + height: 16px; +} +#palette .down, #palette .up, #palette .both { + background: url(images/hook.png) no-repeat 100% 0; +} +#palette .up { + background-position: 100% -27px; +} +#palette .both { + background-position: 100% -54px; +} + +#palette .lock { + float: left; + position: relative; + top: -1.4em; + left: -10px; + 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; +} Index: fivestar.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/fivestar/fivestar.module,v retrieving revision 1.2.2.73 diff -u -r1.2.2.73 fivestar.module --- fivestar.module 10 Mar 2008 06:24:07 -0000 1.2.2.73 +++ fivestar.module 12 Mar 2008 05:36:09 -0000 @@ -37,8 +37,8 @@ 'access' => user_access('administer site configuration'), ); $items[] = array( - 'path' => 'fivestar/preview/widget', - 'callback' => 'fivestar_preview_page', + 'path' => 'fivestar/preview/color', + 'callback' => 'fivestar_preview_color', 'type' => MENU_CALLBACK, 'access' => user_access('administer site configuration'), ); @@ -374,14 +374,49 @@ */ function fivestar_settings() { $form = array(); - $form['fivestar_widget'] = array( - '#type' => 'radios', + + $form['widget'] = array( + '#tree' => FALSE, + '#type' => 'fieldset', '#title' => t('Widget display'), - '#options' => array('default' => t('Default')) + module_invoke_all('fivestar_widgets'), + '#description' => t('Choose a widget set to be used on your site. Classic widgets will be displayed exactly as shown, while widgets supporting custom colors can be further customized using the color settings below.'), + '#weight' => -2, + ); + + $widgets = module_invoke_all('fivestar_widgets'); + $classic_widgets = array(); + $color_widgets = array(); + foreach ($widgets as $path => $name) { + $directory = dirname($path); + $matches = file_scan_directory($directory, '-mask.'); + if (empty($matches)) { + $classic_widgets[$path] = $name; + } + else { + $color_widgets[$path] = $name; + } + } + $form['widget']['fivestar_widget'] = array( + '#type' => 'radios', + '#title' => t('Classic widgets'), + '#options' => array('default' => t('Default')) + $classic_widgets, '#default_value' => variable_get('fivestar_widget', 'default'), - '#description' => t('Choose a widget set to be used on your site.'), - '#attributes' => array('class' => 'fivestar-widgets clear-block'), + '#attributes' => array('class' => 'fivestar-widgets'), ); + + $form['widget']['fivestar_color_widget'] = array( + '#type' => 'radios', + '#title' => t('Custom color widgets'), + '#options' => $color_widgets, + '#default_value' => variable_get('fivestar_widget', 'default'), + '#attributes' => array('class' => 'fivestar-widgets'), + // Put these radios into the same group as the classic widgets. + '#parents' => array('fivestar_widget'), + ); + + include_once(drupal_get_path('module', 'fivestar') .'/fivestar_color.inc'); + $form['color'] = fivestar_color_form(); + $form['fivestar_anonymous_vote_interval'] = array( '#type' => 'select', '#title' => t('Anonymous vote interval'), @@ -393,43 +428,61 @@ } function theme_fivestar_settings($form) { - fivestar_add_css(); drupal_add_css(drupal_get_path('module', 'fivestar') .'/fivestar-admin.css', 'module', 'all', FALSE); drupal_set_title(t('Fivestar Settings')); // Default preview. - $form['fivestar_widget']['default']['#description'] = 'Default '. t('Preview') .':
'; + $form['widget']['fivestar_widget']['default']['#description'] = 'Default '. t('Preview') .':
'. theme('fivestar_preview_widget', 'default'); - // Preview for each widget. + // Preview for each classic widget. $widget_number = 0; - foreach (element_children($form['fivestar_widget']) as $widget_key) { + foreach (element_children($form['widget']['fivestar_widget']) as $widget_key) { if ($widget_key != 'default') { - $form['fivestar_widget'][$widget_key]['#description'] = $form['fivestar_widget'][$widget_key]['#title'] .' '. t('Preview') .':
'; + $form['widget']['fivestar_widget'][$widget_key]['#description'] = $form['widget']['fivestar_widget'][$widget_key]['#title'] .' '. t('Preview') .':
'. theme('fivestar_preview_widget', $widget_key); $widget_number++; } } - return drupal_render($form); -} -/** - * Callback function for fivestar/preview/widget. Outputs an entire page - * containing a preview of the passed in fivestar widget format. - */ -function fivestar_preview_page($widget_number = NULL) { - $widgets = module_invoke_all('fivestar_widgets'); - $css_files = array_keys($widgets); - if (isset($css_files[$widget_number])) { - fivestar_add_css($css_files[$widget_number]); - } - else { - fivestar_add_css('default'); + // Preview for each color-enabled widget. + $widget_number = 0; + foreach (element_children($form['widget']['fivestar_color_widget']) as $widget_key) { + if ($widget_key != 'default') { + $form['widget']['fivestar_color_widget'][$widget_key]['#description'] = $form['widget']['fivestar_color_widget'][$widget_key]['#title'] .' '. t('Preview') .':
'. theme('fivestar_preview_widget', $widget_key); + $widget_number++; + } } - fivestar_add_js(); - print theme('fivestar_preview_page'); - exit; + + $form['widget']['fivestar_widget']['#attributes']['class'] .= ' clear-block'; + $form['widget']['fivestar_color_widget']['#attributes']['class'] .= ' fivestar-color-widgets clear-block'; + + return drupal_render($form); } -function theme_fivestar_preview_page() { +function theme_fivestar_preview_widget($css_file) { + static $default_css_added = FALSE; + + // Add the default CSS to the page to ensure the defaults take precedence. + if (!$default_css_added) { + $css = file_get_contents(drupal_get_path('module', 'fivestar') .'/fivestar.css'); + // Prepend the classes with the unique widget div. + $css = preg_replace('/((div)?\.fivestar-widget)/', 'div.fivestar-widgets $1', $css); + // Update relative URLs with absolute locations. + $css = preg_replace('/url\((.*?)\)/', 'url('. base_path() . drupal_get_path('module', 'fivestar') .'/$1)', $css); + drupal_set_html_head(""); + $default_css_added = TRUE; + } + + // Add widget specific CSS to the page. + $widget_name = str_replace('.css', '', basename($css_file)); + $widget_path = dirname($css_file); + if ($widget_name != 'default') { + $css = file_get_contents($css_file); + // Prepend the classes with the unique widget div. + $css = preg_replace('/((div)?\.fivestar-widget)/', 'div#fivestar-preview-'. $widget_name .' $1', $css); + // Update relative URLs with absolute locations. + $css = preg_replace('/url\((.*?)\)/', 'url('. base_path() . $widget_path .'/$1)', $css); + drupal_set_html_head(''); + } $form = array(); $form['vote'] = array( @@ -441,22 +494,52 @@ $form = form_builder('fivestar_preview', $form); - $output = "\n"; - $output .= ''; - $output .= ''; - $output .= ' '. t('Fivestar Preview') .''; - $output .= drupal_get_html_head(); - $output .= drupal_get_css(); - $output .= drupal_get_js(); - $output .= ' '; - $output .= ' '; + $output = '
'; $output .= drupal_render($form); - $output .= ''; + $output .= '
'; return $output; } /** + * Callback function for fivestar/preview/color. + * + * Outputs a dynamically generated star or cancel png. + */ +function fivestar_preview_color() { + include_once(drupal_get_path('module', 'fivestar') .'/fivestar_color.inc'); + $args = func_get_args(); + $filename = array_pop($args); + $widget = array_pop($args); + + // Kinda a horrible hack at the moment. Put the color scheme into + // off, on, hover order. + $color_scheme = array( + 'off1' => $args[4], + 'off2' => $args[5], + 'on1' => $args[0], + 'on2' => $args[1], + 'hover1' => $args[2], + 'hover2' => $args[3], + ); + $color_scheme = array_values($color_scheme); + + + $widgets = module_invoke_all('fivestar_widgets'); + foreach ($widgets as $key => $name) { + if (drupal_strtolower($name) == $widget) { + $source = str_replace($widget .'.css', '', $key); + break; + } + } + + $image = _fivestar_color_render($source . $filename, $color_scheme); + drupal_set_header('Content-type: image/png'); + imagepng($image); + exit(); +} + +/** * Callback function for fivestar/preview/node. Outputs a JSON page containing * a Fivestar preview of a node rating widget. */ @@ -682,7 +765,7 @@ $widgets = array(); foreach ($files as $file) { - $widgets[$file->filename] = drupal_ucfirst($file->name); + $widgets[$file->filename] = drupal_ucfirst(str_replace('-color', '', $file->name)); } return $widgets; } Index: widgets/outline/outline.css =================================================================== RCS file: widgets/outline/outline.css diff -N widgets/outline/outline.css --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ widgets/outline/outline.css 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,27 @@ +/* Static View-only Star Version */ +div.fivestar-widget-static .star { + background-image: url(star.png); +} + +div.fivestar-widget-static .star span.on { + background-image: url(star.png); +} + +div.fivestar-widget-static .star span.off { + background-image: url(star.png); +} + +/* Javascript Star Version */ +div.fivestar-widget .cancel, +div.fivestar-widget .cancel a { + background-image: url(cancel.png); +} +div.fivestar-widget .star, +div.fivestar-widget .star a { + background-image: url(star.png); +} + +div.fivestar-widget div.hover a, +div.rating div a:hover { + background-image: url(star.png); +} Index: widgets/outline/cancel.png =================================================================== RCS file: widgets/outline/cancel.png diff -N widgets/outline/cancel.png --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ widgets/outline/cancel.png 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,9 @@ +PNG + + +IHDR tEXtSoftwareAdobe ImageReadyqe<IDATxܕKQg2ErQуVAZHZD"dzPPDA"AR$!)[DRQK|4N9s="WUUxUlP-x<yjy_T] chd\.hh4kNӀ|?Le +j0s8F}w3f`\}og+7L?;GTIENDB` Index: fivestar-color.js =================================================================== RCS file: fivestar-color.js diff -N fivestar-color.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fivestar-color.js 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,243 @@ +// $Id$ + +if (Drupal.jsEnabled) { + $(document).ready(function () { + var form = $('#fivestar_color_scheme_form .color-form'); + var inputs = []; + var hooks = []; + var locks = []; + var focused = null; + var radios = document.forms['fivestar-settings']['fivestar_widget']; + var colorRadios = $('.fivestar-color-widgets input', radios); + + // Add Farbtastic + $(form).prepend('
'); + var farb = $.farbtastic('#placeholder'); + + // Decode reference colors to HSL + var reference = Drupal.settings.fivestar.reference; + for (i in reference) { + reference[i] = farb.RGBToHSL(farb.unpack(reference[i])); + } + + // Set up colorscheme selector + $('#edit-scheme', form).change(function () { + var colors = this.options[this.selectedIndex].value; + if (colors != '') { + colors = colors.split(','); + for (i in colors) { + callback(inputs[i], colors[i], false, true); + } + preview(); + } + }); + + // Setup radio buttons. + $(radios).change(function() { + $(colorRadios).each(function() { + var widgetName = this.value.replace(/.*?\/([^\/]+)\.css/, '$1'); + var fivestarWidget = document.getElementById('fivestar-preview-' + widgetName); + $('.star, .star a, .cancel, .cancel a', fivestarWidget).css('background-image', ''); + }); + preview(); + }); + + /** + * Render the preview. + */ + function preview() { + var current = $(colorRadios).filter(':checked').val(); + if (!current) { + return; + } + + var widgetName = current.replace(/.*?\/([^\/]+)\.css/, '$1'); + var fivestarWidget = document.getElementById('fivestar-preview-' + widgetName); + var inputValues = new Array(); + for (var n in inputs) { + inputValues.push(inputs[n].value.replace(/#/, '')); + } + + // Star images. + var time = new Date(); + $('.star, .star a', fivestarWidget).css('background-image', 'url(' + Drupal.settings.fivestar.colorPreview + '/' + inputValues.join('/') + '/' + widgetName + '/star.png?o=' + time.getTime() + ')'); + $('.cancel, .cancel a', fivestarWidget).css('background-image', 'url(' + Drupal.settings.fivestar.colorPreview + '/' + inputValues.join('/') + '/' + widgetName + '/cancel.png?o=' + time.getTime() + ')'); + + // Fix preview background in IE6 + if (navigator.appVersion.match(/MSIE [0-6]\./)) { + $('.star, .star a, .cancel, .cancel a').each(function(element) { + var image = element.currentStyle.backgroundImage; + element.style.backgroundImage = 'none'; + element.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image.substring(5, image.length - 2) + "')"; + }); + } + } + + /** + * Shift 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) { + // Set background/foreground color + $(input).css({ + backgroundColor: color, + color: farb.RGBToHSL(farb.unpack(color))[2] > 0.5 ? '#000' : '#fff' + }); + + // Change input value + if (input.value && input.value != color) { + input.value = color; + + // Update locked values + if (propagate) { + var i = input.i; + for (j = i + 1; ; ++j) { + if (!locks[j - 1] || $(locks[j - 1]).is('.unlocked')) break; + var 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; + var 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(); + } + } + + } + + /** + * Reset the color scheme selector. + */ + function resetScheme() { + $('#edit-scheme', form).each(function () { + this.selectedIndex = this.options.length - 1; + }); + } + + // Focus the 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 = $('
').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 = $('
'); + $(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(); + }); +} Index: widgets/outline/star.png =================================================================== RCS file: widgets/outline/star.png diff -N widgets/outline/star.png --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ widgets/outline/star.png 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,10 @@ +PNG + + +IHDR0_WtEXtSoftwareAdobe ImageReadyqe< ?NF''4f@ e1.G_ &>/ۀDO +rIENDB` Index: widgets/outline/star-mask.png =================================================================== RCS file: widgets/outline/star-mask.png diff -N widgets/outline/star-mask.png --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ widgets/outline/star-mask.png 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,4 @@ +PNG + + +IHDR0_WtEXtSoftwareAdobe ImageReadyqe<IDATxb?%Rfx0&K je8@M # ?@ & HWB3 A a%l.k 0c11@iP;$~f N-$$N<^$5/Έހ`<-F˃`<-H,(npڨIENDB` Index: widgets/outline/cancel-mask.png =================================================================== RCS file: widgets/outline/cancel-mask.png diff -N widgets/outline/cancel-mask.png --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ widgets/outline/cancel-mask.png 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,4 @@ +PNG + + +IHDR tEXtSoftwareAdobe ImageReadyqe<IDATxb?%B00@'13:z4@j f  V!d@T1 ?GW2&i.b=47`aBd? CЂ<B@|(/(1aęhɥ[0"g&FFF2`'9Br#"9xBhy0Z `؞CnIENDB` Index: images/hook.png =================================================================== RCS file: images/hook.png diff -N images/hook.png --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ images/hook.png 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,5 @@ +PNG + + +IHDRQPtIME +%Ù,PLTEDDDtRNS@f!IDAT(c` 0BO\{yjyy2P5,hIENDB` Index: fivestar_color.inc =================================================================== RCS file: fivestar_color.inc diff -N fivestar_color.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fivestar_color.inc 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,389 @@ + array('fivestar_color_form_submit'), + '#tree' => FALSE, + '#theme' => 'fivestar_color_form', + '#type' => 'fieldset', + '#title' => t('Color scheme'), + '#weight' => -1, + '#attributes' => array('id' => 'fivestar_color_scheme_form'), + '#description' => t('If you\'ve selected a Custom color widget above, you can futher customize your widget by choosing colors below.'), + ); + + $theme = variable_get('theme_default', 'garland'); + $theme_info = color_get_info($theme); + $themes = list_themes(); + $theme_name = $themes[$theme]->name; + + // Create the list of available schemes. + $fivestar_schemes = array( + '#ffeb38,#fff385,#ffd60a,#ffe561,#b3b3b3,#e6e6e6' => t('Yellow'), + '#ff383a,#ff5f61,#ef9a9c,#f3b3b4,#b3b3b3,#e6e6e6' => t('Red'), + '#5b6df1,#7c8af4,#1022c1,#2e41ee,#b3b3b3,#e6e6e6' => t('Blue'), + '#5cf15b,#7cf47c,#12c610,#30ee2e,#b3b3b3,#e6e6e6' => t('Green'), + ); + $theme_schemes = array(); + if (!empty($theme_info['schemes'])) { + foreach ($theme_info['schemes'] as $key => $name) { + $colors = explode(',', $key); + $theme_scheme = array( + 'on1' => $colors[0], + 'on2' => _fivestar_color_blend($colors[0], '#FFFFFF', .8), + 'hover1' => $colors[2], + 'hover2' => _fivestar_color_blend($colors[2], '#FFFFFF', .8), + 'off1' => $colors[4], + 'off2' => _fivestar_color_blend($colors[4], '#FFFFFF', .8), + ); + $theme_schemes[implode(',', $theme_scheme)] = $name; + } + } + + if (!empty($theme_schemes)) { + $options = array( + t('Fivestar schemes') => $fivestar_schemes, + t('@theme_name schemes', array('@theme_name' => drupal_ucfirst($theme_name))) => $theme_schemes, + ); + } + else { + $options = $fivestar_schemes; + } + $options[''] = t('Custom'); + + // See if we're using a predefined scheme. + $current = implode(',', variable_get('fivestar_color_palette', explode(',', array_search(t('Yellow'), $fivestar_schemes)))); + + // Add scheme selector. + $form['scheme'] = array( + '#type' => 'select', + '#title' => t('Color set'), + '#options' => $options, + '#default_value' => $current, + ); + + // Add palette fields. + $names = array( + 'on1' => t('On 1 color'), + 'on2' => t('On 2 color'), + 'hover1' => t('Hover 1 color'), + 'hover2' => t('Hover 2 color'), + 'off1' => t('Off 1 color'), + 'off2' => t('Off 2 color'), + ); + + $default_colors = explode(',', $current); + $color_scheme = variable_get('fivestar_colors', $default_colors); + + $form['palette']['#tree'] = true; + $form['palette']['#theme'] = 'fivestar_color_palette'; + $n = 0; + foreach ($names as $key => $name) { + $form['palette'][$key] = array( + '#type' => 'textfield', + '#title' => $name, + '#default_value' => $color_scheme[$n], + '#size' => 8, + ); + $n++; + } + + return $form; +} + +/** + * Theme color form. + */ +function theme_fivestar_color_form($form) { + // Add Farbtastic color picker. + drupal_add_css(drupal_get_path('module', 'color') .'/fivestar-admin.css', 'module', 'all', FALSE); + drupal_add_css('misc/farbtastic/farbtastic.css', 'module', 'all', FALSE); + drupal_add_js(drupal_get_path('module', 'fivestar') .'/fivestar-color.js'); + drupal_add_js('misc/farbtastic/farbtastic.js'); + + // Add custom CSS/JS. + $default_colors = array(); + foreach (element_children($form['palette']) as $key) { + $default_colors[$key] = $form['palette'][$key]['#value']; + } + $color_scheme = variable_get('fivestar_colors', $default_colors); + drupal_add_js(array('fivestar' => array('reference' => $color_scheme, 'colorPreview' => url('fivestar/preview/color'))), 'setting'); + + // Wrapper. + $output .= '
'; + + // Color schemes. + $output .= drupal_render($form['scheme']); + + // Palette. + $output .= '
'; + foreach (element_children($form['palette']) as $name) { + $output .= drupal_render($form['palette'][$name]); + } + $output .= '
'; + + // Render the form. + $output .= drupal_render($form); + // Close wrapper. + $output .= '
'; + + return $output; +} + +/** + * Submit handler for color change form. + */ +function fivstar_color_form_submit($form_id, $values) { + + // Render new images + //_color_render_images($star_set, $palette); + + // Rewrite stylesheet + //_color_rewrite_stylesheet($star_set, $palette); + +} + +/** + * Rewrite the stylesheet to match the colors in the palette. + */ +function _fivestar_color_rewrite_stylesheet($theme, $palette) { + /* + // Load stylesheet + $style = file_get_contents($paths['source'] .'style.css'); + + // Write new stylesheet + $file = fopen($paths['stylesheet'], 'w+'); + fwrite($file, $output); + fclose($file); + $paths['files'][] = $paths['stylesheet']; + + // Set standard file permissions for webserver-generated files + @chmod($paths['stylesheet'], 0664); + */ +} + +/** + * Render images that match a given palette. + * @param $source + * The original image source. A matching mask image must be at the same + * location. + * @param $palette + * The colors to be used in the generation of this image. + * 0: on1 + * 1: on2 + * 2: hover1 + * 3: hover2 + * 4: off1 + * 5: off2 + */ +function _fivestar_color_render($source, $palette) { + + // Prepare template image. + $overlay = imagecreatefrompng($source); + imagealphablending($overlay, true); + imagesavealpha($overlay, true); + + $mask = imagecreatefrompng(str_replace('.png', '-mask.png', $source)); + imagealphablending($mask, true); + imagesavealpha($mask, true); + + $width = imagesx($overlay); + $height = imagesy($overlay); + + // Apply color mask. + $slices = (basename($source) == 'star.png') ? 3 : 2; + $slice_height = floor($height / $slices); + + for ($n = 0; $n < $slices; $n++) { + $slice_y = $slice_height * $n; + _fivestar_color_mask($mask, $palette[$n * 2], 0, $slice_y, $width, $slice_height); + } + + // Apply the overlay on top of the mask. + imagecopy($mask, $overlay, 0, 0, 0, 0, $width, $height); + + return $mask; +} + +/** + * Apply a color to a portion of a black and white mask image. + * + * @param $mask + * A GD image reference containing the mask image. + * @param $color + * A hex color value (i.e. ffdd00) to apply to the mask. + */ +function _fivestar_color_mask(&$mask, $color, $x_offset, $y_offset, $width, $height) { + $rgb = _fivestar_color_unpack($color); + for ($x = $x_offset; $x < $x_offset + $width; $x++) { + for ($y = $y_offset; $y < $y_offset + $height; $y++) { + $current_pixel = imagecolorsforindex($mask, imagecolorat($mask, $x, $y)); + $new_color = imagecolorallocatealpha($mask, $rgb[0], $rgb[1], $rgb[2], $current_pixel['alpha']); + imagesetpixel($mask, $x, $y, $new_color); + } + } +} + +/** + * Shift 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. + * @param $given + * Hex color value. + * @param $ref1 + * Hex color value. + * @param $ref2 + * Hex color value. + * @param $target + * Hex color value. + */ +function _fivestar_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); + 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 = _fivestar_color_rgb2hsl($ref2); + $ref3 = _fivestar_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 = _fivestar_color_rgb2hsl($result); + for ($i = 0; $i < 3; ++$i) { + $result[$i] = min(1, max(0, $result[$i] + $shift[$i])); + } + $result = _fivestar_color_hsl2rgb($result); + + // Return hex color. + return _fivestar_color_pack($result, true); +} + +/** + * Convert a hex triplet into a GD color. + */ +function _fivestar_color_gd($img, $hex) { + $c = array_merge(array($img), _color_unpack($hex)); + return call_user_func_array('imagecolorallocate', $c); +} + +/** + * Blend two hex colors and return the resulting hex color. + */ +function _fivestar_color_blend($hex1, $hex2, $alpha) { + $in1 = _fivestar_color_unpack($hex1); + $in2 = _fivestar_color_unpack($hex2); + for ($i = 0; $i < 3; ++$i) { + $out[] = $in1[$i] + ($in2[$i] - $in1[$i]) * $alpha; + } + return _fivestar_color_pack($out); +} + +/** + * Convert a hex color into an RGB triplet. + */ +function _fivestar_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; +} + +/** + * Convert an RGB triplet to a hex color. + */ +function _fivestar_color_pack($rgb, $normalize = false) { + foreach ($rgb as $k => $v) { + $out |= (($v * ($normalize ? 255 : 1)) << (16 - $k * 8)); + } + return '#'. str_pad(dechex($out), 6, 0, STR_PAD_LEFT); +} + +/** + * Convert a HSL triplet into RGB + */ +function _fivestar_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(_fivestar_color_hue2rgb($m1, $m2, $h + 0.33333), + _fivestar_color_hue2rgb($m1, $m2, $h), + _fivestar_color_hue2rgb($m1, $m2, $h - 0.33333)); +} + +/** + * Helper function for _color_hsl2rgb(). + */ +function _fivestar_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; +} + +/** + * Convert an RGB triplet to HSL. + */ +function _fivestar_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); +} Index: images/lock.png =================================================================== RCS file: images/lock.png diff -N images/lock.png --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ images/lock.png 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,7 @@ +PNG + + +IHDR +26tIME ++a'PLTEaaaxvg-)=tRNS@foIDAT( + @̚YI~DeB[FUILn#&/e*- VYZ_e\QoE}G{;IENDB`