330 lines
8.4 KiB
JavaScript
330 lines
8.4 KiB
JavaScript
|
/**
|
||
|
@author Matt Crinklaw-Vogt (tantaman)
|
||
|
*/
|
||
|
(function( $ ) {
|
||
|
if (!$.event.special.destroyed) {
|
||
|
$.event.special.destroyed = {
|
||
|
remove: function(o) {
|
||
|
if (o.handler) {
|
||
|
o.handler();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function ctrlPtComparator(l,r) {
|
||
|
return l.position - r.position;
|
||
|
}
|
||
|
|
||
|
function bind(fn, ctx) {
|
||
|
if (typeof fn.bind === "function") {
|
||
|
return fn.bind(ctx);
|
||
|
} else {
|
||
|
return function() {
|
||
|
fn.apply(ctx, arguments);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var browserPrefix = "";
|
||
|
if ($.browser.mozilla) {
|
||
|
browserPrefix = "-moz-";
|
||
|
} else if ($.browser.webkit) {
|
||
|
browserPrefix = "-webkit-";
|
||
|
} else if ($.browser.opera) {
|
||
|
browserPrefix = "-o-";
|
||
|
} else if ($.browser.msie) {
|
||
|
browserPrefix = "-ms-";
|
||
|
}
|
||
|
|
||
|
function GradientSelection($el, opts) {
|
||
|
this.$el = $el;
|
||
|
this.$el.css("position", "relative");
|
||
|
this.opts = opts;
|
||
|
|
||
|
var $preview = $("<canvas class='gradientPicker-preview'></canvas>");
|
||
|
this.$el.append($preview);
|
||
|
var canvas = $preview[0];
|
||
|
canvas.width = canvas.clientWidth;
|
||
|
canvas.height = canvas.clientHeight;
|
||
|
this.g2d = canvas.getContext("2d");
|
||
|
|
||
|
var $ctrlPtContainer = $("<div class='gradientPicker-ctrlPts'></div>");
|
||
|
this.$el.append($ctrlPtContainer)
|
||
|
this.$ctrlPtContainer = $ctrlPtContainer;
|
||
|
|
||
|
this.updatePreview = bind(this.updatePreview, this);
|
||
|
this.controlPoints = [];
|
||
|
this.ctrlPtConfig = new ControlPtConfig(this.$el, opts);
|
||
|
for (var i = 0; i < opts.controlPoints.length; ++i) {
|
||
|
var ctrlPt = this.createCtrlPt(opts.controlPoints[i]);
|
||
|
this.controlPoints.push(ctrlPt);
|
||
|
}
|
||
|
|
||
|
this.docClicked = bind(this.docClicked, this);
|
||
|
this.destroyed = bind(this.destroyed, this);
|
||
|
$(document).bind("click", this.docClicked);
|
||
|
this.$el.bind("destroyed", this.destroyed);
|
||
|
this.previewClicked = bind(this.previewClicked, this);
|
||
|
$preview.click(this.previewClicked);
|
||
|
|
||
|
this.updatePreview();
|
||
|
}
|
||
|
|
||
|
GradientSelection.prototype = {
|
||
|
docClicked: function() {
|
||
|
this.ctrlPtConfig.hide();
|
||
|
},
|
||
|
|
||
|
createCtrlPt: function(ctrlPtSetup) {
|
||
|
return new ControlPoint(this.$ctrlPtContainer, ctrlPtSetup, this.opts.orientation, this, this.ctrlPtConfig)
|
||
|
},
|
||
|
|
||
|
destroyed: function() {
|
||
|
$(document).unbind("click", this.docClicked);
|
||
|
},
|
||
|
|
||
|
updateOptions: function(opts) {
|
||
|
$.extend(this.opts, opts);
|
||
|
this.updatePreview();
|
||
|
},
|
||
|
|
||
|
updatePreview: function() {
|
||
|
var result = [];
|
||
|
this.controlPoints.sort(ctrlPtComparator);
|
||
|
if (this.opts.orientation == "horizontal") {
|
||
|
var grad = this.g2d.createLinearGradient(0, 0, this.g2d.canvas.width, 0);
|
||
|
for (var i = 0; i < this.controlPoints.length; ++i) {
|
||
|
var pt = this.controlPoints[i];
|
||
|
grad.addColorStop(pt.position, pt.color);
|
||
|
result.push({
|
||
|
position: pt.position,
|
||
|
color: pt.color
|
||
|
});
|
||
|
}
|
||
|
} else {
|
||
|
|
||
|
}
|
||
|
|
||
|
this.g2d.fillStyle = grad;
|
||
|
this.g2d.fillRect(0, 0, this.g2d.canvas.width, this.g2d.canvas.height);
|
||
|
|
||
|
if (this.opts.generateStyles)
|
||
|
var styles = this._generatePreviewStyles();
|
||
|
|
||
|
this.opts.change(result, styles);
|
||
|
},
|
||
|
|
||
|
removeControlPoint: function(ctrlPt) {
|
||
|
var cpidx = this.controlPoints.indexOf(ctrlPt);
|
||
|
|
||
|
if (cpidx != -1) {
|
||
|
this.controlPoints.splice(cpidx, 1);
|
||
|
ctrlPt.$el.remove();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
previewClicked: function(e) {
|
||
|
var offset = $(e.target).offset();
|
||
|
var x = e.pageX - offset.left;
|
||
|
var y = e.pageY - offset.top;
|
||
|
|
||
|
var imgData = this.g2d.getImageData(x,y,1,1);
|
||
|
var colorStr = "rgb(" + imgData.data[0] + "," + imgData.data[1] + "," + imgData.data[2] + ")";
|
||
|
|
||
|
var cp = this.createCtrlPt({
|
||
|
position: x / this.g2d.canvas.width,
|
||
|
color: colorStr
|
||
|
});
|
||
|
|
||
|
this.controlPoints.push(cp);
|
||
|
this.controlPoints.sort(ctrlPtComparator);
|
||
|
},
|
||
|
|
||
|
_generatePreviewStyles: function() {
|
||
|
//linear-gradient(top, rgb(217,230,163) 86%, rgb(227,249,159) 9%)
|
||
|
var str = this.opts.type + "-gradient(" + ((this.opts.type == "linear") ? (this.opts.fillDirection + ", ") : "");
|
||
|
var first = true;
|
||
|
for (var i = 0; i < this.controlPoints.length; ++i) {
|
||
|
var pt = this.controlPoints[i];
|
||
|
if (!first) {
|
||
|
str += ", ";
|
||
|
} else {
|
||
|
first = false;
|
||
|
}
|
||
|
str += pt.color + " " + ((pt.position*100)|0) + "%";
|
||
|
}
|
||
|
|
||
|
str = str + ")"
|
||
|
var styles = [str, browserPrefix + str];
|
||
|
return styles;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function ControlPoint($parentEl, initialState, orientation, listener, ctrlPtConfig) {
|
||
|
this.$el = $("<div class='gradientPicker-ctrlPt'></div>");
|
||
|
$parentEl.append(this.$el);
|
||
|
this.$parentEl = $parentEl;
|
||
|
this.configView = ctrlPtConfig;
|
||
|
|
||
|
if (typeof initialState === "string") {
|
||
|
initialState = initialState.split(" ");
|
||
|
this.position = parseFloat(initialState[1])/100;
|
||
|
this.color = initialState[0];
|
||
|
} else {
|
||
|
this.position = initialState.position;
|
||
|
this.color = initialState.color;
|
||
|
}
|
||
|
|
||
|
this.listener = listener;
|
||
|
this.outerWidth = this.$el.outerWidth();
|
||
|
|
||
|
this.$el.css("background-color", this.color);
|
||
|
if (orientation == "horizontal") {
|
||
|
var pxLeft = ($parentEl.width() - this.$el.outerWidth()) * (this.position);
|
||
|
this.$el.css("left", pxLeft);
|
||
|
} else {
|
||
|
var pxTop = ($parentEl.height() - this.$el.outerHeight()) * (this.position);
|
||
|
this.$el.css("top", pxTop);
|
||
|
}
|
||
|
|
||
|
this.drag = bind(this.drag, this);
|
||
|
this.stop = bind(this.stop, this);
|
||
|
this.clicked = bind(this.clicked, this);
|
||
|
this.colorChanged = bind(this.colorChanged, this);
|
||
|
this.$el.draggable({
|
||
|
axis: (orientation == "horizontal") ? "x" : "y",
|
||
|
drag: this.drag,
|
||
|
stop: this.stop,
|
||
|
containment: $parentEl
|
||
|
});
|
||
|
this.$el.click(this.clicked);
|
||
|
}
|
||
|
|
||
|
ControlPoint.prototype = {
|
||
|
drag: function(e, ui) {
|
||
|
// convert position to a %
|
||
|
var left = ui.position.left;
|
||
|
this.position = (left / (this.$parentEl.width() - this.outerWidth));
|
||
|
this.listener.updatePreview();
|
||
|
},
|
||
|
|
||
|
stop: function(e, ui) {
|
||
|
this.listener.updatePreview();
|
||
|
this.configView.show(this.$el.position(), this.color, this);
|
||
|
},
|
||
|
|
||
|
clicked: function(e) {
|
||
|
this.configView.show(this.$el.position(), this.color, this);
|
||
|
e.stopPropagation();
|
||
|
return false;
|
||
|
},
|
||
|
|
||
|
colorChanged: function(c) {
|
||
|
this.color = c;
|
||
|
this.$el.css("background-color", this.color);
|
||
|
this.listener.updatePreview();
|
||
|
},
|
||
|
|
||
|
removeClicked: function() {
|
||
|
this.listener.removeControlPoint(this);
|
||
|
this.listener.updatePreview();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function ControlPtConfig($parent, opts) {
|
||
|
//color-chooser
|
||
|
this.$el = $('<div class="gradientPicker-ptConfig" style="visibility: hidden"></div>');
|
||
|
$parent.append(this.$el);
|
||
|
var $cpicker = $('<div class="color-chooser"></div>');
|
||
|
this.$el.append($cpicker);
|
||
|
var $rmEl = $("<div class='gradientPicker-close'></div>");
|
||
|
this.$el.append($rmEl);
|
||
|
|
||
|
this.colorChanged = bind(this.colorChanged, this);
|
||
|
this.removeClicked = bind(this.removeClicked, this);
|
||
|
$cpicker.ColorPicker({
|
||
|
onChange: this.colorChanged
|
||
|
});
|
||
|
this.$cpicker = $cpicker;
|
||
|
this.opts = opts;
|
||
|
this.visible = false;
|
||
|
|
||
|
$rmEl.click(this.removeClicked);
|
||
|
}
|
||
|
|
||
|
ControlPtConfig.prototype = {
|
||
|
show: function(position, color, listener) {
|
||
|
this.visible = true;
|
||
|
this.listener = listener;
|
||
|
this.$el.css("visibility", "visible");
|
||
|
this.$cpicker.ColorPickerSetColor(color);
|
||
|
this.$cpicker.css("background-color", color);
|
||
|
if (this.opts.orientation === "horizontal") {
|
||
|
this.$el.css("left", position.left);
|
||
|
} else {
|
||
|
this.$el.css("top", position.top);
|
||
|
}
|
||
|
//else {
|
||
|
// this.visible = false;
|
||
|
//this.$el.css("visibility", "hidden");
|
||
|
//}
|
||
|
},
|
||
|
|
||
|
hide: function() {
|
||
|
if (this.visible) {
|
||
|
this.$el.css("visibility", "hidden");
|
||
|
this.visible = false;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
colorChanged: function(hsb, hex, rgb) {
|
||
|
hex = "#" + hex;
|
||
|
this.listener.colorChanged(hex);
|
||
|
this.$cpicker.css("background-color", hex)
|
||
|
},
|
||
|
|
||
|
removeClicked: function() {
|
||
|
this.listener.removeClicked();
|
||
|
this.hide();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var methods = {
|
||
|
init: function(opts) {
|
||
|
opts = $.extend({
|
||
|
controlPoints: ["#FFF 0%", "#000 100%"],
|
||
|
orientation: "horizontal",
|
||
|
type: "linear",
|
||
|
fillDirection: "left",
|
||
|
generateStyles: true,
|
||
|
change: function() {}
|
||
|
}, opts);
|
||
|
|
||
|
this.each(function() {
|
||
|
var $this = $(this);
|
||
|
var gradSel = new GradientSelection($this, opts);
|
||
|
$this.data("gradientPicker-sel", gradSel);
|
||
|
});
|
||
|
},
|
||
|
|
||
|
update: function(opts) {
|
||
|
this.each(function() {
|
||
|
var $this = $(this);
|
||
|
var gradSel = $this.data("gradientPicker-sel");
|
||
|
if (gradSel != null) {
|
||
|
gradSel.updateOptions(opts);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
$.fn.gradientPicker = function(method, opts) {
|
||
|
if (typeof method === "string" && method !== "init") {
|
||
|
methods[method].call(this, opts);
|
||
|
} else {
|
||
|
opts = method;
|
||
|
methods.init.call(this, opts);
|
||
|
}
|
||
|
};
|
||
|
})( jQuery );
|