1/* 2 * jQuery miniColors: A small color selector 3 * 4 * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/) 5 * 6 * Dual licensed under the MIT or GPL Version 2 licenses 7 * 8 * 9 * Usage: 10 * 11 * 1. Link to jQuery: <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script> 12 * 13 * 2. Link to miniColors: <script type="text/javascript" src="jquery.miniColors.js"></script> 14 * 15 * 3. Include miniColors stylesheet: <link type="text/css" rel="stylesheet" href="jquery.miniColors.css" /> 16 * 17 * 4. Apply $([selector]).miniColors() to one or more INPUT elements 18 * 19 * 20 * Options: 21 * 22 * disabled [true|false] 23 * readonly [true|false] 24 * 25 * 26 * Specify options on creation: 27 * 28 * $([selector]).miniColors({ 29 * 30 * optionName: value, 31 * optionName: value, 32 * ... 33 * 34 * }); 35 * 36 * 37 * Methods: 38 * 39 * Call a method using: $([selector]).miniColors('methodName', [value]); 40 * 41 * disabled [true|false] 42 * readonly [true|false] 43 * value [hex value] 44 * destroy 45 * 46 * 47 * Events: 48 * 49 * Attach events on creation: 50 * 51 * $([selector]).miniColors({ 52 * 53 * change: function(hex, rgb) { ... } 54 * 55 * }); 56 * 57 * change(hex, rgb) called when the color value changes; 'this' will refer to the original input element; 58 * hex is the string hex value of the selected color; rgb is an object with the RGB values 59 * 60 * 61 * Change log: 62 * 63 * - v0.1 (2011-02-24) - Initial release 64 * 65 * 66 * Attribution: 67 * 68 * - The color picker icon is based on an icon from the amazing Fugue icon set: 69 * http://p.yusukekamiyamane.com/ 70 * 71 * - The gradient image, the hue image, and the math functions are courtesy of 72 * the eyecon.co jQuery color picker: http://www.eyecon.ro/colorpicker/ 73 * 74 * 75*/ 76if(jQuery) (function($) { 77 78 $.extend($.fn, { 79 80 miniColors: function(o, data) { 81 82 83 var create = function(input, o, data) { 84 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 event.preventDefault(); 113 input.trigger('focus'); 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('mousebutton'); 171 input.removeData('moving'); 172 input.unbind('click.miniColors'); 173 input.unbind('focus.miniColors'); 174 input.unbind('blur.miniColors'); 175 input.unbind('keyup.miniColors'); 176 input.unbind('keydown.miniColors'); 177 input.unbind('paste.miniColors'); 178 $(document).unbind('mousedown.miniColors'); 179 $(document).unbind('mousemove.miniColors'); 180 181 }; 182 183 184 var enable = function(input) { 185 186 // 187 // Disables the input control and the selector 188 // 189 190 input.attr('disabled', false); 191 input.data('trigger').css('opacity', 1); 192 193 }; 194 195 196 var disable = function(input) { 197 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 // 212 // Shows the miniColors selector 213 // 214 215 if( input.attr('disabled') ) return false;
216 217 // Hide all other instances 218 hide(); 219 220 // Generate the selector 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 // Set background for colors 231 var hsb = input.data('hsb'); 232 selector.find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); 233 234 // Set colorPicker position 235 var colorPosition = input.data('colorPosition'); 236 if( !colorPosition ) colorPosition = getColorPositionFromHSB(hsb); 237 selector.find('.miniColors-colorPicker').css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px'); 238 239 // Set huePicker position 240 var huePosition = input.data('huePosition'); 241 if( !huePosition ) huePosition = getHuePositionFromHSB(hsb); 242 selector.find('.miniColors-huePicker').css('top', huePosition.y + 'px'); 243 244 245 // Set input data 246 input.data('selector', selector); 247 input.data('huePicker', selector.find('.miniColors-huePicker')); 248 input.data('colorPicker', selector.find('.miniColors-colorPicker')); 249 input.data('mousebutton', 0); 250 251 $('BODY').append(selector); 252 selector.fadeIn(100); 253 254 // Prevent text selection in IE 255 selector.bind('selectstart', function() { return false; }); 256 257 $(document).bind('mousedown.miniColors', function(event) { 258 input.data('mousebutton', 1); 259 260 if( $(event.target).parents().andSelf().hasClass('miniColors-colors') ) { 261 event.preventDefault(); 262 input.data('moving', 'colors'); 263 moveColor(input, event); 264 } 265 266 if( $(event.target).parents().andSelf().hasClass('miniColors-hues') ) { 267 event.preventDefault(); 268 input.data('moving', 'hues'); 269 moveHue(input, event); 270 } 271 272 if( $(event.target).parents().andSelf().hasClass('miniColors-selector') ) { 273 event.preventDefault(); 274 return; 275 } 276 277 if( $(event.target).parents().andSelf().hasClass('miniColors') ) return; 278 279 hide(input); 280 }); 281 282 $(document).bind('mouseup.miniColors', function(event) { 283 input.data('mousebutton', 0); 284 input.removeData('moving'); 285 }); 286 287 $(document).bind('mousemove.miniColors', function(event) { 288 if( input.data('mousebutton') === 1 ) { 289 if( input.data('moving') === 'colors' ) moveColor(input, event); 290 if( input.data('moving') === 'hues' ) moveHue(input, event); 291 } 292 }); 293 294 }; 295 296 297 var hide = function(input) { 298 299 // 300 // Hides one or more miniColors selectors 301 // 302 303 // Hide all other instances if input isn't specified 304 if( !input ) input = '.miniColors'; 305 306 $(input).each( function() { 307 var selector = $(this).data('selector'); 308 $(this).removeData('selector'); 309 $(selector).fadeOut(100, function() { 310 $(this).remove(); 311 }); 312 }); 313 314 $(document).unbind('mousedown.miniColors'); 315 $(document).unbind('mousemove.miniColors'); 316 317 }; 318 319 320 var moveColor = function(input, event) { 321 322 var colorPicker = input.data('colorPicker'); 323 324 colorPicker.hide(); 325 326 var position = { 327 x: event.clientX - input.data('selector').find('.miniColors-colors').offset().left + $(document).scrollLeft() - 5, 328 y: event.clientY - input.data('selector').find('.miniColors-colors').offset().top + $(document).scrollTop() - 5 329 }; 330 331 if( position.x <= -5 ) position.x = -5; 332 if( position.x >= 144 ) position.x = 144; 333 if( position.y <= -5 ) position.y = -5; 334 if( position.y >= 144 ) position.y = 144; 335 input.data('colorPosition', position); 336 colorPicker.css('left', position.x).css('top', position.y).show(); 337 338 // Calculate saturation 339 var s = Math.round((position.x + 5) * .67); 340 if( s < 0 ) s = 0; 341 if( s > 100 ) s = 100; 342 343 // Calculate brightness 344 var b = 100 - Math.round((position.y + 5) * .67); 345 if( b < 0 ) b = 0; 346 if( b > 100 ) b = 100; 347 348 // Update HSB values 349 var hsb = input.data('hsb'); 350 hsb.s = s; 351 hsb.b = b; 352 353 // Set color 354 setColor(input, hsb, true); 355 356 }; 357 358 359 var moveHue = function(input, event) { 360 361 var huePicker = input.data('huePicker'); 362 363 huePicker.hide(); 364 365 var position = { 366 y: event.clientY - input.data('selector').find('.miniColors-colors').offset().top + $(document).scrollTop() - 1 367 }; 368 369 if( position.y <= -1 ) position.y = -1; 370 if( position.y >= 149 ) position.y = 149; 371 input.data('huePosition', position); 372 huePicker.css('top', position.y).show(); 373 374 // Calculate hue 375 var h = Math.round((150 - position.y - 1) * 2.4); 376 if( h < 0 ) h = 0; 377 if( h > 360 ) h = 360; 378 379 // Update HSB values 380 var hsb = input.data('hsb'); 381 hsb.h = h; 382 383 // Set color 384 setColor(input, hsb, true); 385 386 }; 387 388 389 var setColor = function(input, hsb, updateInputValue) { 390 391 input.data('hsb', hsb); 392 var hex = hsb2hex(hsb); 393 if( updateInputValue ) input.val('#' + hex); 394 input.data('trigger').css('backgroundColor', '#' + hex); 395 if( input.data('selector') ) input.data('selector').find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); 396 397 if( input.data('change') ) { 398 input.data('change').call(input, '#' + hex, hsb2rgb(hsb)); 399 } 400 401 }; 402 403 404 var setColorFromInput = function(input) { 405
406 // Don't update if the hex color is invalid 407 var hex = cleanHex(input.val()); 408 if( !hex ) return false; 409 410 // Get HSB equivalent 411 var hsb = hex2hsb(hex); 412 413 // If color is the same, no change required 414 var currentHSB = input.data('hsb'); 415 if( hsb.h === currentHSB.h && hsb.s === currentHSB.s && hsb.b === currentHSB.b ) return true; 416 417 // Set colorPicker position 418 var colorPosition = getColorPositionFromHSB(hsb); 419 var colorPicker = $(input.data('colorPicker')); 420 colorPicker.css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px'); 421 422 // Set huePosition position 423 var huePosition = getHuePositionFromHSB(hsb); 424 var huePicker = $(input.data('huePicker')); 425 huePicker.css('top', huePosition.y + 'px'); 426 427 setColor(input, hsb, false); 428 429 return true; 430 431 }; 432 433 434 var getColorPositionFromHSB = function(hsb) { 435 436 var x = Math.ceil(hsb.s / .67); 437 if( x < 0 ) x = 0; 438 if( x > 150 ) x = 150; 439 440 var y = 150 - Math.ceil(hsb.b / .67); 441 if( y < 0 ) y = 0; 442 if( y > 150 ) y = 150; 443 444 return { x: x - 5, y: y - 5 }; 445 446 } 447 448 449 var getHuePositionFromHSB = function(hsb) { 450 451 var y = 150 - (hsb.h / 2.4); 452 if( y < 0 ) h = 0; 453 if( y > 150 ) h = 150; 454 455 return { y: y - 1 }; 456 457 } 458 459 460 var cleanHex = function(hex) { 461 462 // 463 // Turns a dirty hex string into clean, 6-character hex color 464 // 465 466 hex = hex.replace(/[^A-Fa-f0-9]/, ''); 467 468 if( hex.length == 3 ) { 469 hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 470 } 471 472 return hex.length === 6 ? hex : null; 473 474 }; 475 476 477 var hsb2rgb = function(hsb) { 478 var rgb = {}; 479 var h = Math.round(hsb.h); 480 var s = Math.round(hsb.s*255/100); 481 var v = Math.round(hsb.b*255/100); 482 if(s == 0) { 483 rgb.r = rgb.g = rgb.b = v; 484 } else { 485 var t1 = v; 486 var t2 = (255 - s) * v / 255; 487 var t3 = (t1 - t2) * (h % 60) / 60; 488 if( h == 360 ) h = 0; 489 if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; } 490 else if( h<120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; } 491 else if( h<180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; } 492 else if( h<240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; } 493 else if( h<300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; } 494 else if( h<360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; } 495 else { rgb.r = 0; rgb.g = 0; rgb.b = 0; } 496 } 497 return { 498 r: Math.round(rgb.r), 499 g: Math.round(rgb.g), 500 b: Math.round(rgb.b) 501 }; 502 }; 503 504 505 var rgb2hex = function(rgb) { 506 507 var hex = [ 508 rgb.r.toString(16), 509 rgb.g.toString(16), 510 rgb.b.toString(16) 511 ]; 512 $.each(hex, function(nr, val) { 513 if (val.length == 1) hex[nr] = '0' + val; 514 }); 515 516 return hex.join(''); 517 }; 518 519 520 var hex2rgb = function(hex) { 521 var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); 522 523 return { 524 r: hex >> 16, 525 g: (hex & 0x00FF00) >> 8, 526 b: (hex & 0x0000FF) 527 }; 528 }; 529 530 531 var rgb2hsb = function(rgb) { 532 var hsb = { h: 0, s: 0, b: 0 }; 533 var min = Math.min(rgb.r, rgb.g, rgb.b); 534 var max = Math.max(rgb.r, rgb.g, rgb.b); 535 var delta = max - min; 536 hsb.b = max; 537 hsb.s = max != 0 ? 255 * delta / max : 0; 538 if( hsb.s != 0 ) { 539 if( rgb.r == max ) { 540 hsb.h = (rgb.g - rgb.b) / delta; 541 } else if( rgb.g == max ) { 542 hsb.h = 2 + (rgb.b - rgb.r) / delta; 543 } else { 544 hsb.h = 4 + (rgb.r - rgb.g) / delta; 545 } 546 } else { 547 hsb.h = -1; 548 } 549 hsb.h *= 60; 550 if( hsb.h < 0 ) { 551 hsb.h += 360; 552 } 553 hsb.s *= 100/255; 554 hsb.b *= 100/255; 555 return hsb; 556 }; 557 558 559 var hex2hsb = function(hex) { 560 var hsb = rgb2hsb(hex2rgb(hex)); 561 // Zero out hue marker for black, white, and grays (saturation === 0) 562 if( hsb.s === 0 ) hsb.h = 360; 563 return hsb; 564 }; 565 566 567 var hsb2hex = function(hsb) { 568 return rgb2hex(hsb2rgb(hsb)); 569 }; 570 571 572 // 573 // Handle calls to $([selector]).miniColors() 574 // 575 switch(o) { 576 577 case 'readonly': 578 579 $(this).each( function() { 580 $(this).attr('readonly', data); 581 }); 582 583 return $(this); 584 585 break; 586 587 case 'disabled': 588 589 $(this).each( function() { 590 if( data ) { 591 disable($(this)); 592 } else { 593 enable($(this)); 594 } 595 }); 596 597 return $(this); 598 599 case 'value': 600 601 $(this).each( function() { 602 $(this).val(data).trigger('keyup'); 603 }); 604 605 return $(this); 606 607 break; 608 609 case 'destroy': 610 611 $(this).each( function() { 612 destroy($(this)); 613 }); 614 615 return $(this); 616 617 default: 618
619 if( !o ) o = {}; 620 621 $(this).each( function() { 622 623 // Must be called on an input element 624 if( $(this)[0].tagName.toLowerCase() !== 'input' ) return; 625 626 // If a trigger is present, the control was already created 627 if( $(this).data('trigger') ) return; 628 629 // Create the control 630 create($(this), o, data); 631 632 }); 633 634 return $(this); 635 636 } 637 638 639 } 640 641 642 }); 643 644})(jQuery); 645 646 647
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.