| 1 | // $Id: farbtastic.js,v 1.2 2007/01/08 22:53:01 unconed Exp $ | 
|---|
| 2 | // Farbtastic 1.2 | 
|---|
| 3 |  | 
|---|
| 4 | jQuery.fn.farbtastic = function (callback) { | 
|---|
| 5 |      $.farbtastic(this, callback); | 
|---|
| 6 |      return this; | 
|---|
| 7 | }; | 
|---|
| 8 |  | 
|---|
| 9 | jQuery.farbtastic = function (container, callback) { | 
|---|
| 10 |      var container = $(container).get(0); | 
|---|
| 11 |      return container.farbtastic || (container.farbtastic = new jQuery._farbtastic(container, callback)); | 
|---|
| 12 | }; | 
|---|
| 13 |  | 
|---|
| 14 | jQuery._farbtastic = function (container, callback) { | 
|---|
| 15 |      // Store farbtastic object | 
|---|
| 16 |      var fb = this; | 
|---|
| 17 |       | 
|---|
| 18 |      // Insert markup | 
|---|
| 19 |      $(container).html('<div class="farbtastic"><div class="color"></div><div class="wheel"></div><div class="overlay"></div><div class="h-marker marker"></div><div class="sl-marker marker"></div></div>'); | 
|---|
| 20 |      var e = $('.farbtastic', container); | 
|---|
| 21 |      fb.wheel = $('.wheel', container).get(0); | 
|---|
| 22 |      // Dimensions | 
|---|
| 23 |      fb.radius = 84; | 
|---|
| 24 |      fb.square = 100; | 
|---|
| 25 |      fb.width = 194; | 
|---|
| 26 |       | 
|---|
| 27 |      // Fix background PNGs in IE6 | 
|---|
| 28 |      if (navigator.appVersion.match(/MSIE [0-6]\./)) { | 
|---|
| 29 |           $('*', e).each(function () { | 
|---|
| 30 |                if (this.currentStyle.backgroundImage != 'none') { | 
|---|
| 31 |                     var image = this.currentStyle.backgroundImage; | 
|---|
| 32 |                     image = this.currentStyle.backgroundImage.substring(5, image.length - 2); | 
|---|
| 33 |                     $(this).css({ | 
|---|
| 34 |                          'backgroundImage': 'none', | 
|---|
| 35 |                          'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')" | 
|---|
| 36 |                     }); | 
|---|
| 37 |                } | 
|---|
| 38 |           }); | 
|---|
| 39 |      } | 
|---|
| 40 |  | 
|---|
| 41 |      /** | 
|---|
| 42 |      * Link to the given element(s) or callback. | 
|---|
| 43 |      */ | 
|---|
| 44 |      fb.linkTo = function (callback) { | 
|---|
| 45 |           // Unbind previous nodes | 
|---|
| 46 |           if (typeof fb.callback == 'object') { | 
|---|
| 47 |                $(fb.callback).unbind('keyup', fb.updateValue); | 
|---|
| 48 |           } | 
|---|
| 49 |            | 
|---|
| 50 |           // Reset color | 
|---|
| 51 |           fb.color = null; | 
|---|
| 52 |            | 
|---|
| 53 |           // Bind callback or elements | 
|---|
| 54 |           if (typeof callback == 'function') { | 
|---|
| 55 |                fb.callback = callback; | 
|---|
| 56 |           } | 
|---|
| 57 |           else if (typeof callback == 'object' || typeof callback == 'string') { | 
|---|
| 58 |                fb.callback = $(callback); | 
|---|
| 59 |                fb.callback.bind('keyup', fb.updateValue); | 
|---|
| 60 |                if (fb.callback.get(0).value) { | 
|---|
| 61 |                     fb.setColor(fb.callback.get(0).value); | 
|---|
| 62 |                } | 
|---|
| 63 |           } | 
|---|
| 64 |           return this; | 
|---|
| 65 |      }; | 
|---|
| 66 |       | 
|---|
| 67 |      fb.updateValue = function (event) { | 
|---|
| 68 |           if (this.value && this.value != fb.color) { | 
|---|
| 69 |                fb.setColor(this.value); | 
|---|
| 70 |           } | 
|---|
| 71 |      }; | 
|---|
| 72 |       | 
|---|
| 73 |      /** | 
|---|
| 74 |      * Change color with HTML syntax #123456 | 
|---|
| 75 |      */ | 
|---|
| 76 |      fb.setColor = function (color) { | 
|---|
| 77 |           var unpack = fb.unpack(color); | 
|---|
| 78 |           if (fb.color != color && unpack) { | 
|---|
| 79 |                fb.color = color; | 
|---|
| 80 |                fb.rgb = unpack; | 
|---|
| 81 |                fb.hsl = fb.RGBToHSL(fb.rgb); | 
|---|
| 82 |                fb.updateDisplay(); | 
|---|
| 83 |           } | 
|---|
| 84 |           return this; | 
|---|
| 85 |      }; | 
|---|
| 86 |       | 
|---|
| 87 |      /** | 
|---|
| 88 |      * Change color with HSL triplet [0..1, 0..1, 0..1] | 
|---|
| 89 |      */ | 
|---|
| 90 |      fb.setHSL = function (hsl) { | 
|---|
| 91 |           fb.hsl = hsl; | 
|---|
| 92 |           fb.rgb = fb.HSLToRGB(hsl); | 
|---|
| 93 |           fb.color = fb.pack(fb.rgb); | 
|---|
| 94 |           fb.updateDisplay(); | 
|---|
| 95 |           return this; | 
|---|
| 96 |      }; | 
|---|
| 97 |       | 
|---|
| 98 |      ///////////////////////////////////////////////////// | 
|---|
| 99 |       | 
|---|
| 100 |      /** | 
|---|
| 101 |      * Retrieve the coordinates of the given event relative to the center | 
|---|
| 102 |      * of the widget. | 
|---|
| 103 |      */ | 
|---|
| 104 |      fb.widgetCoords = function (event) { | 
|---|
| 105 |           var x, y; | 
|---|
| 106 |           var el = event.target || event.srcElement; | 
|---|
| 107 |           var reference = fb.wheel; | 
|---|
| 108 |            | 
|---|
| 109 |           if (typeof event.offsetX != 'undefined') { | 
|---|
| 110 |                // Use offset coordinates and find common offsetParent | 
|---|
| 111 |                var pos = { x: event.offsetX, y: event.offsetY }; | 
|---|
| 112 |                 | 
|---|
| 113 |                // Send the coordinates upwards through the offsetParent chain. | 
|---|
| 114 |                var e = el; | 
|---|
| 115 |                while (e) { | 
|---|
| 116 |                     e.mouseX = pos.x; | 
|---|
| 117 |                     e.mouseY = pos.y; | 
|---|
| 118 |                     pos.x += e.offsetLeft; | 
|---|
| 119 |                     pos.y += e.offsetTop; | 
|---|
| 120 |                     e = e.offsetParent; | 
|---|
| 121 |                } | 
|---|
| 122 |                 | 
|---|
| 123 |                // Look for the coordinates starting from the wheel widget. | 
|---|
| 124 |                var e = reference; | 
|---|
| 125 |                var offset = { x: 0, y: 0 }; | 
|---|
| 126 |                while (e) { | 
|---|
| 127 |                     if (typeof e.mouseX != 'undefined') { | 
|---|
| 128 |                          x = e.mouseX - offset.x; | 
|---|
| 129 |                          y = e.mouseY - offset.y; | 
|---|
| 130 |                          break; | 
|---|
| 131 |                     } | 
|---|
| 132 |                     offset.x += e.offsetLeft; | 
|---|
| 133 |                     offset.y += e.offsetTop; | 
|---|
| 134 |                     e = e.offsetParent; | 
|---|
| 135 |                } | 
|---|
| 136 |                 | 
|---|
| 137 |                // Reset stored coordinates | 
|---|
| 138 |                e = el; | 
|---|
| 139 |                while (e) { | 
|---|
| 140 |                     e.mouseX = undefined; | 
|---|
| 141 |                     e.mouseY = undefined; | 
|---|
| 142 |                     e = e.offsetParent; | 
|---|
| 143 |                } | 
|---|
| 144 |           } | 
|---|
| 145 |           else { | 
|---|
| 146 |           // Use absolute coordinates | 
|---|
| 147 |                var pos = fb.absolutePosition(reference); | 
|---|
| 148 |                x = (event.pageX || 0*(event.clientX + $('html').get(0).scrollLeft)) - pos.x; | 
|---|
| 149 |                y = (event.pageY || 0*(event.clientY + $('html').get(0).scrollTop)) - pos.y; | 
|---|
| 150 |           } | 
|---|
| 151 |           // Subtract distance to middle | 
|---|
| 152 |           return { x: x - fb.width / 2, y: y - fb.width / 2 }; | 
|---|
| 153 |      }; | 
|---|
| 154 |       | 
|---|
| 155 |      /** | 
|---|
| 156 |      * Mousedown handler | 
|---|
| 157 |      */ | 
|---|
| 158 |      fb.mousedown = function (event) { | 
|---|
| 159 |           // Capture mouse | 
|---|
| 160 |           if (!document.dragging) { | 
|---|
| 161 |                $(document).bind('mousemove', fb.mousemove).bind('mouseup', fb.mouseup); | 
|---|
| 162 |                document.dragging = true; | 
|---|
| 163 |           } | 
|---|
| 164 |            | 
|---|
| 165 |           // Check which area is being dragged | 
|---|
| 166 |           var pos = fb.widgetCoords(event); | 
|---|
| 167 |           fb.circleDrag = Math.max(Math.abs(pos.x), Math.abs(pos.y)) * 2 > fb.square; | 
|---|
| 168 |            | 
|---|
| 169 |           // Process | 
|---|
| 170 |           fb.mousemove(event); | 
|---|
| 171 |           return false; | 
|---|
| 172 |      }; | 
|---|
| 173 |       | 
|---|
| 174 |      /** | 
|---|
| 175 |      * Mousemove handler | 
|---|
| 176 |      */ | 
|---|
| 177 |      fb.mousemove = function (event) { | 
|---|
| 178 |           // Get coordinates relative to color picker center | 
|---|
| 179 |           var pos = fb.widgetCoords(event); | 
|---|
| 180 |            | 
|---|
| 181 |           // Set new HSL parameters | 
|---|
| 182 |           if (fb.circleDrag) { | 
|---|
| 183 |                var hue = Math.atan2(pos.x, -pos.y) / 6.28; | 
|---|
| 184 |                if (hue < 0) hue += 1; | 
|---|
| 185 |                fb.setHSL([hue, fb.hsl[1], fb.hsl[2]]); | 
|---|
| 186 |           } | 
|---|
| 187 |           else { | 
|---|
| 188 |                var sat = Math.max(0, Math.min(1, -(pos.x / fb.square) + .5)); | 
|---|
| 189 |                var lum = Math.max(0, Math.min(1, -(pos.y / fb.square) + .5)); | 
|---|
| 190 |                fb.setHSL([fb.hsl[0], sat, lum]); | 
|---|
| 191 |           } | 
|---|
| 192 |           return false; | 
|---|
| 193 |      }; | 
|---|
| 194 |       | 
|---|
| 195 |      /** | 
|---|
| 196 |      * Mouseup handler | 
|---|
| 197 |      */ | 
|---|
| 198 |      fb.mouseup = function () { | 
|---|
| 199 |           // Uncapture mouse | 
|---|
| 200 |           $(document).unbind('mousemove', fb.mousemove); | 
|---|
| 201 |           $(document).unbind('mouseup', fb.mouseup); | 
|---|
| 202 |           document.dragging = false; | 
|---|
| 203 |      }; | 
|---|
| 204 |       | 
|---|
| 205 |      /** | 
|---|
| 206 |      * Update the markers and styles | 
|---|
| 207 |      */ | 
|---|
| 208 |      fb.updateDisplay = function () { | 
|---|
| 209 |           // Markers | 
|---|
| 210 |           var angle = fb.hsl[0] * 6.28; | 
|---|
| 211 |           $('.h-marker', e).css({ | 
|---|
| 212 |                left: Math.round(Math.sin(angle) * fb.radius + fb.width / 2) + 'px', | 
|---|
| 213 |                top: Math.round(-Math.cos(angle) * fb.radius + fb.width / 2) + 'px' | 
|---|
| 214 |           }); | 
|---|
| 215 |            | 
|---|
| 216 |           $('.sl-marker', e).css({ | 
|---|
| 217 |                left: Math.round(fb.square * (.5 - fb.hsl[1]) + fb.width / 2) + 'px', | 
|---|
| 218 |                top: Math.round(fb.square * (.5 - fb.hsl[2]) + fb.width / 2) + 'px' | 
|---|
| 219 |           }); | 
|---|
| 220 |            | 
|---|
| 221 |           // Saturation/Luminance gradient | 
|---|
| 222 |           $('.color', e).css('backgroundColor', fb.pack(fb.HSLToRGB([fb.hsl[0], 1, 0.5]))); | 
|---|
| 223 |            | 
|---|
| 224 |           // Linked elements or callback | 
|---|
| 225 |           if (typeof fb.callback == 'object') { | 
|---|
| 226 |                // Set background/foreground color | 
|---|
| 227 |                $(fb.callback).css({ | 
|---|
| 228 |                     backgroundColor: fb.color, | 
|---|
| 229 |                     color: fb.hsl[2] > 0.5 ? '#000' : '#fff' | 
|---|
| 230 |                }); | 
|---|
| 231 |                      | 
|---|
| 232 |                // Change linked value | 
|---|
| 233 |                $(fb.callback).each(function() { | 
|---|
| 234 |                     if (this.value != fb.color) { | 
|---|
| 235 |                          this.value = fb.color; | 
|---|
| 236 |                     } | 
|---|
| 237 |                }); | 
|---|
| 238 |                $(fb.callback).change(); | 
|---|
| 239 |           } | 
|---|
| 240 |           else if (typeof fb.callback == 'function') { | 
|---|
| 241 |                fb.callback.call(fb, fb.color); | 
|---|
| 242 |           } | 
|---|
| 243 |      }; | 
|---|
| 244 |       | 
|---|
| 245 |      /** | 
|---|
| 246 |      * Get absolute position of element | 
|---|
| 247 |      */ | 
|---|
| 248 |      fb.absolutePosition = function (el) { | 
|---|
| 249 |           var r = { x: el.offsetLeft, y: el.offsetTop }; | 
|---|
| 250 |           // Resolve relative to offsetParent | 
|---|
| 251 |           if (el.offsetParent) { | 
|---|
| 252 |                var tmp = fb.absolutePosition(el.offsetParent); | 
|---|
| 253 |                r.x += tmp.x; | 
|---|
| 254 |                r.y += tmp.y; | 
|---|
| 255 |           } | 
|---|
| 256 |           return r; | 
|---|
| 257 |      }; | 
|---|
| 258 |       | 
|---|
| 259 |      /* Various color utility functions */ | 
|---|
| 260 |      fb.pack = function (rgb) { | 
|---|
| 261 |           var r = Math.round(rgb[0] * 255); | 
|---|
| 262 |           var g = Math.round(rgb[1] * 255); | 
|---|
| 263 |           var b = Math.round(rgb[2] * 255); | 
|---|
| 264 |           return '#' + (r < 16 ? '0' : '') + r.toString(16) + | 
|---|
| 265 |                (g < 16 ? '0' : '') + g.toString(16) + | 
|---|
| 266 |                (b < 16 ? '0' : '') + b.toString(16); | 
|---|
| 267 |      }; | 
|---|
| 268 |       | 
|---|
| 269 |      fb.unpack = function (color) { | 
|---|
| 270 |           if (color.length == 7) { | 
|---|
| 271 |                return [parseInt('0x' + color.substring(1, 3)) / 255, | 
|---|
| 272 |                     parseInt('0x' + color.substring(3, 5)) / 255, | 
|---|
| 273 |                     parseInt('0x' + color.substring(5, 7)) / 255]; | 
|---|
| 274 |           } | 
|---|
| 275 |           else if (color.length == 4) { | 
|---|
| 276 |                return [parseInt('0x' + color.substring(1, 2)) / 15, | 
|---|
| 277 |                     parseInt('0x' + color.substring(2, 3)) / 15, | 
|---|
| 278 |                     parseInt('0x' + color.substring(3, 4)) / 15]; | 
|---|
| 279 |           } | 
|---|
| 280 |      }; | 
|---|
| 281 |       | 
|---|
| 282 |      fb.HSLToRGB = function (hsl) { | 
|---|
| 283 |           var m1, m2, r, g, b; | 
|---|
| 284 |           var h = hsl[0], s = hsl[1], l = hsl[2]; | 
|---|
| 285 |           m2 = (l <= 0.5) ? l * (s + 1) : l + s - l*s; | 
|---|
| 286 |           m1 = l * 2 - m2; | 
|---|
| 287 |           return [this.hueToRGB(m1, m2, h+0.33333), | 
|---|
| 288 |                this.hueToRGB(m1, m2, h), | 
|---|
| 289 |                this.hueToRGB(m1, m2, h-0.33333)]; | 
|---|
| 290 |      }; | 
|---|
| 291 |       | 
|---|
| 292 |      fb.hueToRGB = function (m1, m2, h) { | 
|---|
| 293 |           h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h); | 
|---|
| 294 |           if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; | 
|---|
| 295 |           if (h * 2 < 1) return m2; | 
|---|
| 296 |           if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6; | 
|---|
| 297 |           return m1; | 
|---|
| 298 |      }; | 
|---|
| 299 |       | 
|---|
| 300 |      fb.RGBToHSL = function (rgb) { | 
|---|
| 301 |           var min, max, delta, h, s, l; | 
|---|
| 302 |           var r = rgb[0], g = rgb[1], b = rgb[2]; | 
|---|
| 303 |           min = Math.min(r, Math.min(g, b)); | 
|---|
| 304 |           max = Math.max(r, Math.max(g, b)); | 
|---|
| 305 |           delta = max - min; | 
|---|
| 306 |           l = (min + max) / 2; | 
|---|
| 307 |           s = 0; | 
|---|
| 308 |           if (l > 0 && l < 1) { | 
|---|
| 309 |                s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l)); | 
|---|
| 310 |           } | 
|---|
| 311 |           h = 0; | 
|---|
| 312 |           if (delta > 0) { | 
|---|
| 313 |                if (max == r && max != g) h += (g - b) / delta; | 
|---|
| 314 |                if (max == g && max != b) h += (2 + (b - r) / delta); | 
|---|
| 315 |                if (max == b && max != r) h += (4 + (r - g) / delta); | 
|---|
| 316 |                h /= 6; | 
|---|
| 317 |           } | 
|---|
| 318 |           return [h, s, l]; | 
|---|
| 319 |      }; | 
|---|
| 320 |       | 
|---|
| 321 |      // Install mousedown handler (the others are set on the document on-demand) | 
|---|
| 322 |      $('*', e).mousedown(fb.mousedown); | 
|---|
| 323 |       | 
|---|
| 324 |      // Init color | 
|---|
| 325 |      fb.setColor('#000000'); | 
|---|
| 326 |       | 
|---|
| 327 |      // Set linked elements/callback | 
|---|
| 328 |      if (callback) { | 
|---|
| 329 |           fb.linkTo(callback); | 
|---|
| 330 |      } | 
|---|
| 331 | }; | 
|---|