1/* 2 * jQuery miniColors: A small color selector 3 * http://www.abeautifulsite.net/blog/2011/02/jquery-minicolors-a-color-selector-for-input-controls/ 4 * http://labs.abeautifulsite.net/jquery-miniColors/ 5 * 6 * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/) 7 * 8 * Dual licensed under the MIT or GPL Version 2 licenses 9 * 10 * 11 * Usage: 12 * 13 * 1. Link to jQuery: <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script> 14 * 15 * 2. Link to miniColors: <script type="text/javascript" src="jquery.miniColors.js"></script> 16 * 17 * 3. Include miniColors stylesheet: <link type="text/css" rel="stylesheet" href="jquery.miniColors.css" /> 18 * 19 * 4. Apply $([selector]).miniColors() to one or more INPUT elements 20 * 21 * 22 * Options: 23 * 24 * disabled [true|false] 25 * readonly [true|false] 26 * 27 * 28 * Specify options on creation: 29 * 30 * $([selector]).miniColors({ 31 * 32 * optionName: value, 33 * optionName: value, 34 * ... 35 * 36 * }); 37 * 38 * 39 * Methods: 40 * 41 * Call a method using: $([selector]).miniColors('methodName', [value]); 42 * 43 * disabled [true|false] 44 * readonly [true|false] 45 * value [hex value] 46 * destroy 47 * 48 * 49 * Events: 50 * 51 * Attach events on creation: 52 * 53 * $([selector]).miniColors({ 54 * 55 * change: function(hex, rgb) { ... } 56 * 57 * }); 58 * 59 * change(hex, rgb) called when the color value changes; 'this' will refer to the original input element; 60 * hex is the string hex value of the selected color; rgb is an object with the RGB values 61 * 62 * 63 * Change log: 64 * 65 * - v0.1 (2011-02-24) - Initial release 66 * 67 * 68 * Attribution: 69 * 70 * - The color picker icon is based on an icon from the amazing Fugue icon set: 71 * http://p.yusukekamiyamane.com/ 72 * 73 * - The gradient image, the hue image, and the math functions are courtesy of 74 * the eyecon.co jQuery color picker: http://www.eyecon.ro/colorpicker/ 75 * 76 * 77*/ 78if(jQuery) (function($) { 79 80 $.extend($.fn, { 81 82 miniColors: function(o, data) { 83 84 var create = function(input, o, data) { 85 // 86 // Creates a new instance of the miniColors selector 87 // 88 89 // Determine initial color (defaults to white) 90 var color = cleanHex(input.val()); 91 if( !color ) color = 'FFFFFF'; 92 var hsb = hex2hsb(color); 93 94 // Create trigger 95 var trigger = $('<a class="miniColors-trigger" style="background-color: #' + color + '" href="#"></a>'); 96 trigger.insertAfter(input); 97 98 // Add necessary attributes 99 input.addClass('miniColors').attr('maxlength', 7).attr('autocomplete', 'off'); 100 101 // Set input data 102 input.data('trigger', trigger); 103 input.data('hsb', hsb); 104 if( o.change ) input.data('change', o.change); 105 106 // Handle options 107 if( o.readonly ) input.attr('readonly', true); 108 if( o.disabled ) disable(input); 109 110 // Show selector when trigger is clicked 111 trigger.bind('click.miniColors', function(event) { 112 show(input); 113 event.preventDefault(); 114 }); 115 116 // Show selector when input receives focus 117 input.bind('focus.miniColors', function(event) { 118 show(input); 119 }); 120 121 // Hide on blur 122 input.bind('blur.miniColors', function(event) { 123 var hex = cleanHex(input.val()); 124 input.val( hex ? '#' + hex : '' ); 125 }); 126 127 // Hide when tabbing out of the input 128 input.bind('keydown.miniColors', function(event) { 129 if( event.keyCode === 9 ) hide(input); 130 }); 131 132 // Update when color is typed in 133 input.bind('keyup.miniColors', function(event) { 134 // Remove non-hex characters 135 var filteredHex = input.val().replace(/[^A-F0-9#]/ig, ''); 136 input.val(filteredHex); 137 if( !setColorFromInput(input) ) { 138 // Reset trigger color when color is invalid 139 input.data('trigger').css('backgroundColor', '#FFF'); 140 } 141 }); 142 143 // Handle pasting 144 input.bind('paste.miniColors', function(event) { 145 // Short pause to wait for paste to complete 146 setTimeout( function() { 147 input.trigger('keyup'); 148 }, 5); 149 }); 150 151 }; 152 153 154 var destroy = function(input) { 155 156 // 157 // Destroys an active instance of the miniColors selector 158 // 159 160 hide(); 161 162 input = $(input); 163 input.data('trigger').remove(); 164 input.removeAttr('autocomplete'); 165 input.removeData('trigger'); 166 input.removeData('selector'); 167 input.removeData('hsb'); 168 input.removeData('huePicker'); 169 input.removeData('colorPicker'); 170 input.removeData('colorPosition'); 171 input.removeData('mousebutton'); 172 input.removeData('moving'); 173 input.unbind('click.miniColors'); 174 input.unbind('focus.miniColors'); 175 input.unbind('blur.miniColors'); 176 input.unbind('keyup.miniColors'); 177 input.unbind('keydown.miniColors'); 178 input.unbind('paste.miniColors'); 179 $(document).unbind('mousedown.miniColors'); 180 $(document).unbind('mousemove.miniColors'); 181 182 183 }; 184 185 186 var enable = function(input) { 187 188 // 189 // Disables the input control and the selector 190 // 191 192 input.attr('disabled', false); 193 input.data('trigger').css('opacity', 1); 194 195 }; 196 197 var disable = function(input) { 198 // 199 // Disables the input control and the selector 200 // 201 202 hide(input); 203 input.attr('disabled', true); 204 input.data('trigger').css('opacity', .5); 205 206 }; 207 208 209 var show = function(input) { 210 // 211 // Shows the miniColors selector 212 // 213 214 if( input.attr('disabled') ) return false;
215 216 // Hide all other instances 217 hide(); 218 219 // Generate the selector 220 /* 221 var selector = $('<div class="miniColors-selector"></div>'); 222 selector.append('<div class="miniColors-colors" style="background-color: #FFF;"><div class="miniColors-colorPicker"></div></div>'); 223 selector.append('<div class="miniColors-hues"><div class="miniColors-huePicker"></div></div>'); 224 selector.css({ 225 top: input.is(':visible') ? input.offset().top + input.outerHeight() : input.data('trigger').offset().top + input.data('trigger').outerHeight(), 226 left: input.is(':visible') ? input.offset().left : input.data('trigger').offset().left, 227 display: 'none' 228 }).addClass( input.attr('class') ); 229 */ 230 231 var selector = $(".miniColors-selector"); 232 selector.addClass( input.attr('class') ); 233 234 // Set the starting color 235 var hsb = input.data('hsb'); 236 var hex = hsb2hex({ h: hsb.h, s: 100, b: 100 }); 237 238 // Set background for colors 239 selector.find('.miniColors-colors').css('backgroundColor', '#' + hex); 240 241 // Set colorPicker position 242 //var colorPosition = input.data('colorPosition'); 243 //if( !colorPosition ) 244 colorPosition = getColorPositionFromHSB(hsb); 245 selector.find('.miniColors-colorPicker').css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px'); 246 247 // Set huePicker position 248 var huePosition = input.data('huePosition'); 249 if( !huePosition ) huePosition = getHuePositionFromHSB(hsb); 250 selector.find('.miniColors-huePicker').css('top', huePosition.y + 'px'); 251 252 // Fire the show event and pass the hex value 253 $(document).trigger("colorPickerShow", [hex]); 254 255 256 // Set input data 257 input.data('selector', selector); 258 input.data('huePicker', selector.find('.miniColors-huePicker')); 259 input.data('colorPicker', selector.find('.miniColors-colorPicker')); 260 input.data('mousebutton', 0); 261 262 //$('BODY').append(selector); 263 selector.show(); 264 265 // Prevent text selection in IE 266 selector.bind('selectstart', function() { return false; }); 267 268 $(document).bind('mousedown.miniColors', function(event) { 269 input.data('mousebutton', 1); 270 271 if( $(event.target).parents().andSelf().hasClass('miniColors-colors') ) { 272 event.preventDefault(); 273 input.data('moving', 'colors'); 274 moveColor(input, event); 275 } 276 277 if( $(event.target).parents().andSelf().hasClass('miniColors-hues') ) { 278 event.preventDefault(); 279 input.data('moving', 'hues'); 280 moveHue(input, event); 281 } 282 283 if( $(event.target).parents().andSelf().hasClass('miniColors-selector') ) { 284 event.preventDefault(); 285 return; 286 } 287 288 if( $(event.target).parents().andSelf().hasClass('miniColors') ) return; 289 290 hide(input); 291 }); 292 293 $(document).bind('mouseup.miniColors', function(event) { 294 input.data('mousebutton', 0); 295 input.removeData('moving'); 296 }); 297 298 $(document).bind('mousemove.miniColors', function(event) { 299 if( input.data('mousebutton') === 1 ) { 300 if( input.data('moving') === 'colors' ) moveColor(input, event); 301 if( input.data('moving') === 'hues' ) moveHue(input, event); 302 } 303 }); 304 305 }; 306 307 308 var hide = function(input) { 309 // Fire the destroy event 310 $(document).trigger("colorPickerHide"); 311 312 // 313 // Hides one or more miniColors selectors 314 // 315 316 // Hide all other instances if input isn't specified 317 if( !input ) input = '.miniColors'; 318 319 /* This removes the element from stage 320 $(input).each( function() { 321 var selector = $(this).data('selector'); 322 $(this).removeData('selector'); 323 $(selector).fadeOut(0, function() { 324 $(this).remove(); 325 }); 326 327 }); 328 */ 329 330 // Just hide the element instead of deleting it 331 $(".miniColors-selector").hide(); 332 333 $(document).unbind('mousedown.miniColors'); 334 $(document).unbind('mousemove.miniColors'); 335 336 }; 337 338 339 var moveColor = function(input, event) { 340 341 var colorPicker = input.data('colorPicker'); 342 343 colorPicker.hide(); 344 345 var position = { 346 x: event.clientX - input.data('selector').find('.miniColors-colors').offset().left + $(document).scrollLeft() - 5, 347 y: event.clientY - input.data('selector').find('.miniColors-colors').offset().top + $(document).scrollTop() - 5 348 }; 349 350 if( position.x <= -5 ) position.x = -5; 351 if( position.x >= 144 ) position.x = 144; 352 if( position.y <= -5 ) position.y = -5; 353 if( position.y >= 144 ) position.y = 144; 354 input.data('colorPosition', position); 355 colorPicker.css('left', position.x).css('top', position.y).show(); 356 357 // Calculate saturation 358 var s = Math.round((position.x + 5) * .67); 359 if( s < 0 ) s = 0; 360 if( s > 100 ) s = 100; 361 362 // Calculate brightness 363 var b = 100 - Math.round((position.y + 5) * .67); 364 if( b < 0 ) b = 0; 365 if( b > 100 ) b = 100; 366 367 // Update HSB values 368 var hsb = input.data('hsb'); 369 hsb.s = s; 370 hsb.b = b; 371 372 // Set color 373 setColor(input, hsb, true); 374 375 }; 376 377 378 var moveHue = function(input, event) { 379 380 var huePicker = input.data('huePicker'); 381 382 huePicker.hide(); 383 384 var position = { 385 y: event.clientY - input.data('selector').find('.miniColors-colors').offset().top + $(document).scrollTop() - 1 386 }; 387 388 if( position.y <= -1 ) position.y = -1; 389 if( position.y >= 149 ) position.y = 149; 390 input.data('huePosition', position); 391 huePicker.css('top', position.y).show(); 392 393 // Calculate hue 394 var h = Math.round((150 - position.y - 1) * 2.4); 395 if( h < 0 ) h = 0; 396 if( h > 360 ) h = 360; 397 398 // Update HSB values 399 var hsb = input.data('hsb'); 400 hsb.h = h; 401 402 // Set color 403 setColor(input, hsb, true); 404 405 }; 406 407 408 var setColor = function(input, hsb, updateInputValue) { 409 input.data('hsb', hsb); 410 var hex = hsb2hex(hsb); 411 if( updateInputValue ) input.val('#' + hex); 412 input.data('trigger').css('backgroundColor', '#' + hex); 413 if( input.data('selector') ) input.data('selector').find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); 414 415 if( input.data('change') ) { 416 input.data('change').call(input, '#' + hex, hsb2rgb(hsb)); 417 } 418 419 }; 420 421 422 var setColorFromInput = function(input) { 423
424 // Don't update if the hex color is invalid 425 var hex = cleanHex(input.val()); 426 if( !hex ) return false; 427 428 // Get HSB equivalent 429 var hsb = hex2hsb(hex); 430 431 // If color is the same, no change required 432 var currentHSB = input.data('hsb'); 433 if( hsb.h === currentHSB.h && hsb.s === currentHSB.s && hsb.b === currentHSB.b ) return true; 434 435 // Set colorPicker position 436 var colorPosition = getColorPositionFromHSB(hsb); 437 var colorPicker = $(input.data('colorPicker')); 438 colorPicker.css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px'); 439 440 // Set huePosition position 441 var huePosition = getHuePositionFromHSB(hsb); 442 var huePicker = $(input.data('huePicker')); 443 huePicker.css('top', huePosition.y + 'px'); 444 445 setColor(input, hsb, false); 446 447 return true; 448 449 }; 450 451 452 var getColorPositionFromHSB = function(hsb) { 453 454 var x = Math.ceil(hsb.s / .67); 455 if( x < 0 ) x = 0; 456 if( x > 150 ) x = 150; 457 458 var y = 150 - Math.ceil(hsb.b / .67); 459 if( y < 0 ) y = 0; 460 if( y > 150 ) y = 150; 461 462 return { x: x - 5, y: y - 5 }; 463 464 } 465 466 467 var getHuePositionFromHSB = function(hsb) { 468 469 var y = 150 - (hsb.h / 2.4); 470 if( y < 0 ) h = 0; 471 if( y > 150 ) h = 150; 472 473 return { y: y - 1 }; 474 475 } 476 477 478 var cleanHex = function(hex) { 479 480 // 481 // Turns a dirty hex string into clean, 6-character hex color 482 // 483 484 hex = hex.replace(/[^A-Fa-f0-9]/, ''); 485 486 if( hex.length == 3 ) { 487 hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 488 } 489 490 return hex.length === 6 ? hex : null; 491 492 }; 493 494 495 var hsb2rgb = function(hsb) { 496 var rgb = {}; 497 var h = Math.round(hsb.h); 498 var s = Math.round(hsb.s*255/100); 499 var v = Math.round(hsb.b*255/100); 500 if(s == 0) { 501 rgb.r = rgb.g = rgb.b = v; 502 } else { 503 var t1 = v; 504 var t2 = (255 - s) * v / 255; 505 var t3 = (t1 - t2) * (h % 60) / 60; 506 if( h == 360 ) h = 0; 507 if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; } 508 else if( h<120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; } 509 else if( h<180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; } 510 else if( h<240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; } 511 else if( h<300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; } 512 else if( h<360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; } 513 else { rgb.r = 0; rgb.g = 0; rgb.b = 0; } 514 } 515 return { 516 r: Math.round(rgb.r), 517 g: Math.round(rgb.g), 518 b: Math.round(rgb.b) 519 }; 520 }; 521 522 523 var rgb2hex = function(rgb) { 524 525 var hex = [ 526 rgb.r.toString(16), 527 rgb.g.toString(16), 528 rgb.b.toString(16) 529 ]; 530 $.each(hex, function(nr, val) { 531 if (val.length == 1) hex[nr] = '0' + val; 532 }); 533 534 return hex.join(''); 535 }; 536 537 538 var hex2rgb = function(hex) { 539 var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); 540 541 return { 542 r: hex >> 16, 543 g: (hex & 0x00FF00) >> 8, 544 b: (hex & 0x0000FF) 545 }; 546 }; 547 548 549 var rgb2hsb = function(rgb) { 550 var hsb = { h: 0, s: 0, b: 0 }; 551 var min = Math.min(rgb.r, rgb.g, rgb.b); 552 var max = Math.max(rgb.r, rgb.g, rgb.b); 553 var delta = max - min; 554 hsb.b = max; 555 hsb.s = max != 0 ? 255 * delta / max : 0; 556 if( hsb.s != 0 ) { 557 if( rgb.r == max ) { 558 hsb.h = (rgb.g - rgb.b) / delta; 559 } else if( rgb.g == max ) { 560 hsb.h = 2 + (rgb.b - rgb.r) / delta; 561 } else { 562 hsb.h = 4 + (rgb.r - rgb.g) / delta; 563 } 564 } else { 565 hsb.h = -1; 566 } 567 hsb.h *= 60; 568 if( hsb.h < 0 ) { 569 hsb.h += 360; 570 } 571 hsb.s *= 100/255; 572 hsb.b *= 100/255; 573 return hsb; 574 }; 575 576 577 var hex2hsb = function(hex) { 578 var hsb = rgb2hsb(hex2rgb(hex)); 579 // Zero out hue marker for black, white, and grays (saturation === 0) 580 if( hsb.s === 0 ) hsb.h = 360; 581 return hsb; 582 }; 583 584 585 var hsb2hex = function(hsb) { 586 return rgb2hex(hsb2rgb(hsb)); 587 }; 588 589 590 // 591 // Handle calls to $([selector]).miniColors() 592 // 593 switch(o) { 594 595 596 case 'readonly': 597 598 $(this).each( function() { 599 $(this).attr('readonly', data); 600 }); 601 602 return $(this); 603 604 break; 605 606 case 'disabled': 607 608 $(this).each( function() { 609 if( data ) { 610 disable($(this)); 611 } else { 612 enable($(this)); 613 } 614 }); 615 616 return $(this); 617 618 case 'value': 619 620 $(this).each( function() { 621 $(this).val(data).trigger('keyup'); 622 }); 623 624 return $(this); 625 626 break; 627 628 case 'destroy': 629 630 $(this).each( function() { 631 destroy($(this)); 632 }); 633 634 return $(this); 635 636 case 'hide': 637 638 $(this).each( function() { 639 hide($(this)); 640 }); 641 642 return $(this); 643 644 default: 645
646 if( !o ) o = {}; 647 648 $(this).each( function() { 649 650 // Force an input element 651 //if( $(this)[0].tagName.toLowerCase() !== 'input' ) return; 652 653 // If a trigger is present, the control was already created 654 if( $(this).data('trigger') ) return; 655 656 // Create the control 657 create($(this), o, data); 658 659 }); 660 661 return $(this); 662 663 } 664 665 666 } 667 668 669 }); 670 671})(jQuery); 672 673 674
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.