Color Picker macro, useful for CSS

Posted by toddw on 2007-02-28 12:03

I demonstrated this color picker JavaScript macro at PyCon 2007.

Uses the system color picker (or defaults to a basic color chart popup) that will on selection create a hexadecimal color value (like #FFCC77), which is useful in HTML and CSS documents. You can also select a css color of the form "#123ABC" and trigger this macro to initialize the color picker with this value.

Linux screenshot (Note: Win and Mac color pickers do look better :)

Installation

  • Create a "New Macro" in the Komodo toolbox
  • In the macro dialog, set the language to "JavaScript" and paste in the macro code contents below
  • In the macro dialog, select the "Key Bindings" tab and you can assign a keybinding to it (i.e. "Ctrl-/" for example)

To trigger the macro whilst your editing a file, just use the keybinding sequence you've assigned to the macro and the dialog should appear. Note: On a Windows or Mac computer, the color picker should use the OS system color picker.

/*
 * JavaScript macro to provide a basic color picker for hexadecimal colors.
 * Assign a useful keybinding to this macro and ka-zam, funky color picking!
 *
 * Version: 1.3
 *
 * Authored by: David Ascher
 * Modified by: Shane Caraveo
 *              Todd Whiteman
 *              Conor Kerr
 *              Michal Ko?árek
 *
 * [ADDED] 1.3: Color picker parses named colors from selection as well.
 * [ADDED] 1.2: Support for pre-selected colours without a hash and 3 character
 *              colour codes added.
 */

function system_colorpicker(color) {
    var sysUtils = Components.classes['@activestate.com/koSysUtils;1'].
                    getService(Components.interfaces.koISysUtils);
    if (!color)
        color = "#000000";
   
    // If color is not in hexa format (#XXXXXX, #XXX with/without hash)
    // convert the color to hexa format before passing it to color picker
    if (!/^#?[\da-f]{3}(?:[\da-f]{3})?$/i.test(color)) {
        var span = document.createElement('span');
        span.style.color = color;
       
        var color_rgb = window.getComputedStyle(span, null).color;
        color = rgb2hex(color_rgb);
        delete span;
    }
   
    // Make sure selected colour is in correct format (preceded by hash) before passing to dialog
    var hash_added = false;
    if (color.charAt(0) != '#') {
        color = '#' + color;
        hash_added = true;
    }
   
    // Handle 3 character (shorthand) colour representations
    if (color.length == 4) {
        color = color.substr(0, 2) + color.charAt(1) + color.charAt(2) +
                color.charAt(2) + color.charAt(3) + color.charAt(3);
    }

    newcolor = sysUtils.pickColor(color);
    if (newcolor) {
       var scimoz = ko.views.manager.currentView.scimoz;
       // Remove any hash added to the selection
       if (hash_added) {
           newcolor = newcolor.substr(1, newcolor.length - 1);
       }

       scimoz.replaceSel(newcolor);
       scimoz.anchor = scimoz.currentPos;
    }
}

function colorpicker_onchange(event, cp) {
    var scimoz = ko.views.manager.currentView.scimoz;
    scimoz.insertText(scimoz.currentPos, cp.color);
    // Move cursor position to end of the inserted color
    // Note: currentPos is a byte offset, so we need to corrext the length
    var newCurrentPos = scimoz.currentPos + ko.stringutils.bytelength(cp.color);
    scimoz.currentPos = newCurrentPos;
    // Move the anchor as well, so we don't have a selection
    scimoz.anchor = newCurrentPos;
    // for some reason we get the event twice, removing
    // onselect fixes the problem.  Tried to solve it
    // by canceling the event below, but it went on anyway
    cp.removeAttribute('onselect');
    cp.parentNode.hidePopup();

    event.preventDefault();
    event.stopPropagation();
    event.cancelBubble = true;
    remove_colorpicker();
}

function remove_colorpicker() {
    // remove the popup from the document.  this cleans up so
    // we can change the macro code if needed
    var p = document.getElementById('popup_colorpicker');
    if (p)
        p.parentNode.removeChild(p);
}

function init_colorpicker() {
    remove_colorpicker();
    var p = document.createElement('popup');
    p.setAttribute('id', 'popup_colorpicker');
    var cp = document.createElement('colorpicker');
    cp.colorChanged = colorpicker_onchange;
    cp.setAttribute('onselect', 'this.colorChanged(event, this);');
    p.appendChild(cp);
    document.documentElement.appendChild(p);
}

/**
 * Convert CSS color representation from rgb(...) string
 * to the hexa format with hash on beginning.
 * @param color {String} Color in rgb(...) css format
 * @returns {String} Color in hexa string (#xxxxxx) (Falls back to black color)
 */

function rgb2hex(color) {
    var match = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/i);
    if (!match)
        return "#000000";
    match.shift();
   
    for(var i = 0; i < match.length; ++i) {
        match[i] = (match[i] < 16 ? '0' : '')
            + parseInt(match[i]).toString(16).toLowerCase();
    }
   
    match = '#' + match.join('');
    return match;
}

var currentView = ko.views.manager.currentView;
if (currentView) {
    currentView.scintilla.focus();
    var os_prefix = window.navigator.platform.substring(0, 3).toLowerCase();
    if ((os_prefix == "win") || (os_prefix == "mac")) {
        var color = null;
        try {
            color = ko.interpolate.interpolate(window, ["%s"], [])[0];
        } catch(ex) {
            // No selection, ignore this error.
        }
        system_colorpicker(color);
    } else {
        init_colorpicker();
        var scimoz = currentView.scimoz;
        var pos = scimoz.currentPos;
        var x = scimoz.pointXFromPosition(pos);
        var y = scimoz.pointYFromPosition(pos);
        var boxObject = currentView.boxObject;
        var cp = document.getElementById('popup_colorpicker');
        cp.showPopup(currentView.scintilla,
                     x + boxObject.x,
                     y + boxObject.y,
                     'colorpicker',"topleft","topleft");
    }
}

sparky222b | Fri, 2007-03-02 12:52

Uh..where's the link?

php_knecht | Mon, 2007-09-10 08:29

i miss the colorpicker in komodo edit and hope i can get this macro finally (if it is functional)

toddw
ActiveState Staff
Mon, 2007-09-10 10:58

Sorry, not sure what happened with the macro download link. I've added the code directly.

Cheers,
Todd

Tom | Sat, 2007-09-29 09:48

This extension looks useful but I really like the added functionality that comes with the OS X colour picker. Would it be possible to have that pop-up on Macs rather than the simple colour grid?

samush | Sat, 2007-11-24 10:49

Im very new to Komodo, could someone give me an idea of how i can get this javascript to work with Komodo (Edit 4.2)?
Or a way that the OS X color picker can be used.

Thanks
Sam

toddw
ActiveState Staff
Mon, 2007-11-26 12:09

I've updated the macro now:

  • Includes instructions for installation
  • Now uses the Mac OSX native color picker
  • Works without warnings in Komodo 4.2

Cheers,
Todd

remb | Mon, 2007-11-26 12:43

Hi,

This is a great macro but it is fairly basic and not very useful if you need to use other colors besides the websafe ones. It would be great to make it use the OS native picker for Linux and Windows as well.

Thank you!

toddw
ActiveState Staff
Mon, 2007-11-26 13:55

The macro was not working correctly for Windows, I've updated the macro code again so it now uses the Windows native color picker.

For Linux, I'm not sure what the native color picker is... or even if there is one... I'd be glad to add a Linux native picker if someone can demonstrate this (it needs to be Python code preferably, or a JavaScript version is also acceptable).

Cheers,
Todd

remb | Wed, 2007-12-19 14:47

Linux has a native color picker (at least for Gnome) but I think that's C not Python. I found a great tutorial about a Photoshop like color picker made entirely with JavaScript which seems to be pretty cool. I guess if it's possible to be implemented into the macro, it'll look much more better for Linux systems then the current one. I'm not sure if that's possible or not, it is fairly big for a simple macro, it looks more like a candidate for an extension :)

It was just an idea...

http://www.webreference.com/programming/javascript/mk/column3/index.html

Thanks.

Tom | Mon, 2007-11-26 14:05

This is brilliant, thanks a million. :-D

marlar | Wed, 2007-12-19 09:33

Hi,

It works great. Thanks!

One useful suggestion: if the color code is selected when invoking the macro, that color will show up in the color picker.

Would that be difficult to implement?

Martin

toddw
ActiveState Staff
Wed, 2007-12-19 14:18

It's updated to use a selection on Windows and Mac, the Linux side does not really support a preselected color.

Note: Only color selections of the form "#123ABC" will work.

Cheers,
Todd

marlar | Wed, 2007-12-19 14:50

Thanks a lot :-)

husain | Wed, 2008-01-09 03:07

Thanks, it's great!

isaak | Tue, 2008-03-11 15:14

Is it possible to make this macro trigger on CSS color property/value calls?

--
Isaak Malik
Web Developer

toddw
ActiveState Staff
Tue, 2008-03-11 15:46

Hi Isaak,

It is possible, but it would require some work to get it integrated nicely. The Komodo team discussed this when we initially started the CSS code intelligence helpers, and it is on our CSS completions todo list.

The idea we were thinking of is that the css property completion list which is shown at "color: <|>" would include some type of "colorpicker" item, which upon selection would then popup the necessary color picker controls similar to this macro. If your curious, all of the code that does the css completions is found here:
http://svn.openkomodo.com/openkomodo/view/openkomodo/trunk/src/codeintel...

Cheers,
Todd

isaak | Thu, 2008-03-13 11:02

Thank you for the details Todd.

It's a great idea to add a colorpicker item but if a property is called which can only contain color values, why not open the colorpicker automatically?

Also thank you for the SVN link, I wish I could understand Python though. That will be for later :).

--
Isaak Malik
Web Developer

stephenr | Mon, 2008-08-25 22:30

Because for some things it will be a lot quicker to type the colour value than picking it!

conor | Fri, 2008-05-02 06:21

Hi, I've added support for colour code selections which don't have a preceeding hash and for 3 character (shorthand) colour codes to the system colour picker (i.e. Win and Mac):

Just copy over the current code for the macro and save... now you can select "#77ff99", "77ff99", "#7f9" or "7f9" in the editor and all will result in the correct colour being pre-selected in the system colour picker.

Very handy given that double clicking on a CSS colour code in Komodo only pre-selects the hash if clicking on the hash part of the code! :)

Update (ToddW)
I've moved Conor's macro code (v1.2) into the main macro code above. Thanks for the nice enhancements Conor!

shanec
ActiveState Staff
Tue, 2008-10-21 20:47

michal.kocarek | Sun, 2008-11-09 13:28

I have created updated version of the color picker, which parses named colors as well.

Internal Gecko engine is used for named color handling. I create SPAN element, assign named color into a css rule and then use window.getComputedStyle(...) function to get color in rgb(...) format.

Modified parts:

  • system_colorpicker() was modified to handle named colors (well... all strings in non-hexa format)
  • rgb2hex() was added to convert rgb(...) notation to hexa

Note: Updated code was moved into the main code above

toddw
ActiveState Staff
Mon, 2008-11-10 16:31

Thanks Michal for the contribution, I've moved your code into the main post above.

Cheers,
Todd

jadeorchard | Sat, 2009-11-07 03:33

This macro stopped working in 5.2. I finally grew frustrated enough to dig through code and found the culprit. See the bug report for a solution:
http://bugs.activestate.com/show_bug.cgi?id=85226

While I'm here I should also share my modification to this already fantastic macro. I've modified the main routine to select the contiguous string under the text cursor (if you didn't explicitly select something). Simple change, but it makes it so much more pleasant by eliminating the need to select a color before replacing it:

  1. var currentView = ko.views.manager.currentView;
  2. var editor = currentView.scimoz;
  3. if (currentView) {
  4.     currentView.scintilla.focus();
  5.     var os_prefix = window.navigator.platform.substring(0, 3).toLowerCase();
  6.     if ((os_prefix == "win") || (os_prefix == "mac")) {
  7.         var color = null;
  8.  
  9.           if (editor.anchor === editor.currentPos) {
  10.                 color = ko.interpolate.getWordUnderCursor();
  11.                
  12.                 if (!color) {
  13.          
  14.                      // Start at cursor position and break at any non-word character
  15.                      rangeStart = rangeEnd = editor.currentPos;
  16.                      currentRange = color = null;
  17.                      rangeRegEx = /^[\w]+$/;
  18.                       do {
  19.                
  20.                           color = currentRange;
  21.                           rangeStart --;
  22.                           if (rangeStart >= 0) currentRange = editor.getTextRange(rangeStart, rangeEnd);
  23.                
  24.                      } while (rangeStart >= 0 && rangeRegEx.test(currentRange));
  25.                 }
  26.          
  27.                 if (color) {
  28.                      currentView.setFocus();
  29.                      ko.commands.doCommand('cmd_beginningOfWord');
  30.                      ko.commands.doCommand('cmd_endOfWordExtend');
  31.                 }
  32.          
  33.           } else color = editor.selText;
  34.  
  35.         system_colorpicker(color);
  36.     } else {
  37.         init_colorpicker();
  38.         var scimoz = currentView.scimoz;
  39.         var pos = scimoz.currentPos;
  40.         var x = scimoz.pointXFromPosition(pos);
  41.         var y = scimoz.pointYFromPosition(pos);
  42.         var boxObject = currentView.boxObject;
  43.         var cp = document.getElementById('popup_colorpicker');
  44.         cp.showPopup(currentView.scintilla,
  45.                      x + boxObject.x,
  46.                      y + boxObject.y,
  47.                      'colorpicker',"topleft","topleft");
  48.     }
  49. }

toddw
ActiveState Staff
Sun, 2009-11-08 11:04

Thanks for the code. Have you tried this css hyperlinks? As much of this macro has gone into making the color picker hyperlinks feature:
http://community.activestate.com/komodo-51-features#hyperlinks

which does not need to use a selection (just holdingthe Ctrl or Cmd key).

Cheers,
Todd

justusthane | Sat, 2010-01-23 00:40

100% awesome on OS X. Thank you so much!

marrsattax | Tue, 2010-08-10 08:08

Been looking for something like this for a while. Works a charm - Thank You!