      1 /**
      2  * SlickGrid color editor and formatter.
      3  *
      4  * @module SlickGridColors
      5  */
      6 define([
      7     'jquery',
      8     'underscore',
      9     'view',
     10     'viewcontroller',
     11     'spectrum'
     12 ],
     13 function($, _, DecompositionView, ViewControllers, spectrum) {
     14
     15   /**
     16    *
     17    * @class ColorEditor
     18    *
     19    * This class represents a color editor defined by the SlickGrid
     20    * project.
     21    *
     22    * Note, this object is heavily based on classes in slick.editors.js and in
     23    * the documentation that can be found [here](https://github.com/mleibman/
     24    * SlickGrid/wiki/Writing-custom-cell-editors).
     25    *
     26    * Also see ColorFormatter, a function in charge of formatting colors for the
     27    * SlickGrid object.
     28    *
     29    * @param {object} args Arguments passed by SlickGrid.
     30    *
     31    * @constructs ColorEditor
     32    * @alias module:SlickGridColors.ColorEditor
     33    *
     34    */
     35   function ColorEditor(args) {
     36     var $input = $("<div class='colorbox'></div>");
     37     var defaultValue;
     38     var scope = this;
     39
     40     this.init = function() {
     41       // make the background look exactly the same when the color is being
     42       // edited
     43       $(args.container).css('background-color', '#eeeeee');
     44       $input.appendTo($(args.container));
     45       $input.css('background-color', args.item.value);
     46
     47       // initialize spectrum
     48       $input.spectrum({
     49         color: args.item.color,
     50         showInput: true,
     51         allowEmpty: false,
     52         showInitial: true,
     53         clickoutFiresChange: true,
     54         className: 'full-spectrum',
     55         preferredFormat: 'hex6',
     56         // Show the whole set of color palette on the menu (only discrete)
     57         showPalette: args.grid.selectionPalette !== undefined,
     58         showSelectionPalette: args.grid.selectionPalette !== undefined,
     59         palette: args.grid.selectionPalette,
     60
     61
     62         /* On change callback */
     63         change: function(color) {
     64           $input.css('background-color', color.toHexString());
     65
     66           // commit the changes as soon as a new shape is selected
     67           // https://stackoverflow.com/a/35768360/379593
     68           args.grid.getEditorLock().commitCurrentEdit();
     69           args.grid.resetActiveCell();
     70         }
     71       });
     72
     73       // Don't propagate the keydown and keypress events so that inputing a
     74       // color doesn't interfere with the shortcuts of the Jupyter Notebook
     75       $input
     76         .spectrum('container')
     77         .find('.sp-input')
     78         .on('keydown keypress', function(e) {
     79           e.stopPropagation();
     80         });
     81     };
     82
     83     this.destroy = function() {
     84       $input.spectrum('hide');
     85       $input.spectrum('destroy');
     86       $input.remove();
     87     };
     88
     89     this.focus = function() {
     90       $input.focus();
     91       $input.spectrum('show');
     92     };
     93
     94     this.isValueChanged = function() {
     95       return $input.spectrum('get').toHexString() !== defaultValue;
     96     };
     97
     98     this.serializeValue = function() {
     99       return $input.spectrum('get').toHexString();
    100     };
    101
    102     this.loadValue = function(item) {
    103       defaultValue = item[args.column.field];
    104       $input.spectrum('set', defaultValue);
    105     };
    106
    107     this.applyValue = function(item, state) {
    108       item[args.column.field] = state;
    109     };
    110
    111     this.validate = function() {
    112       return {valid: true, msg: null};
    113     };
    114
    115     this.hide = function() {
    116       $input.spectrum('hide');
    117     };
    118
    119     this.show = function() {
    120       /*
    121        *
    122        * We setup a brief timeout that gives the browser enough time to finish
    123        * preparing the specturm widget. If we don't wait for 100 milliseconds,
    124        * opening the color picker will take two clicks. This is also a
    125        * consequence of spectrum initializing asynchronously, by the time
    126        * SlickGrid executes the show method the widget is not ready yet so the
    127        * 'spectrum('show')' call results in a noop, and in order to show the
    128        * color picker users would need to click again on the colorbox.
    129        *
    130        */
    131       setTimeout(function() {
    132         $input.spectrum('show');
    133       }, 100);
    134     };
    135
    136     this.position = function(cellBox) {
    137       $input.spectrum('reflow');
    138     };
    139
    140     this.init();
    141   }
    142
    143   /**
    144    *
    145    * Function to format colors for the SlickGrid object.
    146    *
    147    * This formatter is heavily based in the examples found in
    148    * slick.formattters.js and is only intended to be used with ColorFormatter.
    149    *
    150    * @param {integer} row SlickGrid row.
    151    * @param {integer} cell SlickGrid cell.
    152    * @param {integer|string|bool} value The value in the row.
    153    * @param {object} columnDef SlickGrid column definition.
    154    * @param {object} dataContext Data model of the SlickGrid object.
    155    *
    156    * @return {string} String with a div where the background color is set as
    157    * the value that's passed in.
    158    *
    159    * @alias module:SlickGridColors.ColorFormatter
    160    *
    161    */
    162   function ColorFormatter(row, cell, value, columnDef, dataContext) {
    163     return "<div class='colorbox' style='cursor:pointer;background-color:" +
    164            value + ";'></div>";
    165   }
    166
    167   return {'ColorEditor': ColorEditor, 'ColorFormatter': ColorFormatter};
    168 });
    169