2882 lines
96 KiB
JavaScript
2882 lines
96 KiB
JavaScript
// Generated by CoffeeScript 1.7.1
|
||
(function() {
|
||
var CanvasUtil, PointPreviewSilk, Recorder, Silk, Sparks, Tape, initTips, initUI, _ref,
|
||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
|
||
__hasProp = {}.hasOwnProperty,
|
||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||
__slice = [].slice;
|
||
|
||
window._d = window.d = (_ref = typeof console !== "undefined" && console !== null ? console.log.bind(console) : void 0) != null ? _ref : function() {};
|
||
|
||
_.mixin({
|
||
touch: function(e, prevent) {
|
||
var touch;
|
||
if (prevent == null) {
|
||
prevent = false;
|
||
}
|
||
touch = null;
|
||
if ((e.originalEvent != null) && (e.originalEvent.touches != null)) {
|
||
touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
|
||
}
|
||
if (touch != null) {
|
||
if (prevent) {
|
||
e.originalEvent.preventDefault();
|
||
}
|
||
return touch;
|
||
} else {
|
||
return e;
|
||
}
|
||
},
|
||
save: function(key, val) {
|
||
var e;
|
||
val = JSON.stringify({
|
||
contents: val
|
||
});
|
||
try {
|
||
return localStorage.setItem(key, val);
|
||
} catch (_error) {
|
||
e = _error;
|
||
}
|
||
},
|
||
load: function(key, otherwise) {
|
||
var e, item;
|
||
try {
|
||
item = localStorage.getItem(key);
|
||
} catch (_error) {
|
||
e = _error;
|
||
return otherwise;
|
||
}
|
||
if (item !== null) {
|
||
return JSON.parse(item).contents;
|
||
} else {
|
||
return otherwise;
|
||
}
|
||
},
|
||
lerp: function(a, b, pc) {
|
||
return a + (b - a) * pc;
|
||
},
|
||
unlerp: function(a, b, val) {
|
||
if (a === b) {
|
||
return val;
|
||
} else {
|
||
return (val - a) / (b - a);
|
||
}
|
||
},
|
||
constrain: function(num, lo, hi) {
|
||
if (num > hi) {
|
||
return hi;
|
||
}
|
||
if (num < lo) {
|
||
return lo;
|
||
}
|
||
return num;
|
||
},
|
||
rto: function(hi) {
|
||
return Math.random() * hi;
|
||
},
|
||
hexToRGB: function(hex) {
|
||
var i, rgb, _i, _results;
|
||
rgb = /^#?([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i.exec(hex).slice(1);
|
||
_results = [];
|
||
for (i = _i = 0; _i <= 2; i = ++_i) {
|
||
_results.push(parseInt(rgb[i], 16));
|
||
}
|
||
return _results;
|
||
}
|
||
});
|
||
|
||
CanvasUtil = (function() {
|
||
function CanvasUtil(canvas, fullscreen) {
|
||
this.canvas = canvas;
|
||
this.fullscreen = fullscreen != null ? fullscreen : true;
|
||
this.resizeToWindowAndPreserveContents = __bind(this.resizeToWindowAndPreserveContents, this);
|
||
if (this.fullscreen) {
|
||
this.resizeToWindow();
|
||
$(window).resize(_.debounce(this.resizeToWindowAndPreserveContents, 300));
|
||
} else {
|
||
this.updateSizeOnScreen(this.canvas.width, this.canvas.height);
|
||
this.transformAndResizeForHighDPI();
|
||
}
|
||
}
|
||
|
||
CanvasUtil.prototype.updateSizeOnScreen = function(width, height) {
|
||
this.widthOnScreen = width;
|
||
this.halfWidthOnScreen = this.widthOnScreen / 2;
|
||
this.heightOnScreen = height;
|
||
return this.halfHeightOnScreen = this.heightOnScreen / 2;
|
||
};
|
||
|
||
CanvasUtil.prototype.transformAndResizeForHighDPI = function() {
|
||
var backingStoreRatio, ctx, devicePixelRatio;
|
||
if (MobileDetect()) {
|
||
return;
|
||
}
|
||
ctx = this.canvas.getContext('2d');
|
||
devicePixelRatio = window.devicePixelRatio || 1;
|
||
backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
|
||
this.scaleRatio = devicePixelRatio / backingStoreRatio;
|
||
if (this.scaleRatio !== 1) {
|
||
this.canvas.width = this.widthOnScreen * this.scaleRatio;
|
||
this.canvas.height = this.heightOnScreen * this.scaleRatio;
|
||
this.canvas.style.width = this.widthOnScreen + 'px';
|
||
this.canvas.style.height = this.heightOnScreen + 'px';
|
||
return ctx.scale(this.scaleRatio, this.scaleRatio);
|
||
}
|
||
};
|
||
|
||
CanvasUtil.prototype.resizeToWindow = function() {
|
||
return this.resizeCanvas(window.innerWidth, window.innerHeight);
|
||
};
|
||
|
||
CanvasUtil.prototype.resizeCanvas = function(width, height) {
|
||
this.canvas.width = width;
|
||
this.canvas.height = height;
|
||
this.updateSizeOnScreen(width, height);
|
||
return this.transformAndResizeForHighDPI();
|
||
};
|
||
|
||
CanvasUtil.prototype.resizeToWindowAndPreserveContents = function() {
|
||
var ctx, image, prevHeight, prevWidth;
|
||
prevWidth = this.canvas.width;
|
||
prevHeight = this.canvas.height;
|
||
image = this.canvas.getContext('2d').getImageData(0, 0, prevWidth - 1, prevHeight - 1);
|
||
this.resizeToWindow();
|
||
ctx = this.canvas.getContext('2d');
|
||
|
||
/* Silk-specific blackness hack! */
|
||
ctx.fillStyle = '#000';
|
||
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||
|
||
/* End hack */
|
||
return ctx.putImageData(image, (this.canvas.width - prevWidth) / 2, (this.canvas.height - prevHeight) / 2);
|
||
};
|
||
|
||
return CanvasUtil;
|
||
|
||
})();
|
||
|
||
Silk = (function() {
|
||
function Silk(ctx, scaleInfo, state) {
|
||
this.ctx = ctx;
|
||
this.scaleInfo = scaleInfo;
|
||
if (state == null) {
|
||
state = {};
|
||
}
|
||
this.drawCurve = __bind(this.drawCurve, this);
|
||
this.drawInstruction = __bind(this.drawInstruction, this);
|
||
state = $.extend(true, {}, state);
|
||
$.extend(this, Silk.initialState, state);
|
||
if (this.originalLogicalWidth == null) {
|
||
this.originalLogicalWidth = this.scaleInfo.logicalWidth;
|
||
}
|
||
if (this.originalLogicalHeight == null) {
|
||
this.originalLogicalHeight = this.scaleInfo.logicalHeight;
|
||
}
|
||
this.drawScale = Math.min(this.scaleInfo.logicalWidth / this.originalLogicalWidth, this.scaleInfo.logicalHeight / this.originalLogicalHeight, 1);
|
||
this.offsetX = (this.scaleInfo.logicalWidth - this.originalLogicalWidth) / 2;
|
||
this.offsetY = (this.scaleInfo.logicalHeight - this.originalLogicalHeight) / 2;
|
||
if (this.curve == null) {
|
||
this.curve = [];
|
||
}
|
||
this.initColors();
|
||
this.cx = this.symCenterX;
|
||
this.cy = this.symCenterY;
|
||
this.twoCx = 2 * this.cx;
|
||
this.twoCy = 2 * this.cy;
|
||
this.generateDrawInstructions();
|
||
}
|
||
|
||
Silk.prototype.initColors = function() {
|
||
var _ref1;
|
||
switch (this.highlightMode) {
|
||
case 'time':
|
||
this.colorScale = d3.scale.linear().domain([this.timeColorScaleDomainLow, this.timeColorScaleDomainHigh]).range([this.highlightColor, this.color]);
|
||
break;
|
||
case 'velocity':
|
||
this.colorScale = d3.scale.pow().exponent(this.velocityColorScaleExponent).domain([this.velocityColorScaleDomainLow, this.velocityColorScaleDomainHigh]).range([this.color, this.highlightColor]);
|
||
}
|
||
this.colorScale.clamp(true);
|
||
this.colorScale.interpolate(d3.interpolateHcl);
|
||
return this.isEraser = (this.color === (_ref1 = this.highlightColor) && _ref1 === this.eraserColor);
|
||
};
|
||
|
||
Silk.prototype.frame = function() {
|
||
var i, _i, _ref1, _results;
|
||
this.frameTime++;
|
||
_results = [];
|
||
for (i = _i = 1, _ref1 = this.drawsPerFrame; 1 <= _ref1 ? _i <= _ref1 : _i >= _ref1; i = 1 <= _ref1 ? ++_i : --_i) {
|
||
_results.push(this.step(true));
|
||
}
|
||
return _results;
|
||
};
|
||
|
||
Silk.prototype.step = function(drawThisStep) {
|
||
var accx, accy, curve, difference, dist, fx, fy, i, noiseAngle, noiseValue, p, p2, symmetryAxisAngle, windAngle, xoff, yoff, _i, _len;
|
||
if (drawThisStep == null) {
|
||
drawThisStep = true;
|
||
}
|
||
if (this.startDrawingOnceCompleted && !this.completed) {
|
||
return;
|
||
}
|
||
curve = this.curve;
|
||
this.timeColorScaleTime++;
|
||
this.time++;
|
||
while (curve.length && curve[0].life === 0) {
|
||
curve.shift();
|
||
}
|
||
for (i = _i = 0, _len = curve.length; _i < _len; i = ++_i) {
|
||
p = curve[i];
|
||
accx = accy = 0;
|
||
if (this.rotateAnglesAroundSymmetryAxis) {
|
||
symmetryAxisAngle = Math.atan2(this.cx - p.y, this.cy - p.x);
|
||
}
|
||
if (this.noiseForceScale) {
|
||
noiseValue = noise(this.noiseOffset + p.x * this.noiseSpaceScale + 1000000, this.noiseOffset + p.y * this.noiseSpaceScale + 1000000, this.noiseOffset + this.noiseTimeScale * this.time, this.noiseOctaves, this.noiseFallout);
|
||
noiseAngle = this.noiseAngleOffset + this.noiseAngleScale * noiseValue;
|
||
if (this.rotateAnglesAroundSymmetryAxis) {
|
||
noiseAngle += symmetryAxisAngle;
|
||
}
|
||
accx += this.noiseForceScale * Math.cos(noiseAngle);
|
||
accy += this.noiseForceScale * Math.sin(noiseAngle);
|
||
}
|
||
if (this.initialVelocityForceScale) {
|
||
accx += this.initialVelocityForceScale * p.inputVx;
|
||
accy += this.initialVelocityForceScale * p.inputVy;
|
||
if (p.inputVx && p.inputVy) {
|
||
p.inputVx *= this.initialVelocityDecay;
|
||
p.inputVy *= this.initialVelocityDecay;
|
||
}
|
||
}
|
||
if (this.windForceScale > 0) {
|
||
windAngle = this.windAngle;
|
||
if (this.rotateAnglesAroundSymmetryAxis) {
|
||
windAngle += symmetryAxisAngle;
|
||
}
|
||
accx += this.windForceScale * Math.cos(windAngle);
|
||
accy += this.windForceScale * Math.sin(windAngle);
|
||
}
|
||
p.x += (p.x - p.px) * this.friction + accx;
|
||
p.y += (p.y - p.py) * this.friction + accy;
|
||
p.px = p.x;
|
||
p.py = p.y;
|
||
p.life--;
|
||
if (i) {
|
||
p2 = curve[i - 1];
|
||
xoff = p2.x - p.x;
|
||
yoff = p2.y - p.y;
|
||
dist = Math.sqrt(xoff * xoff + yoff * yoff);
|
||
if (dist > this.restingDistance + 0.01) {
|
||
difference = this.rigidity * (this.restingDistance - dist) / dist;
|
||
fx = difference * xoff;
|
||
fy = difference * yoff;
|
||
p.x -= fx;
|
||
p2.x += fx;
|
||
p.y -= fy;
|
||
p2.y += fy;
|
||
}
|
||
}
|
||
}
|
||
if (drawThisStep) {
|
||
return this.draw();
|
||
}
|
||
};
|
||
|
||
Silk.prototype.generateDrawInstructions = function() {
|
||
var cx, cy, instr, pc, rotateAmount, rotateBy, rotationIndex, spiralIndex, spiralScaleScale, _i, _ref1, _results;
|
||
this.drawInstructions = [];
|
||
cx = this.cx;
|
||
cy = this.cy;
|
||
rotateAmount = 2 * Math.PI / this.symNumRotations;
|
||
spiralScaleScale = d3.scale.pow().exponent(.5).domain([0, 1]).range([1, 0]);
|
||
_results = [];
|
||
for (rotationIndex = _i = 0, _ref1 = this.symNumRotations; _i < _ref1; rotationIndex = _i += 1) {
|
||
rotateBy = rotationIndex * rotateAmount;
|
||
_results.push((function() {
|
||
var _j, _ref2, _results1;
|
||
_results1 = [];
|
||
for (spiralIndex = _j = 0, _ref2 = this.spiralCopies; _j < _ref2; spiralIndex = _j += 1) {
|
||
spiralIndex = spiralIndex + 0.25 - (1 / 4);
|
||
pc = spiralIndex / this.spiralCopies;
|
||
instr = {
|
||
rotationIndex: rotationIndex,
|
||
spiralIndex: spiralIndex,
|
||
cos: Math.cos(rotateBy + this.spiralAngle * pc),
|
||
sin: Math.sin(rotateBy + this.spiralAngle * pc),
|
||
scale: spiralScaleScale(pc) * this.brushScale,
|
||
original: rotationIndex === 0 && spiralIndex === 0
|
||
};
|
||
this.drawInstructions.push(instr);
|
||
if (this.symMirror) {
|
||
_results1.push(this.drawInstructions.push(_.extend({}, instr, {
|
||
mirror: true
|
||
})));
|
||
} else {
|
||
_results1.push(void 0);
|
||
}
|
||
}
|
||
return _results1;
|
||
}).call(this));
|
||
}
|
||
return _results;
|
||
};
|
||
|
||
Silk.prototype.draw = function() {
|
||
var curve, instr, p, _i, _j, _k, _len, _len1, _len2, _ref1;
|
||
curve = this.curve;
|
||
this.setColor();
|
||
for (_i = 0, _len = curve.length; _i < _len; _i++) {
|
||
p = curve[_i];
|
||
p.__x__ = p.x;
|
||
p.__y__ = p.y;
|
||
}
|
||
_ref1 = this.drawInstructions;
|
||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||
instr = _ref1[_j];
|
||
this.drawInstruction(instr);
|
||
}
|
||
for (_k = 0, _len2 = curve.length; _k < _len2; _k++) {
|
||
p = curve[_k];
|
||
p.x = p.__x__;
|
||
p.y = p.__y__;
|
||
}
|
||
};
|
||
|
||
Silk.prototype.drawInstruction = function(instr) {
|
||
var curve, cx, cy, p, x, y, _i, _len;
|
||
curve = this.curve;
|
||
cx = this.cx;
|
||
cy = this.cy;
|
||
if (this.scaleLineWidth) {
|
||
this.ctx.lineWidth = instr.scale;
|
||
}
|
||
for (_i = 0, _len = curve.length; _i < _len; _i++) {
|
||
p = curve[_i];
|
||
x = p.__x__ - this.cx;
|
||
y = p.__y__ - this.cy;
|
||
p.x = (x * instr.cos - y * instr.sin) * instr.scale;
|
||
p.y = (x * instr.sin + y * instr.cos) * instr.scale;
|
||
if (instr.mirror) {
|
||
p.x = -p.x;
|
||
}
|
||
p.x *= this.drawScale;
|
||
p.y *= this.drawScale;
|
||
p.x += this.cx;
|
||
p.y += this.cy;
|
||
p.x += this.offsetX;
|
||
p.y += this.offsetY;
|
||
}
|
||
return this.drawCurve(instr);
|
||
};
|
||
|
||
Silk.prototype.drawCurve = function(instr) {
|
||
var ctx, curve, i, lenMinusOne, p1, p2, twoCx, _i, _ref1;
|
||
curve = this.curve;
|
||
ctx = this.ctx;
|
||
twoCx = this.twoCx;
|
||
if (!curve.length) {
|
||
return;
|
||
}
|
||
lenMinusOne = curve.length - 1;
|
||
if (instr.original && this.frameTime % 10 === 0) {
|
||
this.sparkleLine();
|
||
}
|
||
ctx.beginPath();
|
||
ctx.moveTo(curve[0].x, curve[0].y);
|
||
p1 = curve[1];
|
||
for (i = _i = 1, _ref1 = lenMinusOne - 1; _i < _ref1; i = _i += 1) {
|
||
p2 = curve[i + 1];
|
||
ctx.quadraticCurveTo(p1.x, p1.y, (p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
|
||
p1 = p2;
|
||
}
|
||
ctx.stroke();
|
||
};
|
||
|
||
Silk.prototype.addPoint = function(x, y, vx, vy) {
|
||
var p;
|
||
if (this.completed) {
|
||
return;
|
||
}
|
||
p = this.curve[this.curve.length] = {
|
||
px: x,
|
||
py: y,
|
||
x: x,
|
||
y: y,
|
||
inputVx: vx,
|
||
inputVy: vy,
|
||
life: this.startLife
|
||
};
|
||
};
|
||
|
||
Silk.prototype.complete = function() {
|
||
this.completed = true;
|
||
if (this.stopDrawingOnceCompleted) {
|
||
return this.curve = [];
|
||
}
|
||
};
|
||
|
||
Silk.prototype.finish = function() {
|
||
this.complete();
|
||
return this.curve = [];
|
||
};
|
||
|
||
Silk.prototype.isFinishedDrawing = function() {
|
||
return this.completed && (this.curve.length === 0 || this.stopDrawingOnceCompleted);
|
||
};
|
||
|
||
Silk.prototype.setColor = function() {
|
||
var p;
|
||
if (!this.curve.length) {
|
||
return;
|
||
}
|
||
this.ctx.globalCompositeOperation = this.isEraser ? 'source-over' : this.compositeOperation;
|
||
p = this.curve[this.curve.length - 1];
|
||
this.ctx.globalAlpha = this.startOpacity * (p.life / this.startLife);
|
||
return this.ctx.strokeStyle = this.colorScale((function() {
|
||
switch (this.highlightMode) {
|
||
case 'time':
|
||
return this.timeColorScaleTime;
|
||
case 'velocity':
|
||
return Math.sqrt(p.inputVx * p.inputVx + p.inputVy * p.inputVy);
|
||
}
|
||
}).call(this));
|
||
};
|
||
|
||
Silk.prototype.setSparks = function(sparks) {
|
||
this.sparks = sparks;
|
||
return this.sparkle = true;
|
||
};
|
||
|
||
Silk.prototype.sparkleLine = function() {
|
||
var len;
|
||
len = this.curve.length;
|
||
if (len) {
|
||
return this.sparklePoint(this.curve[_.random(len - 1)]);
|
||
}
|
||
};
|
||
|
||
Silk.prototype.sparklePoint = function(p) {
|
||
var color, opacity;
|
||
if (this.sparkle) {
|
||
opacity = 0.8 * p.life / this.startLife;
|
||
color = d3.rgb(this.ctx.strokeStyle).brighter(2).toString();
|
||
return this.sparks.add(p.x, p.y, {
|
||
a: opacity,
|
||
color: color
|
||
});
|
||
}
|
||
};
|
||
|
||
Silk.prototype.serialize = function() {
|
||
var cereal, key, p, value, _i, _len, _ref1, _ref2;
|
||
_ref1 = this.curve;
|
||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||
p = _ref1[_i];
|
||
delete p.__x__;
|
||
delete p.__y__;
|
||
}
|
||
cereal = {};
|
||
_ref2 = Silk.initialState;
|
||
for (key in _ref2) {
|
||
if (!__hasProp.call(_ref2, key)) continue;
|
||
value = _ref2[key];
|
||
cereal[key] = this[key];
|
||
}
|
||
cereal.curve = $.extend(true, [], this.curve);
|
||
return cereal;
|
||
};
|
||
|
||
Silk.initialState = {
|
||
type: 'silk',
|
||
version: 1,
|
||
time: 0,
|
||
frameTime: 0,
|
||
completed: false,
|
||
startDrawingOnceCompleted: false,
|
||
stopDrawingOnceCompleted: false,
|
||
brushScale: 1,
|
||
scaleLineWidth: true,
|
||
startLife: 150,
|
||
startOpacity: 0.09,
|
||
color: '#276f9b',
|
||
highlightColor: '#276f9b',
|
||
highlightMode: 'velocity',
|
||
eraserColor: '#000000',
|
||
velocityColorScaleExponent: 1.5,
|
||
velocityColorScaleDomainLow: 10,
|
||
velocityColorScaleDomainHigh: 30,
|
||
timeColorScaleDomainLow: 0,
|
||
timeColorScaleDomainHigh: 350,
|
||
timeColorScaleTime: 0,
|
||
compositeOperation: 'lighter',
|
||
noiseForceScale: 1,
|
||
noiseSpaceScale: 0.02,
|
||
noiseTimeScale: 0.005,
|
||
noiseOffset: 0,
|
||
noiseOctaves: 8,
|
||
noiseFallout: 0.65,
|
||
noiseAngleScale: 5 * Math.PI,
|
||
noiseAngleOffset: 0,
|
||
initialVelocityForceScale: 0.3,
|
||
initialVelocityDecay: 0.98,
|
||
windForceScale: 0,
|
||
windAngle: Math.PI,
|
||
rotateAnglesAroundSymmetryAxis: true,
|
||
friction: 0.975,
|
||
restingDistance: 0,
|
||
rigidity: 0.2,
|
||
symType: 'point',
|
||
symNumRotations: 1,
|
||
symMirror: true,
|
||
symCenter: 'centerScreen',
|
||
symCenterX: 0,
|
||
symCenterY: 0,
|
||
spiralCopies: 1,
|
||
spiralAngle: 0.75 * Math.PI,
|
||
curve: null,
|
||
originalLogicalWidth: null,
|
||
originalLogicalHeight: null,
|
||
drawsPerFrame: 5
|
||
};
|
||
|
||
return Silk;
|
||
|
||
})();
|
||
|
||
PointPreviewSilk = (function(_super) {
|
||
__extends(PointPreviewSilk, _super);
|
||
|
||
function PointPreviewSilk(ctx, scaleInfo, state) {
|
||
var baseColor;
|
||
this.ctx = ctx;
|
||
this.scaleInfo = scaleInfo;
|
||
if (state == null) {
|
||
state = {};
|
||
}
|
||
this.drawCurve = __bind(this.drawCurve, this);
|
||
PointPreviewSilk.__super__.constructor.apply(this, arguments);
|
||
if (this.previewRadius == null) {
|
||
this.previewRadius = 4;
|
||
}
|
||
if (this.previewOpacity == null) {
|
||
this.previewOpacity = 1;
|
||
}
|
||
if (this.targetOpacity == null) {
|
||
this.targetOpacity = 1;
|
||
}
|
||
if (this.visible == null) {
|
||
this.visible = true;
|
||
}
|
||
switch (this.previewEmphasis) {
|
||
case 'spiral':
|
||
this.previewEmphasisFn = function(instr) {
|
||
return instr.spiralIndex > 0;
|
||
};
|
||
break;
|
||
case 'rotation':
|
||
this.previewEmphasisFn = function(instr) {
|
||
return instr.spiralIndex === 0 && !instr.mirror;
|
||
};
|
||
break;
|
||
case 'mirror':
|
||
this.previewEmphasisFn = function(instr) {
|
||
return instr.mirror;
|
||
};
|
||
}
|
||
baseColor = (function() {
|
||
switch (this.hightlightMode) {
|
||
case 'time':
|
||
return this.highlightColor;
|
||
case 'velocity':
|
||
return this.color;
|
||
}
|
||
}).call(this);
|
||
}
|
||
|
||
PointPreviewSilk.prototype.frame = function() {
|
||
if (!this.curve.length) {
|
||
return;
|
||
}
|
||
switch (false) {
|
||
case !(this.previewOpacity > this.targetOpacity):
|
||
this.previewOpacity -= 0.05;
|
||
if (this.previewOpacity < this.targetOpacity) {
|
||
this.previewOpacity = this.targetOpacity;
|
||
}
|
||
break;
|
||
case !(this.previewOpacity < this.targetOpacity):
|
||
this.previewOpacity += 0.05;
|
||
if (this.previewOpacity > this.targetOpacity) {
|
||
this.previewOpacity = this.targetOpacity;
|
||
}
|
||
}
|
||
if (this.completed && this.previewOpacity === 0) {
|
||
return this.finish();
|
||
} else {
|
||
if (this.visible) {
|
||
return this.draw();
|
||
}
|
||
}
|
||
};
|
||
|
||
PointPreviewSilk.prototype.setVisible = function(visible) {
|
||
this.visible = visible;
|
||
};
|
||
|
||
PointPreviewSilk.prototype.setTargetOpacity = function(targetOpacity) {
|
||
this.targetOpacity = targetOpacity;
|
||
};
|
||
|
||
PointPreviewSilk.prototype.fadeIn = function() {
|
||
return this.targetOpacity = 1;
|
||
};
|
||
|
||
PointPreviewSilk.prototype.fadeOut = function() {
|
||
return this.targetOpacity = 0;
|
||
};
|
||
|
||
PointPreviewSilk.prototype.fadeInFromZero = function() {
|
||
this.previewOpacity = 0;
|
||
return this.targetOpacity = 1;
|
||
};
|
||
|
||
PointPreviewSilk.prototype.fadeOutFromOne = function() {
|
||
this.previewOpacity = 1;
|
||
return this.targetOpacity = 0;
|
||
};
|
||
|
||
PointPreviewSilk.prototype.completeAndFadeOut = function() {
|
||
this.complete();
|
||
return this.fadeOut();
|
||
};
|
||
|
||
PointPreviewSilk.prototype.drawCurve = function(instr) {
|
||
var fillStyle, p;
|
||
p = this.curve[0];
|
||
this.ctx.beginPath();
|
||
fillStyle = this.ctx.fillStyle;
|
||
this.ctx.fillStyle = '#4e4866';
|
||
if (typeof this.previewEmphasisFn === "function" ? this.previewEmphasisFn(instr) : void 0) {
|
||
this.ctx.fillStyle = '#ffffff';
|
||
}
|
||
this.ctx.arc(p.x, p.y, instr.scale * this.previewRadius, 0, 2 * Math.PI, false);
|
||
this.ctx.fill();
|
||
this.ctx.fillStyle = fillStyle;
|
||
this.ctx.globalCompositeOperation = 'source-over';
|
||
this.ctx.strokeWidth = 0.5;
|
||
this.ctx.stroke();
|
||
return this.ctx.closePath();
|
||
};
|
||
|
||
PointPreviewSilk.prototype.addPoint = function(x, y, vx, vy) {
|
||
if (this.completed) {
|
||
return;
|
||
}
|
||
return this.curve = [
|
||
{
|
||
x: x,
|
||
y: y
|
||
}
|
||
];
|
||
};
|
||
|
||
PointPreviewSilk.prototype.setColor = function() {
|
||
this.ctx.globalAlpha = this.previewOpacity;
|
||
return this.ctx.globalCompositeOperation = this.globalCompositeOperation;
|
||
};
|
||
|
||
PointPreviewSilk.prototype.serialize = function() {
|
||
var state;
|
||
state = PointPreviewSilk.__super__.serialize.call(this);
|
||
state.curve = [];
|
||
return state;
|
||
};
|
||
|
||
return PointPreviewSilk;
|
||
|
||
})(Silk);
|
||
|
||
this.Silks = (function() {
|
||
function Silks(container, silkCanvas, bufferCanvas, sparksCanvas) {
|
||
this.container = container;
|
||
this.silkCanvas = silkCanvas;
|
||
this.bufferCanvas = bufferCanvas;
|
||
this.sparksCanvas = sparksCanvas;
|
||
this.replayReplay = __bind(this.replayReplay, this);
|
||
this.undo = __bind(this.undo, this);
|
||
this.clear = __bind(this.clear, this);
|
||
this.all = {};
|
||
this.sparks = new Sparks(this.sparksCanvas);
|
||
this.recorder = new Recorder(this);
|
||
this.silkCtx = this.silkCanvas.getContext('2d');
|
||
this.sparksCtx = this.sparksCanvas.getContext('2d');
|
||
this.sparksCanvas._util = new CanvasUtil(this.sparksCanvas);
|
||
this.bufferCanvas._util = new CanvasUtil(this.bufferCanvas);
|
||
this.silkCanvas._util = new CanvasUtil(this.silkCanvas);
|
||
this.backgroundColor = '#000';
|
||
this.snapshotState = {
|
||
justCleared: ko.observable(true),
|
||
canUndo: ko.observable(false)
|
||
};
|
||
this.previewSilks = {};
|
||
this.inputPreviewSilk = null;
|
||
this.inputPreviewSilkId = null;
|
||
this.nextUndoIsRedo = ko.observable(false);
|
||
this.undoSnapshot = null;
|
||
this.clearUndoSnapshot = null;
|
||
this.silkSettingsState = $.extend(true, {}, Silk.initialState);
|
||
this.fillSilkCanvas();
|
||
this.drawInputPreview = true;
|
||
this.frameTime = 0;
|
||
}
|
||
|
||
Silks.prototype.scaleInfo = function() {
|
||
return {
|
||
logicalWidth: this.silkCanvas._util.widthOnScreen,
|
||
logicalHeight: this.silkCanvas._util.heightOnScreen
|
||
};
|
||
};
|
||
|
||
Silks.prototype.frame = function(dt) {
|
||
var id, silk, _ref1, _ref2, _results;
|
||
this.frameTime++;
|
||
this.recorder.frame();
|
||
if (this.trackingInput) {
|
||
this.inputFrame();
|
||
}
|
||
_ref1 = this.all;
|
||
for (id in _ref1) {
|
||
silk = _ref1[id];
|
||
silk.frame();
|
||
if (silk.isFinishedDrawing()) {
|
||
delete this.all[id];
|
||
}
|
||
}
|
||
if (this.allSilksCompleted()) {
|
||
this.recorder.stopRecording();
|
||
}
|
||
this.sparks.frame(dt);
|
||
_ref2 = this.previewSilks;
|
||
_results = [];
|
||
for (id in _ref2) {
|
||
silk = _ref2[id];
|
||
if (!(this.drawInputPreview === false && id === this.inputPreviewSilkId)) {
|
||
silk.frame();
|
||
}
|
||
if (silk.isFinishedDrawing()) {
|
||
_results.push(delete this.previewSilks[id]);
|
||
} else {
|
||
_results.push(void 0);
|
||
}
|
||
}
|
||
return _results;
|
||
};
|
||
|
||
Silks.prototype.randomId = function() {
|
||
return Math.round(Math.random() * 9999999999) + '';
|
||
};
|
||
|
||
Silks.prototype.extendSilkSettingsState = function(state) {
|
||
return $.extend(this.silkSettingsState, state);
|
||
};
|
||
|
||
Silks.prototype.silkStartState = function() {
|
||
var state;
|
||
state = $.extend(true, {}, this.silkSettingsState);
|
||
if (state.symCenter === 'centerScreen') {
|
||
$.extend(state, {
|
||
symCenterX: this.silkCanvas._util.halfWidthOnScreen,
|
||
symCenterY: this.silkCanvas._util.halfHeightOnScreen
|
||
});
|
||
}
|
||
return state;
|
||
};
|
||
|
||
Silks.prototype.add = function(id, state) {
|
||
var color, scaleInfo, silk;
|
||
this.undoSnapshot = this.takeSnapshot();
|
||
this.clearUndoSnapshot = null;
|
||
this.nextUndoIsRedo(false);
|
||
this.snapshotState.justCleared(false);
|
||
this.recorder.startRecording();
|
||
if (id == null) {
|
||
id = this.randomId();
|
||
}
|
||
scaleInfo = this.scaleInfo();
|
||
if (state != null) {
|
||
silk = new Silk(this.silkCtx, scaleInfo, state);
|
||
} else if (this.inputPreviewSilkId != null) {
|
||
silk = new Silk(this.silkCtx, scaleInfo, this.inputPreviewSilk.serialize());
|
||
} else {
|
||
silk = new Silk(this.silkCtx, scaleInfo, this.silkStartState());
|
||
}
|
||
silk.setSparks(this.sparks);
|
||
if (typeof Hue !== "undefined" && Hue !== null) {
|
||
color = d3.hsl(silk.color);
|
||
d('color:', color);
|
||
Hue.setBri(3, parseInt(255 * color.l));
|
||
d(color.h, color.s, color.l);
|
||
d(Hue.setHueSat(3, parseInt(65536 * (color.h / 360)), parseInt(255 * color.s)));
|
||
}
|
||
this.recorder.rec('add', id, silk.serialize());
|
||
this.all[id] = silk;
|
||
return id;
|
||
};
|
||
|
||
Silks.prototype.addPreviewSilk = function(state) {
|
||
var id, startState;
|
||
id = this.randomId();
|
||
startState = this.silkStartState();
|
||
if (state != null) {
|
||
$.extend(startState, state);
|
||
}
|
||
this.previewSilks[id] = new PointPreviewSilk(this.sparksCtx, this.scaleInfo(), startState);
|
||
return id;
|
||
};
|
||
|
||
Silks.prototype.getPreviewSilk = function(id) {
|
||
return this.previewSilks[id];
|
||
};
|
||
|
||
Silks.prototype.removePreviewSilk = function(id) {
|
||
return delete this.previewSilks[id];
|
||
};
|
||
|
||
Silks.prototype.nextInputPreviewSilk = function() {
|
||
if (this.inputPreviewSilkId != null) {
|
||
this.inputPreviewSilk.completeAndFadeOut();
|
||
}
|
||
this.inputPreviewSilkId = this.addPreviewSilk();
|
||
this.inputPreviewSilk = this.previewSilks[this.inputPreviewSilkId];
|
||
return this.inputPreviewSilk.fadeInFromZero();
|
||
};
|
||
|
||
Silks.prototype.addPoint = function(id, x, y, vx, vy, fromRecording) {
|
||
var scaleInfo;
|
||
if (!fromRecording) {
|
||
0;
|
||
scaleInfo = this.scaleInfo();
|
||
}
|
||
this.recorder.rec('addPoint', id, x, y, vx, vy, true);
|
||
if (id in this.all) {
|
||
return this.all[id].addPoint(x, y, vx, vy);
|
||
}
|
||
};
|
||
|
||
Silks.prototype.complete = function(id) {
|
||
if (id in this.all) {
|
||
this.all[id].complete();
|
||
return this.recorder.rec('complete', id);
|
||
}
|
||
};
|
||
|
||
Silks.prototype.allSilksCompleted = function() {
|
||
var id, silk, _ref1;
|
||
_ref1 = this.all;
|
||
for (id in _ref1) {
|
||
silk = _ref1[id];
|
||
if (!silk.completed) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
};
|
||
|
||
Silks.prototype.clear = function(withParticles) {
|
||
var id;
|
||
if (withParticles == null) {
|
||
withParticles = true;
|
||
}
|
||
if (!this.snapshotState.justCleared()) {
|
||
this.clearUndoSnapshot = this.takeSnapshot();
|
||
this.clearUndoSnapshot.nextUndoIsRedo = this.nextUndoIsRedo();
|
||
this.nextUndoIsRedo(false);
|
||
this.snapshotState.justCleared(true);
|
||
this.recorder.stopRecording();
|
||
this.recorder.ejectAll();
|
||
}
|
||
for (id in this.all) {
|
||
this.complete(id);
|
||
}
|
||
this.all = {};
|
||
this.swapSilkCanvii();
|
||
if (withParticles) {
|
||
return this.addParticles();
|
||
}
|
||
};
|
||
|
||
Silks.prototype.addParticles = function(dir, num) {
|
||
var angle, h, h2, i, scale, spark, w, w2, x, y, _i, _results;
|
||
if (dir == null) {
|
||
dir = 1;
|
||
}
|
||
if (num == null) {
|
||
num = 100;
|
||
}
|
||
w = this.silkCanvas._util.widthOnScreen;
|
||
h = this.silkCanvas._util.heightOnScreen;
|
||
w2 = w / 2;
|
||
h2 = h / 2;
|
||
scale = .25 * dir;
|
||
_results = [];
|
||
for (i = _i = 1; 1 <= num ? _i <= num : _i >= num; i = 1 <= num ? ++_i : --_i) {
|
||
x = _.random(w);
|
||
y = _.random(h);
|
||
angle = Math.atan2(y - h2, x - w2);
|
||
_results.push(spark = this.sparks.add(x, y, {
|
||
a: 1,
|
||
color: '#ffffff',
|
||
vx: scale * Math.cos(angle),
|
||
vy: scale * Math.sin(angle),
|
||
lifespan: 25 + _.random(60)
|
||
}));
|
||
}
|
||
return _results;
|
||
};
|
||
|
||
Silks.prototype.swapSilkCanvii = function() {
|
||
var _ref1;
|
||
_ref1 = [this.bufferCanvas, this.silkCanvas], this.silkCanvas = _ref1[0], this.bufferCanvas = _ref1[1];
|
||
this.silkCtx = this.silkCanvas.getContext('2d');
|
||
$(this.silkCanvas).removeClass('buffer onepacity').addClass('active zeropacity').insertBefore($(this.bufferCanvas));
|
||
$(this.bufferCanvas).removeClass('active').addClass('buffer onepacity');
|
||
this.fillSilkCanvas();
|
||
return _.defer((function(_this) {
|
||
return function() {
|
||
return $(_this.silkCanvas).removeClass('zeropacity');
|
||
};
|
||
})(this));
|
||
};
|
||
|
||
Silks.prototype.fillCanvas = function(canvas, color) {
|
||
var ctx;
|
||
ctx = canvas.getContext('2d');
|
||
ctx.globalCompositeOperation = 'source-over';
|
||
ctx.globalAlpha = 1;
|
||
ctx.fillStyle = color;
|
||
if (color === '' || color === 'transparent') {
|
||
return ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||
} else {
|
||
return ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||
}
|
||
};
|
||
|
||
Silks.prototype.clearSilkCanvas = function() {
|
||
return this.fillCanvas(this.silkCanvas, 'transparent');
|
||
};
|
||
|
||
Silks.prototype.fillSilkCanvas = function(color) {
|
||
if (color == null) {
|
||
color = this.backgroundColor;
|
||
}
|
||
return this.fillCanvas(this.silkCanvas, color);
|
||
};
|
||
|
||
Silks.prototype.nextSnapshotCanvas = (function() {
|
||
var canvii, i, _i;
|
||
canvii = [];
|
||
for (i = _i = 1; _i <= 4; i = ++_i) {
|
||
canvii.push(document.createElement('canvas'));
|
||
}
|
||
i = 0;
|
||
return function() {
|
||
var canvas;
|
||
canvas = canvii[i];
|
||
i = (i + 1) % canvii.length;
|
||
return canvas;
|
||
};
|
||
})();
|
||
|
||
Silks.prototype.takeSnapshot = function() {
|
||
var all, binding, bindingValues, canvas, id, name, recorder, silk, sizeOnScreen, _ref1, _ref2;
|
||
canvas = this.nextSnapshotCanvas();
|
||
canvas.width = this.silkCanvas.width;
|
||
canvas.height = this.silkCanvas.height;
|
||
canvas.getContext('2d').drawImage(this.silkCanvas, 0, 0, canvas.width, canvas.height);
|
||
all = {};
|
||
_ref1 = this.all;
|
||
for (id in _ref1) {
|
||
silk = _ref1[id];
|
||
all[id] = silk.serialize();
|
||
}
|
||
recorder = this.recorder.serialize();
|
||
bindingValues = {};
|
||
_ref2 = this.snapshotState;
|
||
for (name in _ref2) {
|
||
binding = _ref2[name];
|
||
bindingValues[name] = binding();
|
||
}
|
||
sizeOnScreen = {
|
||
width: this.silkCanvas._util.widthOnScreen,
|
||
height: this.silkCanvas._util.heightOnScreen
|
||
};
|
||
return {
|
||
all: all,
|
||
canvas: canvas,
|
||
recorder: recorder,
|
||
sizeOnScreen: sizeOnScreen,
|
||
bindingValues: bindingValues
|
||
};
|
||
};
|
||
|
||
Silks.prototype.loadSnapshot = function(data) {
|
||
var id, name, scaleInfo, state, value, _ref1, _ref2, _results;
|
||
this.silkCtx.globalAlpha = 1;
|
||
this.fillSilkCanvas();
|
||
this.silkCtx.drawImage(data.canvas, (this.silkCanvas._util.widthOnScreen - data.sizeOnScreen.width) / 2, (this.silkCanvas._util.heightOnScreen - data.sizeOnScreen.height) / 2, data.sizeOnScreen.width, data.sizeOnScreen.height);
|
||
this.all = {};
|
||
scaleInfo = this.scaleInfo();
|
||
_ref1 = data.all;
|
||
for (id in _ref1) {
|
||
state = _ref1[id];
|
||
this.all[id] = new Silk(this.silkCtx, scaleInfo, state);
|
||
}
|
||
this.recorder.unserialize(data.recorder);
|
||
_ref2 = data.bindingValues;
|
||
_results = [];
|
||
for (name in _ref2) {
|
||
value = _ref2[name];
|
||
_results.push(this.snapshotState[name](value));
|
||
}
|
||
return _results;
|
||
};
|
||
|
||
Silks.prototype.undo = function() {
|
||
var snapshot;
|
||
switch (false) {
|
||
case this.clearUndoSnapshot == null:
|
||
this.swapSilkCanvii();
|
||
this.loadSnapshot(this.clearUndoSnapshot);
|
||
this.nextUndoIsRedo(this.clearUndoSnapshot.nextUndoIsRedo);
|
||
this.clearUndoSnapshot = null;
|
||
return this.addParticles(-1);
|
||
case this.undoSnapshot == null:
|
||
snapshot = this.takeSnapshot();
|
||
this.swapSilkCanvii();
|
||
this.loadSnapshot(this.undoSnapshot);
|
||
if (this.nextUndoIsRedo()) {
|
||
this.addParticles();
|
||
} else {
|
||
this.addParticles(-1);
|
||
}
|
||
this.nextUndoIsRedo(!this.nextUndoIsRedo());
|
||
return this.undoSnapshot = snapshot;
|
||
}
|
||
};
|
||
|
||
Silks.prototype.replayReplay = function() {
|
||
var tape;
|
||
tape = this.recorder.ejectAll();
|
||
this.clear(false);
|
||
return this.recorder.play(tape);
|
||
};
|
||
|
||
Silks.prototype.getReplay = function() {
|
||
return this.recorder.get();
|
||
};
|
||
|
||
Silks.prototype.playReplay = function(tape) {
|
||
return this.recorder.play(tape);
|
||
};
|
||
|
||
Silks.prototype.makeThumb = function() {
|
||
var canvas, ctx, drawHeight, drawWidth, r, size;
|
||
canvas = document.createElement('canvas');
|
||
size = 300;
|
||
canvas.width = canvas.height = size;
|
||
drawWidth = drawHeight = size;
|
||
r = this.silkCanvas.width / this.silkCanvas.height;
|
||
if (r < 1) {
|
||
drawHeight *= r;
|
||
} else {
|
||
drawWidth *= r;
|
||
}
|
||
ctx = canvas.getContext('2d');
|
||
ctx.fillStyle = this.backgroundColor;
|
||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||
ctx.imageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = ctx.mozImageSmoothingEnabled = true;
|
||
ctx.drawImage(this.silkCanvas, (size - drawWidth) / 2, (size - drawHeight) / 2, drawWidth, drawHeight);
|
||
return canvas.toDataURL('image/png').replace(/^data:image\/\w+;base64,/, '');
|
||
};
|
||
|
||
Silks.prototype.getImageUrl = function() {
|
||
return this.silkCanvas.toDataURL('image/png');
|
||
};
|
||
|
||
Silks.prototype.inputFrame = function() {
|
||
var _ref1;
|
||
if (this.pinputX != null) {
|
||
if (this.inputIsActive) {
|
||
this.addPoint(this.activeSilkId, this.inputX, this.inputY, this.inputX - this.pinputX, this.inputY - this.pinputY);
|
||
} else {
|
||
if ((_ref1 = this.inputPreviewSilk) != null) {
|
||
_ref1.addPoint(this.inputX, this.inputY, this.inputX - this.pinputX, this.inputY - this.pinputY);
|
||
}
|
||
}
|
||
}
|
||
this.pinputX = this.inputX;
|
||
return this.pinputY = this.inputY;
|
||
};
|
||
|
||
Silks.prototype.initInputEvents = function() {
|
||
var fadeTimer, previewActive;
|
||
this.trackingInput = true;
|
||
this.inputX = this.inputY = null;
|
||
this.pinputX = this.pinputY = null;
|
||
this.inputIsActive = false;
|
||
this.nextInputPreviewSilk();
|
||
fadeTimer = null;
|
||
previewActive = (function(_this) {
|
||
return function() {
|
||
var _ref1;
|
||
if (!_this.inputIsActive) {
|
||
if ((_ref1 = _this.inputPreviewSilk) != null) {
|
||
_ref1.fadeIn();
|
||
}
|
||
}
|
||
clearTimeout(fadeTimer);
|
||
return fadeTimer = setTimeout(function() {
|
||
var _ref2;
|
||
return (_ref2 = _this.inputPreviewSilk) != null ? _ref2.fadeOut() : void 0;
|
||
}, 3000);
|
||
};
|
||
})(this);
|
||
$(this.sparksCanvas).mousedown((function(_this) {
|
||
return function(e) {
|
||
if (e.button === 2) {
|
||
return;
|
||
}
|
||
_this.updateInputFromEvent(e);
|
||
_this.stopPreviewingInput();
|
||
_this.inputStarted();
|
||
return false;
|
||
};
|
||
})(this)).mousemove((function(_this) {
|
||
return function(e) {
|
||
_this.updateInputFromEvent(e);
|
||
return previewActive();
|
||
};
|
||
})(this)).mouseup((function(_this) {
|
||
return function(e) {
|
||
_this.updateInputFromEvent(e);
|
||
if (_this.inputIsActive) {
|
||
_this.nextInputPreviewSilk();
|
||
_this.inputEnded();
|
||
}
|
||
return previewActive();
|
||
};
|
||
})(this)).mouseenter((function(_this) {
|
||
return function(e) {
|
||
return _this.nextInputPreviewSilk();
|
||
};
|
||
})(this)).mouseleave((function(_this) {
|
||
return function(e) {
|
||
_this.stopPreviewingInput();
|
||
return clearTimeout(fadeTimer);
|
||
};
|
||
})(this)).bind('touchstart', (function(_this) {
|
||
return function(e) {
|
||
var _ref1;
|
||
_this.updateInputFromEvent(_this.firstTouch(e));
|
||
_ref1 = [_this.inputX, _this.inputY], _this.pinputX = _ref1[0], _this.pinputY = _ref1[1];
|
||
_this.inputStarted();
|
||
return false;
|
||
};
|
||
})(this)).bind('touchmove', (function(_this) {
|
||
return function(e) {
|
||
_this.updateInputFromEvent(_this.firstTouch(e));
|
||
return false;
|
||
};
|
||
})(this)).bind('touchend', (function(_this) {
|
||
return function(e) {
|
||
_this.updateInputFromEvent(_this.firstTouch(e));
|
||
_this.inputEnded();
|
||
return false;
|
||
};
|
||
})(this));
|
||
return $(window).mouseup((function(_this) {
|
||
return function() {
|
||
return _this.inputIsActive = false;
|
||
};
|
||
})(this));
|
||
};
|
||
|
||
Silks.prototype.stopPreviewingInput = function() {
|
||
var _ref1;
|
||
if ((_ref1 = this.inputPreviewSilk) != null) {
|
||
_ref1.completeAndFadeOut();
|
||
}
|
||
return this.inputPreviewSilkId = null;
|
||
};
|
||
|
||
Silks.prototype.inputStarted = function() {
|
||
this.inputIsActive = true;
|
||
this.complete(this.activeSilkId);
|
||
return this.activeSilkId = this.add();
|
||
};
|
||
|
||
Silks.prototype.inputEnded = function() {
|
||
this.complete(this.activeSilkId);
|
||
return this.inputIsActive = false;
|
||
};
|
||
|
||
Silks.prototype.firstTouch = function(e) {
|
||
return e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
|
||
};
|
||
|
||
Silks.prototype.updateInputFromEvent = function(e) {
|
||
this.inputX = e.pageX - this.container.offsetLeft;
|
||
return this.inputY = e.pageY - this.container.offsetTop;
|
||
};
|
||
|
||
Silks.prototype.getCenterCoordinates = function() {
|
||
return [this.silkCanvas._util.halfWidthOnScreen, this.silkCanvas._util.halfHeightOnScreen];
|
||
};
|
||
|
||
return Silks;
|
||
|
||
})();
|
||
|
||
Sparks = (function() {
|
||
function Sparks(canvas) {
|
||
this.canvas = canvas;
|
||
this.ctx = canvas.getContext('2d');
|
||
this.ctx.globalCompositeOperation = 'lighter';
|
||
this.points = [];
|
||
}
|
||
|
||
Sparks.prototype.frame = function(dt) {
|
||
var ctx, p, points, _i, _len;
|
||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||
points = this.points;
|
||
ctx = this.ctx;
|
||
while (points.length > 0 && points[0].age >= points[0].lifespan) {
|
||
points.shift();
|
||
}
|
||
for (_i = 0, _len = points.length; _i < _len; _i++) {
|
||
p = points[_i];
|
||
p.x += p.vx * dt;
|
||
p.y += p.vy * dt;
|
||
p.age++;
|
||
if (p.age < p.lifespan) {
|
||
ctx.beginPath();
|
||
ctx.globalAlpha = p.a * (1 - p.age / p.lifespan);
|
||
if (p.invertA) {
|
||
ctx.globalAlpha = 1 - ctx.globalAlpha;
|
||
}
|
||
ctx.fillStyle = p.color;
|
||
ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, false);
|
||
ctx.fill();
|
||
ctx.closePath();
|
||
}
|
||
}
|
||
};
|
||
|
||
Sparks.prototype.add = function(x, y, params) {
|
||
var p;
|
||
p = $.extend({
|
||
x: x,
|
||
y: y,
|
||
a: 1,
|
||
age: 0,
|
||
lifespan: 75,
|
||
radius: 0.75,
|
||
radiusScale: 1,
|
||
color: '#ffffff',
|
||
vx: 2 * Math.random() - 1,
|
||
vy: Math.random() - 1
|
||
}, params);
|
||
p.radius *= p.radiusScale;
|
||
this.points.push(p);
|
||
return p;
|
||
};
|
||
|
||
return Sparks;
|
||
|
||
})();
|
||
|
||
Tape = (function() {
|
||
function Tape(target, recordOnly) {
|
||
this.target = target;
|
||
this.recordOnly = recordOnly != null ? recordOnly : false;
|
||
this.unserialize = __bind(this.unserialize, this);
|
||
this.serialize = __bind(this.serialize, this);
|
||
this.eject = __bind(this.eject, this);
|
||
this.get = __bind(this.get, this);
|
||
this.rewind = __bind(this.rewind, this);
|
||
this.fastforward = __bind(this.fastforward, this);
|
||
this.stop = __bind(this.stop, this);
|
||
this.play = __bind(this.play, this);
|
||
this.load = __bind(this.load, this);
|
||
this.frame = __bind(this.frame, this);
|
||
this.rec = __bind(this.rec, this);
|
||
this.tape = {};
|
||
this.time = 0;
|
||
this.playing = false;
|
||
}
|
||
|
||
Tape.prototype.rec = function() {
|
||
var info, _base, _name;
|
||
info = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
||
if ((_base = this.tape)[_name = this.time] == null) {
|
||
_base[_name] = [];
|
||
}
|
||
return this.tape[this.time].push(info);
|
||
};
|
||
|
||
Tape.prototype.frame = function() {
|
||
var args, info, name, _i, _len, _ref1, _ref2;
|
||
if (this.playing) {
|
||
if (!this.recordOnly && this.time in this.tape) {
|
||
_ref1 = this.tape[this.time];
|
||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||
info = _ref1[_i];
|
||
name = info[0], args = 2 <= info.length ? __slice.call(info, 1) : [];
|
||
(_ref2 = this.target[name]).call.apply(_ref2, [this.target].concat(__slice.call(args)));
|
||
}
|
||
}
|
||
return this.time += 1;
|
||
}
|
||
};
|
||
|
||
Tape.prototype.load = function(tape) {
|
||
this.tape = tape;
|
||
return this.rewind();
|
||
};
|
||
|
||
Tape.prototype.play = function() {
|
||
return this.playing = true;
|
||
};
|
||
|
||
Tape.prototype.stop = function() {
|
||
return this.playing = false;
|
||
};
|
||
|
||
Tape.prototype.fastforward = function(amount) {
|
||
return this.time += amount;
|
||
};
|
||
|
||
Tape.prototype.rewind = function() {
|
||
return this.time = 0;
|
||
};
|
||
|
||
Tape.prototype.get = function() {
|
||
return this.tape;
|
||
};
|
||
|
||
Tape.prototype.eject = function() {
|
||
var tape;
|
||
tape = this.tape;
|
||
this.load({});
|
||
return tape;
|
||
};
|
||
|
||
Tape.prototype.serialize = function() {
|
||
return {
|
||
time: this.time,
|
||
playing: this.playing,
|
||
tape: $.extend(true, {}, this.tape)
|
||
};
|
||
};
|
||
|
||
Tape.prototype.unserialize = function(data) {
|
||
return this.tape = data.tape, this.time = data.time, this.playing = data.playing, data;
|
||
};
|
||
|
||
return Tape;
|
||
|
||
})();
|
||
|
||
Recorder = (function() {
|
||
function Recorder(target) {
|
||
this.target = target;
|
||
this.recTape = new Tape(this.target, true);
|
||
this.playTape = new Tape(this.target);
|
||
this.rec = this.recTape.rec;
|
||
this.get = this.recTape.get;
|
||
}
|
||
|
||
Recorder.prototype.frame = function() {
|
||
this.recTape.frame();
|
||
return this.playTape.frame();
|
||
};
|
||
|
||
Recorder.prototype.play = function(tape) {
|
||
if (tape != null) {
|
||
this.recTape.eject();
|
||
this.playTape.load(tape);
|
||
}
|
||
return this.playTape.play();
|
||
};
|
||
|
||
Recorder.prototype.stopPlaying = function() {
|
||
return this.playTape.stop();
|
||
};
|
||
|
||
Recorder.prototype.startRecording = function() {
|
||
return this.recTape.play();
|
||
};
|
||
|
||
Recorder.prototype.stopRecording = function() {
|
||
return this.recTape.stop();
|
||
};
|
||
|
||
Recorder.prototype.ejectAll = function() {
|
||
this.playTape.eject();
|
||
return this.recTape.eject();
|
||
};
|
||
|
||
Recorder.prototype.serialize = function() {
|
||
return {
|
||
recTape: this.recTape.serialize(),
|
||
playTape: this.playTape.serialize()
|
||
};
|
||
};
|
||
|
||
Recorder.prototype.unserialize = function(data) {
|
||
this.recTape.unserialize(data.recTape);
|
||
return this.playTape.unserialize(data.playTape);
|
||
};
|
||
|
||
return Recorder;
|
||
|
||
})();
|
||
|
||
this.initSound = function() {
|
||
var SoundController, bg, bgIntro, bgIntroPlayed, bgIntroVolume, bgMusicVolume, blipEffect, bloopEffect, context, controls, drawSparkle, drawWhoosh, drawing, fadeInBgMusic, loadSilkSound, loadSound, modulateDrawSound, muteEffects, muteMusic, muteSound, playClearSound, playDrawSound, started, stopDrawSound, urlify;
|
||
this.audioContext = this.audioContext || this.webkitAudioContext;
|
||
SoundController = (function() {
|
||
function SoundController(buffer, ctx) {
|
||
this.buffer = buffer;
|
||
this.ctx = ctx;
|
||
this.setLoop(false);
|
||
this.setVolume(1);
|
||
}
|
||
|
||
SoundController.prototype.setLoop = function(loop) {
|
||
this.loop = loop;
|
||
};
|
||
|
||
SoundController.prototype.setVolume = function(gain) {
|
||
this.gain = gain;
|
||
};
|
||
|
||
SoundController.prototype.createSource = function() {
|
||
this.source = this.ctx.createBufferSource();
|
||
this.source.buffer = this.buffer;
|
||
this.source.loop = this.loop;
|
||
this.source.gain.value = this.gain;
|
||
return this.source.connect(this.ctx.destination);
|
||
};
|
||
|
||
SoundController.prototype.play = function() {
|
||
var _ref1;
|
||
return (_ref1 = this.source) != null ? _ref1.noteOn(0) : void 0;
|
||
};
|
||
|
||
SoundController.prototype.fadeTo = function(gain, duration) {
|
||
var now;
|
||
this.gain = gain;
|
||
now = this.ctx.currentTime;
|
||
this.source.gain.cancelScheduledValues(now);
|
||
this.source.gain.setValueAtTime(this.source.gain.value, now);
|
||
return this.source.gain.linearRampToValueAtTime(this.gain, now + duration);
|
||
};
|
||
|
||
SoundController.prototype.trigger = function(multiplier) {
|
||
var backup, _ref1;
|
||
if (multiplier == null) {
|
||
multiplier = 1;
|
||
}
|
||
backup = this.triggerSource;
|
||
this.triggerSource = this.triggerSourceBk;
|
||
this.triggerSourceBk = backup;
|
||
if ((_ref1 = this.triggerSource) != null) {
|
||
_ref1.noteOff(0);
|
||
}
|
||
this.triggerSource = this.ctx.createBufferSource();
|
||
this.triggerSource.buffer = this.buffer;
|
||
this.triggerSource.loop = this.loop;
|
||
this.triggerSource.gain.value = this.gain * multiplier;
|
||
this.triggerSource.connect(this.ctx.destination);
|
||
return this.triggerSource.noteOn(0);
|
||
};
|
||
|
||
return SoundController;
|
||
|
||
})();
|
||
urlify = function(name) {
|
||
return "http://weavesilk.com.s3.amazonaws.com/sound/" + (escape(name));
|
||
};
|
||
loadSound = function(url, ctx, success, error) {
|
||
var request;
|
||
request = new XMLHttpRequest();
|
||
request.open('GET', url, true);
|
||
request.responseType = 'arraybuffer';
|
||
request.onload = function() {
|
||
d('Loaded sound.');
|
||
return ctx.decodeAudioData(request.response, function(buffer) {
|
||
d("Decoded");
|
||
return typeof success === "function" ? success(buffer) : void 0;
|
||
}, function(arg) {});
|
||
};
|
||
return request.send();
|
||
};
|
||
if (this.audioContext != null) {
|
||
context = new this.audioContext();
|
||
loadSilkSound = function(name, cb) {
|
||
var url;
|
||
url = urlify(name);
|
||
return loadSound(url, context, function(buffer) {
|
||
var sound;
|
||
sound = new SoundController(buffer, context);
|
||
return cb(sound);
|
||
});
|
||
};
|
||
d('Thank the Lord, there is an audio context.');
|
||
playClearSound = (function() {
|
||
var clears, loaded, num, onload;
|
||
clears = [];
|
||
loaded = false;
|
||
onload = function(sound) {
|
||
d('onload', sound);
|
||
sound.setVolume(0.27);
|
||
clears.push(sound);
|
||
return loaded = clears.length === 4;
|
||
};
|
||
loadSilkSound("Clear 1 16-44.m4a", onload);
|
||
num = 1;
|
||
return function() {
|
||
var index;
|
||
if (clears.length > 0) {
|
||
d(clears);
|
||
index = _.random(clears.length - 1);
|
||
clears[index].trigger();
|
||
}
|
||
if (num < 4) {
|
||
loadSilkSound("Clear " + (num + 1) + " 16-44.m4a", onload);
|
||
return num += 1;
|
||
}
|
||
};
|
||
})();
|
||
drawSparkle = null;
|
||
loadSilkSound('Sparks 16-44 -looped.m4a', function(result) {
|
||
drawSparkle = result;
|
||
drawSparkle.setVolume(0);
|
||
drawSparkle.setLoop(true);
|
||
drawSparkle.createSource();
|
||
return drawSparkle.play();
|
||
});
|
||
drawWhoosh = null;
|
||
loadSilkSound('Draw B2 2048 loop.m4a', function(result) {
|
||
d('woosh', result);
|
||
drawWhoosh = result;
|
||
drawWhoosh.setVolume(0);
|
||
drawWhoosh.setLoop(true);
|
||
drawWhoosh.createSource();
|
||
return drawWhoosh.play();
|
||
});
|
||
d('Attempting to load a whoosh');
|
||
drawing = false;
|
||
playDrawSound = function() {
|
||
drawing = true;
|
||
if (drawSparkle != null) {
|
||
drawSparkle.fadeTo(0.01, 0.5);
|
||
}
|
||
return drawWhoosh != null ? drawWhoosh.fadeTo(0.15, 0.5) : void 0;
|
||
};
|
||
stopDrawSound = function() {
|
||
drawing = false;
|
||
if (drawSparkle != null) {
|
||
drawSparkle.fadeTo(0, 1);
|
||
}
|
||
return drawWhoosh != null ? drawWhoosh.fadeTo(0, 1) : void 0;
|
||
};
|
||
modulateDrawSound = function(level) {
|
||
if (!drawing) {
|
||
return;
|
||
}
|
||
level *= 1 / 30;
|
||
level = Math.log(Math.log(level + 1));
|
||
level = _.constrain(level, .15, .6);
|
||
return drawWhoosh != null ? drawWhoosh.fadeTo(level, 0.2) : void 0;
|
||
};
|
||
blipEffect = null;
|
||
loadSilkSound('Palette A5.m4a', function(result) {
|
||
blipEffect = result;
|
||
return blipEffect.setVolume(0.4);
|
||
});
|
||
bloopEffect = null;
|
||
loadSilkSound('Palette A4.m4a', function(result) {
|
||
bloopEffect = result;
|
||
return bloopEffect.setVolume(0.3);
|
||
});
|
||
}
|
||
muteMusic = ko.observable(true);
|
||
muteEffects = ko.observable(true);
|
||
muteSound = ko.computed(function() {
|
||
return muteMusic() && muteEffects();
|
||
});
|
||
started = false;
|
||
bgMusicVolume = 0.50;
|
||
bgIntroVolume = 0.55;
|
||
bg = $('#bg-music')[0];
|
||
bgIntro = $('#bg-music-intro')[0];
|
||
bgIntroPlayed = false;
|
||
fadeInBgMusic = function() {
|
||
var duration, interval, stepSize, target;
|
||
target = bgMusicVolume;
|
||
duration = 15;
|
||
interval = .1;
|
||
stepSize = duration / interval;
|
||
bg.volume = 0;
|
||
bg.play();
|
||
return setTimeout(function() {
|
||
var step;
|
||
return step = setInterval(function() {
|
||
bg.volume += target / stepSize;
|
||
bg.volume = Math.min(bg.volume, target) + 0.01;
|
||
if (bg.volume >= target) {
|
||
return clearInterval(step);
|
||
}
|
||
}, 1000 * interval);
|
||
}, 500);
|
||
};
|
||
controls = {
|
||
muteMusic: muteMusic,
|
||
muteSound: muteSound,
|
||
setMuteMusic: function(val) {
|
||
muteMusic(val);
|
||
if (val) {
|
||
_.save('muteMusic', true);
|
||
} else {
|
||
_.save('muteMusic', false);
|
||
controls.setMuteEffects(false);
|
||
controls.start();
|
||
}
|
||
bgIntro.muted = val;
|
||
return bg.muted = val;
|
||
},
|
||
setMuteEffects: function(val) {
|
||
muteEffects(val);
|
||
if (val) {
|
||
return _.save('muteEffects', true);
|
||
} else {
|
||
return _.save('muteEffects');
|
||
}
|
||
},
|
||
start: function() {
|
||
var check;
|
||
if (started) {
|
||
return;
|
||
}
|
||
if (muteMusic()) {
|
||
return;
|
||
}
|
||
started = true;
|
||
return check = setInterval(function() {
|
||
var bgBuffered, introBuffered;
|
||
bgBuffered = bg.buffered.length > 0 && bg.buffered.end(0) > 10;
|
||
introBuffered = bgIntro.buffered.length > 0 && bgIntro.buffered.end(0) > 2;
|
||
if (introBuffered && !bgIntroPlayed) {
|
||
bgIntro.volume = bgIntroVolume;
|
||
bgIntro.play();
|
||
bgIntroPlayed = true;
|
||
}
|
||
if (bgBuffered) {
|
||
clearInterval(check);
|
||
return fadeInBgMusic();
|
||
}
|
||
}, 100);
|
||
},
|
||
playClearSound: function() {
|
||
if (!muteEffects()) {
|
||
return typeof playClearSound === "function" ? playClearSound() : void 0;
|
||
}
|
||
},
|
||
playDrawSound: function() {
|
||
var ex;
|
||
try {
|
||
if (!muteEffects()) {
|
||
return typeof playDrawSound === "function" ? playDrawSound() : void 0;
|
||
}
|
||
} catch (_error) {
|
||
ex = _error;
|
||
return d('Error playing clear sound', ex);
|
||
}
|
||
},
|
||
stopDrawSound: function() {
|
||
var ex;
|
||
try {
|
||
if (!muteEffects()) {
|
||
return typeof stopDrawSound === "function" ? stopDrawSound() : void 0;
|
||
}
|
||
} catch (_error) {
|
||
ex = _error;
|
||
return d('Error stopping draw sound', ex);
|
||
}
|
||
},
|
||
modulateDrawSound: function(level) {
|
||
var ex;
|
||
try {
|
||
return typeof modulateDrawSound === "function" ? modulateDrawSound(level) : void 0;
|
||
} catch (_error) {
|
||
ex = _error;
|
||
return d('Error modulating draw sound', ex);
|
||
}
|
||
},
|
||
blip: function(multiplier) {
|
||
var ex;
|
||
try {
|
||
if (!muteEffects()) {
|
||
return blipEffect != null ? blipEffect.trigger(multiplier) : void 0;
|
||
}
|
||
} catch (_error) {
|
||
ex = _error;
|
||
return d('Error playing blip sound', ex);
|
||
}
|
||
},
|
||
bloop: function(multiplier) {
|
||
var ex;
|
||
try {
|
||
if (!muteEffects()) {
|
||
return bloopEffect != null ? bloopEffect.trigger(multiplier) : void 0;
|
||
}
|
||
} catch (_error) {
|
||
ex = _error;
|
||
return d('Error playing bloop sound', ex);
|
||
}
|
||
}
|
||
};
|
||
return controls;
|
||
};
|
||
|
||
initTips = function(ui) {
|
||
var hide, index, show, showNext, tips;
|
||
index = -1;
|
||
tips = function() {
|
||
return $('#tips .tip');
|
||
};
|
||
hide = function() {
|
||
return tips().removeClass('showing');
|
||
};
|
||
show = function() {
|
||
var t;
|
||
if (MobileDetect()) {
|
||
return;
|
||
}
|
||
hide();
|
||
t = tips();
|
||
if ((0 <= index && index < t.length)) {
|
||
return t.filter(":eq(" + index + ")").addClass('showing');
|
||
}
|
||
};
|
||
showNext = function() {
|
||
if (MobileDetect()) {
|
||
return;
|
||
}
|
||
index++;
|
||
if (index === 2) {
|
||
ui.showColorPicker(true);
|
||
ui.showSymmetryControls(true);
|
||
_.save('magicTipShown', true);
|
||
}
|
||
return show();
|
||
};
|
||
return {
|
||
hide: hide,
|
||
show: show,
|
||
showNext: showNext
|
||
};
|
||
};
|
||
|
||
initUI = function(silks) {
|
||
var initColorPicker, initPreview, initSlider, initializedUI, makeToggle, mirror, mouseDownOnSlider, mouseOverPreviewableControls, preview, settingObservable, showColorPicker, showSymmetryControls, spiral, symSlider, toggleMirror, toggleSpiral, _ref1, _ref2;
|
||
showColorPicker = ko.observable(false);
|
||
showSymmetryControls = ko.observable(false);
|
||
showColorPicker.subscribe(function(val) {
|
||
return _.save('showColorPicker', val);
|
||
});
|
||
showSymmetryControls.subscribe(function(val) {
|
||
return _.save('showSymmetryControls', val);
|
||
});
|
||
mouseOverPreviewableControls = ko.observable(false);
|
||
mouseDownOnSlider = ko.observable(false);
|
||
initPreview = function() {
|
||
var addPointTimer, changeState, newPreviewSilk, previewSilk, previewSilkId, previewX, previewY, showPreview;
|
||
previewSilk = null;
|
||
previewSilkId = null;
|
||
addPointTimer = null;
|
||
previewX = previewY = null;
|
||
changeState = function(setting, value) {
|
||
var state;
|
||
state = {};
|
||
state[setting] = value;
|
||
return newPreviewSilk(state);
|
||
};
|
||
newPreviewSilk = function(state, previewEmphasis) {
|
||
var hadPreviewSilk, newState, prevState;
|
||
if (state == null) {
|
||
state = {};
|
||
}
|
||
if (previewEmphasis == null) {
|
||
previewEmphasis = null;
|
||
}
|
||
if (!initializedUI) {
|
||
return;
|
||
}
|
||
if (!showPreview()) {
|
||
return;
|
||
}
|
||
hadPreviewSilk = previewSilk != null;
|
||
if (hadPreviewSilk) {
|
||
prevState = previewSilk.serialize();
|
||
prevState.previewOpacity = previewSilk.previewOpacity;
|
||
previewSilk.finish();
|
||
} else {
|
||
prevState = {};
|
||
}
|
||
newState = $.extend(prevState, state, {
|
||
previewRadius: 5
|
||
});
|
||
if (previewEmphasis != null) {
|
||
newState.previewEmphasis = previewEmphasis;
|
||
} else {
|
||
if (previewSilk != null) {
|
||
newState.previewEmphasis = previewSilk.previewEmphasis;
|
||
}
|
||
}
|
||
previewSilkId = silks.addPreviewSilk(newState);
|
||
previewSilk = silks.getPreviewSilk(previewSilkId);
|
||
if (!hadPreviewSilk) {
|
||
previewSilk.fadeInFromZero();
|
||
}
|
||
return previewSilk.addPoint(previewX, previewY);
|
||
};
|
||
$('.control').mouseenter((function(_this) {
|
||
return function() {
|
||
var radius, scale, time;
|
||
mouseOverPreviewableControls(true);
|
||
d('enter', showPreview());
|
||
time = 0;
|
||
radius = 10;
|
||
scale = 0.02;
|
||
return addPointTimer = setInterval(function() {
|
||
var cx, cy, offset, x, y, _ref1;
|
||
_ref1 = silks.getCenterCoordinates(), cx = _ref1[0], cy = _ref1[1];
|
||
x = radius * Math.cos(time * scale);
|
||
y = radius * Math.sin(time * scale);
|
||
offset = 100;
|
||
previewX = cx + x - offset;
|
||
previewY = cy + y - offset;
|
||
if (previewSilk != null) {
|
||
previewSilk.addPoint(previewX, previewY);
|
||
}
|
||
return time++;
|
||
}, 1000 / 30);
|
||
};
|
||
})(this)).mouseleave((function(_this) {
|
||
return function() {
|
||
mouseOverPreviewableControls(false);
|
||
return d('exit');
|
||
};
|
||
})(this)).mousemove((function(_this) {
|
||
return function(e) {};
|
||
})(this));
|
||
showPreview = ko.computed((function(_this) {
|
||
return function() {
|
||
var show;
|
||
show = mouseOverPreviewableControls() || mouseDownOnSlider();
|
||
if (!show) {
|
||
if (previewSilk != null) {
|
||
previewSilk.completeAndFadeOut();
|
||
}
|
||
previewSilk = null;
|
||
previewSilkId = null;
|
||
clearInterval(addPointTimer);
|
||
}
|
||
return show;
|
||
};
|
||
})(this));
|
||
return {
|
||
newPreviewSilk: newPreviewSilk,
|
||
changeState: changeState,
|
||
isActive: function() {
|
||
return previewSilkId != null;
|
||
}
|
||
};
|
||
};
|
||
settingObservable = function(setting) {
|
||
var obs;
|
||
silks.silkSettingsState[setting] = _.load(setting, silks.silkSettingsState[setting]);
|
||
obs = ko.observable(silks.silkSettingsState[setting]);
|
||
return ko.computed({
|
||
read: function() {
|
||
return obs();
|
||
},
|
||
write: function(value) {
|
||
d('Saving', setting, value);
|
||
_.save(setting, value);
|
||
silks.silkSettingsState[setting] = value;
|
||
return obs(value);
|
||
}
|
||
});
|
||
};
|
||
makeToggle = function(setting, onValue, offValue) {
|
||
var isOn, obs, toggle;
|
||
obs = settingObservable(setting);
|
||
isOn = ko.computed(function() {
|
||
return obs() === onValue;
|
||
});
|
||
toggle = function() {
|
||
var value;
|
||
value = isOn() ? offValue : onValue;
|
||
obs(value);
|
||
return preview.changeState(setting, value);
|
||
};
|
||
return [isOn, toggle];
|
||
};
|
||
initColorPicker = function() {
|
||
var $svg, angleDifference, angleStep, centerX, centerY, color, colorSwatch, colors, dist, draggedSwatch, fiveForty, g, highlightSwatch, i, mouseDistScale, mouseoverSwatch, normalizeAngle, oneEighty, s, svg, swatch, swatches, threeSixty, twoPi, _fn, _i, _j, _len, _len1, _ref1;
|
||
twoPi = 2 * Math.PI;
|
||
oneEighty = Math.PI;
|
||
threeSixty = Math.PI * 2;
|
||
fiveForty = Math.PI * 3;
|
||
angleDifference = function(start, end) {
|
||
return (end - start + fiveForty) % threeSixty - oneEighty;
|
||
};
|
||
normalizeAngle = function(angle) {
|
||
while (angle > twoPi) {
|
||
angle -= twoPi;
|
||
}
|
||
while (angle < 0) {
|
||
angle += twoPi;
|
||
}
|
||
return angle;
|
||
};
|
||
dist = function(x, y, xx, yy) {
|
||
return Math.sqrt(Math.pow(x - xx, 2) + Math.pow(y - yy, 2));
|
||
};
|
||
svg = d3.select('#colorpicker');
|
||
$svg = $(svg.node());
|
||
g = svg.append('g');
|
||
mouseDistScale = d3.scale.linear().domain([0, 90]).range([0, Math.PI / 2]).clamp(true);
|
||
swatch = function(x, y, color, highlightColor, trueColor, trueHighlightColor) {
|
||
var R, calcPoints, clip, clipId, gradient, gradientId, line, originalHighlightColor, path, points, r, setGradientAngle, stops, thing, trans, update, updateGradientAngle, updateGradientColors;
|
||
if (trueColor == null) {
|
||
trueColor = color;
|
||
}
|
||
if (trueHighlightColor == null) {
|
||
trueHighlightColor = trueColor;
|
||
}
|
||
R = 27;
|
||
r = 5;
|
||
originalHighlightColor = highlightColor;
|
||
clipId = _.uniqueId();
|
||
clip = svg.append('defs').append('svg:clipPath').attr('id', clipId).append('path').attr('x', x).attr('y', y);
|
||
gradientId = _.uniqueId();
|
||
gradient = svg.append('svg:defs').append('svg:linearGradient').attr('id', gradientId).attr('x1', '100%').attr('y1', '0%').attr('x2', '100%').attr('y2', '100%').attr('spreadMethod', 'pad');
|
||
stops = [gradient.append('svg:stop').attr('stop-opacity', 1).attr('offset', '0%'), gradient.append('svg:stop').attr('stop-opacity', 1).attr('offset', '25%'), gradient.append('svg:stop').attr('stop-opacity', 1).attr('offset', '50%'), gradient.append('svg:stop').attr('stop-opacity', 1).attr('offset', '75%'), gradient.append('svg:stop').attr('stop-opacity', 1).attr('offset', '100%')];
|
||
points = new Array(40);
|
||
calcPoints = function(mouseSvgX, mouseSvgY, mouseAngle, mouseDistance) {
|
||
var angle, ccos, csin, cx, cy, i, threshold, _i, _ref1, _results;
|
||
_results = [];
|
||
for (i = _i = 0, _ref1 = points.length; 0 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 0 <= _ref1 ? ++_i : --_i) {
|
||
angle = i / points.length * twoPi;
|
||
ccos = Math.cos(angle);
|
||
csin = Math.sin(angle);
|
||
cx = x + R * ccos;
|
||
cy = y + R * csin;
|
||
threshold = mouseDistScale(mouseDistance);
|
||
if (mouseDistance > R && Math.abs(angleDifference(angle, mouseAngle)) < threshold && mouseDistance > dist(cx, cy, mouseSvgX, mouseSvgY)) {
|
||
_results.push(points[i] = {
|
||
x: mouseSvgX + r * ccos,
|
||
y: mouseSvgY + r * csin
|
||
});
|
||
} else {
|
||
_results.push(points[i] = {
|
||
x: cx,
|
||
y: cy
|
||
});
|
||
}
|
||
}
|
||
return _results;
|
||
};
|
||
line = d3.svg.line().x(function(d) {
|
||
return d.x;
|
||
}).y(function(d) {
|
||
return d.y;
|
||
}).interpolate('linear-closed');
|
||
path = g.append('svg:path').style('fill', "url(#" + gradientId + ")").attr('clip-path', "url(#" + clipId + ")");
|
||
trans = function(sel) {
|
||
return sel.transition().ease('exp-out').duration(350);
|
||
};
|
||
setGradientAngle = function(angle) {
|
||
var xpc, ypc;
|
||
xpc = 50 * (Math.cos(angle) + 1);
|
||
ypc = 50 * (Math.sin(angle) + 1);
|
||
return gradient.attr({
|
||
x1: xpc + '%',
|
||
y1: ypc + '%',
|
||
x2: (100 - xpc) + '%',
|
||
y2: (100 - ypc) + '%'
|
||
});
|
||
};
|
||
updateGradientAngle = function(targetAngle, anim) {
|
||
if (anim) {
|
||
return trans(gradient).duration(1000).tween('gradient', function() {
|
||
var diff, startAngle, x1, y1;
|
||
x1 = parseInt(gradient.attr('x1')) - 50;
|
||
y1 = parseInt(gradient.attr('y1')) - 50;
|
||
startAngle = Math.atan2(y1, x1);
|
||
diff = angleDifference(normalizeAngle(startAngle), normalizeAngle(targetAngle));
|
||
targetAngle = startAngle + diff;
|
||
return function(t) {
|
||
var angle;
|
||
angle = t * targetAngle + (1 - t) * startAngle;
|
||
return setGradientAngle(angle);
|
||
};
|
||
});
|
||
} else {
|
||
gradient.transition();
|
||
return setGradientAngle(targetAngle);
|
||
}
|
||
};
|
||
updateGradientColors = function() {
|
||
var interp, linear, logistic, pc, selected, stop, val, _i, _len, _results;
|
||
interp = d3.interpolateHcl(color, highlightColor);
|
||
logistic = function(t) {
|
||
var val;
|
||
return val = 1 / (1 + Math.pow(Math.E, -(t * 12 - 6)));
|
||
};
|
||
linear = d3.scale.linear().domain([0, 1]).range([0, 0.7]).clamp(true);
|
||
selected = path.classed('selected');
|
||
_results = [];
|
||
for (_i = 0, _len = stops.length; _i < _len; _i++) {
|
||
stop = stops[_i];
|
||
pc = parseInt(stop.attr('offset')) / 100;
|
||
if (selected && highlightColor !== originalHighlightColor) {
|
||
val = logistic(linear(pc));
|
||
} else {
|
||
val = pc;
|
||
}
|
||
_results.push(trans(stop).attr('stop-color', interp(val)));
|
||
}
|
||
return _results;
|
||
};
|
||
update = function(mousePageX, mousePageY, mouseDown) {
|
||
var anim, gradientAngle, mouseAngle, mouseDistance, mouseRelativeX, mouseRelativeY, mouseSvgX, mouseSvgY, o, pathData;
|
||
if (mouseDown == null) {
|
||
mouseDown = true;
|
||
}
|
||
o = $svg.offset();
|
||
mouseSvgX = mousePageX - o.left;
|
||
mouseSvgY = mousePageY - o.top;
|
||
mouseRelativeX = mousePageX - x;
|
||
mouseRelativeY = mousePageY - y;
|
||
mouseAngle = mousePageX !== 0 && mousePageY !== 0 ? Math.atan2(mouseSvgY - y, mouseSvgX - x) : 0;
|
||
mouseDistance = dist(x, y, mouseSvgX, mouseSvgY);
|
||
if (mouseDistance < R - 5) {
|
||
gradientAngle = Math.PI / 2;
|
||
if (mouseDown) {
|
||
gradientAngle = -gradientAngle;
|
||
}
|
||
} else {
|
||
if (mouseDown) {
|
||
gradientAngle = Math.PI + mouseAngle;
|
||
} else {
|
||
gradientAngle = Math.PI / 2;
|
||
}
|
||
}
|
||
if (mouseDown) {
|
||
anim = false;
|
||
calcPoints(mouseSvgX, mouseSvgY, mouseAngle, mouseDistance);
|
||
} else {
|
||
calcPoints(x, y, 0, 0);
|
||
anim = mouseDistance > R - 5;
|
||
}
|
||
updateGradientAngle(gradientAngle, anim);
|
||
pathData = line(points);
|
||
if (anim) {
|
||
trans(path).attr('d', pathData);
|
||
return trans(clip).attr('d', pathData);
|
||
} else {
|
||
path.attr('d', pathData);
|
||
return clip.attr('d', pathData);
|
||
}
|
||
};
|
||
update(x, y, false);
|
||
updateGradientColors();
|
||
return thing = {
|
||
path: path,
|
||
update: update,
|
||
updateGradientAngle: updateGradientAngle,
|
||
trueColor: function() {
|
||
return trueColor;
|
||
},
|
||
trueHighlightColor: function() {
|
||
return trueHighlightColor;
|
||
},
|
||
color: function() {
|
||
return color;
|
||
},
|
||
highlightColor: function(c) {
|
||
if (!arguments.length) {
|
||
return highlightColor;
|
||
}
|
||
if (c != null) {
|
||
highlightColor = c;
|
||
} else {
|
||
highlightColor = originalHighlightColor;
|
||
}
|
||
updateGradientColors();
|
||
return thing;
|
||
},
|
||
select: function() {
|
||
path.classed('selected', true);
|
||
$('#selected-color-icon').css('color', originalHighlightColor);
|
||
return thing;
|
||
},
|
||
deselect: function() {
|
||
path.classed('selected', false);
|
||
return thing;
|
||
}
|
||
};
|
||
};
|
||
colors = [['#3e95cc', '#82cefc', '#276f9b', '#3dbbea'], ['#53bf39', '#65eb43', '#53BD39'], ['#e1d730', '#fdf23f', '#E3BF30'], ['#ea5126', '#fd722e', '#EB5126'], ['#db4775', '#fd5e8f', '#dd4876', '#fe495e'], ['#825ba1', '#b585da'], ['#555555', '#717171']];
|
||
swatches = [];
|
||
centerX = 175 / 2 + 10;
|
||
centerY = 175 / 2;
|
||
draggedSwatch = null;
|
||
mouseoverSwatch = null;
|
||
angleStep = Math.PI * 2 / 6;
|
||
_ref1 = colors.slice(0, 6);
|
||
for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) {
|
||
color = _ref1[i];
|
||
swatches.push(swatch.apply(null, [centerX + 65 * Math.cos(angleStep * i), centerY + 65 * Math.sin(angleStep * i)].concat(__slice.call(color))));
|
||
}
|
||
swatches.push(swatch.apply(null, [centerX, centerY].concat(__slice.call(colors[6]))));
|
||
_fn = function(s) {
|
||
return $(s.path.node()).on('mouseover', function() {
|
||
if (draggedSwatch != null) {
|
||
draggedSwatch.highlightColor(s.highlightColor());
|
||
}
|
||
return mouseoverSwatch = s;
|
||
}).on('mousemove', function() {}).on('mouseout', function() {
|
||
if (draggedSwatch != null) {
|
||
draggedSwatch.highlightColor(null);
|
||
}
|
||
return mouseoverSwatch = null;
|
||
}).on('mousedown', function(e) {
|
||
if (e.button === 2) {
|
||
return false;
|
||
}
|
||
this.parentNode.appendChild(this);
|
||
s.path.style('pointer-events', 'none');
|
||
_.each(swatches, function(s) {
|
||
return s.highlightColor(null).deselect();
|
||
});
|
||
s.select();
|
||
s.updateGradientAngle(-Math.PI / 2);
|
||
return draggedSwatch = s;
|
||
});
|
||
};
|
||
for (_j = 0, _len1 = swatches.length; _j < _len1; _j++) {
|
||
s = swatches[_j];
|
||
_fn(s);
|
||
}
|
||
colorSwatch = swatches[0];
|
||
highlightSwatch = swatches[1];
|
||
colorSwatch.highlightColor(highlightSwatch.highlightColor()).select();
|
||
silks.silkSettingsState.color = colorSwatch.trueColor();
|
||
silks.silkSettingsState.highlightColor = highlightSwatch.trueColor();
|
||
return $(window).on('mousemove', function(e) {
|
||
if (draggedSwatch != null) {
|
||
return draggedSwatch.update(e.pageX, e.pageY);
|
||
}
|
||
}).on('mouseup', function(e) {
|
||
var _k, _len2;
|
||
if (draggedSwatch != null) {
|
||
draggedSwatch.update(e.pageX, e.pageY, false);
|
||
draggedSwatch.path.style('pointer-events', '');
|
||
silks.silkSettingsState.color = draggedSwatch.trueColor();
|
||
silks.silkSettingsState.highlightColor = (mouseoverSwatch != null) && mouseoverSwatch !== draggedSwatch ? mouseoverSwatch.trueColor() : draggedSwatch.trueHighlightColor();
|
||
for (_k = 0, _len2 = swatches.length; _k < _len2; _k++) {
|
||
s = swatches[_k];
|
||
s.deselect();
|
||
}
|
||
draggedSwatch.select();
|
||
if (typeof Hue !== "undefined" && Hue !== null) {
|
||
Hue.setColor(silks.silkSettingsState.color);
|
||
}
|
||
}
|
||
draggedSwatch = null;
|
||
return mouseoverSwatch = null;
|
||
});
|
||
};
|
||
initSlider = function(sel, vals, handleMoved) {
|
||
var $el, $ghostHandle, $handle, $sliderBg, $sliderBgPc, bringHandlesTo, el, eventToPos, eventToX, eventToY, ghostHandle, halfHandleSize, handle, handleSize, height, mouseDown, sliderBg, sliderBgOffsetLeft, sliderBgOffsetTop, startPos, useX, useY, width;
|
||
$el = $(sel);
|
||
el = $el[0];
|
||
$handle = $('.handle', el);
|
||
handle = $handle[0];
|
||
$ghostHandle = $('.ghost-handle', el);
|
||
ghostHandle = $ghostHandle[0];
|
||
$sliderBg = $el.find('.slider-bg');
|
||
$sliderBgPc = $el.find('.slider-bg-pc');
|
||
sliderBg = $sliderBg.position();
|
||
sliderBgOffsetTop = sliderBg.top;
|
||
sliderBgOffsetLeft = sliderBg.left + parseInt($sliderBg.css('margin-left'));
|
||
d('off', sliderBgOffsetLeft);
|
||
width = $sliderBg.width();
|
||
height = $sliderBg.height();
|
||
handleSize = $handle.width();
|
||
halfHandleSize = handleSize / 2;
|
||
useX = vals.x != null;
|
||
useY = vals.y != null;
|
||
bringHandlesTo = function(pos, onlyGhost) {
|
||
var handleVals, x, y;
|
||
if (onlyGhost == null) {
|
||
onlyGhost = false;
|
||
}
|
||
handleVals = {};
|
||
if (useX) {
|
||
x = pos.x;
|
||
handleVals.x = x / width;
|
||
$ghostHandle.css('left', x + sliderBgOffsetLeft - halfHandleSize + 'px');
|
||
if (!onlyGhost) {
|
||
$handle.css('left', x + sliderBgOffsetLeft - halfHandleSize + 'px');
|
||
$sliderBgPc.width(x + 'px');
|
||
vals.x = handleVals.x;
|
||
}
|
||
}
|
||
if (useY) {
|
||
y = pos.y;
|
||
handleVals.y = y / height;
|
||
$ghostHandle.css('top', y + sliderBgOffsetTop - halfHandleSize + 'px');
|
||
if (!onlyGhost) {
|
||
$handle.css('top', y + sliderBgOffsetTop - halfHandleSize + 'px');
|
||
vals.y = handleVals.y;
|
||
}
|
||
}
|
||
return handleMoved(handleVals, onlyGhost);
|
||
};
|
||
eventToX = function(e) {
|
||
var bucket, bucketSize, n, x;
|
||
x = e.pageX - $el.offset().left - sliderBgOffsetLeft;
|
||
if (x < 0) {
|
||
x = 0;
|
||
}
|
||
if (x > width) {
|
||
x = width;
|
||
}
|
||
n = 6;
|
||
bucketSize = width / n;
|
||
bucket = Math.round(x / bucketSize);
|
||
return bucket * bucketSize;
|
||
};
|
||
eventToY = function(e) {
|
||
var y;
|
||
y = e.pageY - $el.offset().top + sliderBgOffsetTop;
|
||
if (y < 0) {
|
||
y = 0;
|
||
}
|
||
if (y > height) {
|
||
y = height;
|
||
}
|
||
return y;
|
||
};
|
||
eventToPos = function(e) {
|
||
var pos;
|
||
pos = {};
|
||
if (useX) {
|
||
pos.x = eventToX(e);
|
||
}
|
||
if (useY) {
|
||
pos.y = eventToY(e);
|
||
}
|
||
return pos;
|
||
};
|
||
startPos = {};
|
||
if (useX) {
|
||
if (vals.x > 1) {
|
||
vals.x = 1;
|
||
}
|
||
if (vals.x < 0) {
|
||
vals.x = 0;
|
||
}
|
||
startPos.x = vals.x * width;
|
||
}
|
||
if (useY) {
|
||
if (vals.y > 1) {
|
||
vals.y = 1;
|
||
}
|
||
if (vals.y < 0) {
|
||
vals.y = 0;
|
||
}
|
||
startPos.y = vals.y * height;
|
||
}
|
||
bringHandlesTo(startPos);
|
||
mouseDown = false;
|
||
$el.on('mousedown', function(e) {
|
||
mouseDown = true;
|
||
mouseDownOnSlider(true);
|
||
return bringHandlesTo(eventToPos(e));
|
||
}).on('mousemove', function(e) {
|
||
if (mouseDownOnSlider() === mouseDown) {
|
||
return bringHandlesTo(eventToPos(e), true);
|
||
}
|
||
}).on('mouseup', function(e) {
|
||
mouseDown = false;
|
||
return mouseDownOnSlider(false);
|
||
}).mouseleave(function(e) {
|
||
if (!mouseDown) {
|
||
return handleMoved(vals);
|
||
}
|
||
});
|
||
return $(window).on('mousemove', function(e) {
|
||
if (mouseDown) {
|
||
return bringHandlesTo(eventToPos(e));
|
||
}
|
||
}).on('mouseup', function(e) {
|
||
if (mouseDown) {
|
||
mouseDown = false;
|
||
return mouseDownOnSlider(false);
|
||
}
|
||
});
|
||
};
|
||
initializedUI = false;
|
||
initColorPicker();
|
||
preview = initPreview();
|
||
_ref1 = makeToggle('symMirror', true, false), mirror = _ref1[0], toggleMirror = _ref1[1];
|
||
_ref2 = makeToggle('spiralCopies', 4, 1), spiral = _ref2[0], toggleSpiral = _ref2[1];
|
||
$('#toggle-mirror').mouseenter(function() {
|
||
return preview.newPreviewSilk({}, 'mirror');
|
||
});
|
||
$('#toggle-spiral').mouseenter(function() {
|
||
return preview.newPreviewSilk({}, 'spiral');
|
||
});
|
||
symSlider = initSlider('#sym-num-rotations', {
|
||
x: _.unlerp(1, 6, _.load('symNumRotations', silks.silkSettingsState.symNumRotations))
|
||
}, function(vals, ghost) {
|
||
var silkState, state;
|
||
state = {
|
||
symNumRotations: Math.round(_.lerp(1, 6, vals.x))
|
||
};
|
||
_.save('symNumRotations', state.symNumRotations);
|
||
if (state.symNumRotations > 1) {
|
||
$('#sym-num-rotations-label').html("" + state.symNumRotations + "-fold rotational symmetry");
|
||
} else {
|
||
$('#sym-num-rotations-label').html("No rotational symmetry");
|
||
}
|
||
preview.newPreviewSilk(state, 'rotation');
|
||
if (!ghost) {
|
||
silks.extendSilkSettingsState(state);
|
||
}
|
||
return silkState = silks.silkSettingsState;
|
||
});
|
||
initializedUI = true;
|
||
return _.extend({
|
||
tips: initTips({
|
||
showColorPicker: showColorPicker,
|
||
showSymmetryControls: showSymmetryControls
|
||
}),
|
||
mirror: mirror,
|
||
toggleMirror: toggleMirror,
|
||
spiral: spiral,
|
||
toggleSpiral: toggleSpiral,
|
||
showColorPicker: showColorPicker,
|
||
showSymmetryControls: showSymmetryControls,
|
||
mouseOverPreviewableControls: mouseOverPreviewableControls,
|
||
mouseDownOnSlider: mouseDownOnSlider
|
||
});
|
||
};
|
||
|
||
$((function(_this) {
|
||
return function() {
|
||
var b, bufferCanvas, container, drawsPerFrame, drawsPerFrameRatio, endTime, frame, frameCount, h2, hideIntroSilk, introEnd, introLength, introSilkId, introStart, isIPhone, isRightSideUp, pmouseX, pmouseY, replayUrlForId, resetShareOptions, silkCanvas, silks, sound, sparksCanvas, startTime, ui, updateOrientation, urlParams, w2, weShouldEvenHaveAnIntroSilkAtAll, _ref1, _ref2;
|
||
container = document.getElementById('canvii-container');
|
||
silkCanvas = document.getElementById('silk-1');
|
||
bufferCanvas = document.getElementById('silk-2');
|
||
sparksCanvas = document.getElementById('sparks');
|
||
isIPhone = /(iPhone|iPod).*AppleWebKit/i.test(navigator.userAgent);
|
||
isRightSideUp = ko.observable(window.orientation === 0 || window.orientation === 180);
|
||
updateOrientation = function() {
|
||
return isRightSideUp(window.orientation === 0 || window.orientation === 180);
|
||
};
|
||
window.addEventListener('orientationchange', updateOrientation, false);
|
||
hideIntroSilk = function() {
|
||
if (b.introSilkShowing()) {
|
||
silks.clear(false);
|
||
return b.introSilkShowing(false);
|
||
}
|
||
};
|
||
$('#sparks').mousedown(hideIntroSilk).on('touchstart', hideIntroSilk);
|
||
window._s = silks = new Silks(container, silkCanvas, bufferCanvas, sparksCanvas);
|
||
silks.initInputEvents();
|
||
ui = initUI(silks);
|
||
startTime = +new Date();
|
||
endTime = startTime + 16;
|
||
if ((_ref1 = window.Hue) != null) {
|
||
_ref1.setColor = function(color) {
|
||
var h, l, num, s;
|
||
color = d3.hsl(color);
|
||
h = parseInt(65536 * (color.h / 360));
|
||
s = color.s > 50 ? 255 : parseInt(255 * color.s);
|
||
l = parseInt(255 * color.l);
|
||
num = [1, 2, 3];
|
||
if (typeof Hue !== "undefined" && Hue !== null) {
|
||
Hue.setBri(num, l);
|
||
}
|
||
return typeof Hue !== "undefined" && Hue !== null ? Hue.setHueSat(num, h, s) : void 0;
|
||
};
|
||
}
|
||
frameCount = 0;
|
||
h2 = $(window).height() / 2;
|
||
w2 = $(window).width() / 2;
|
||
replayUrlForId = function(id) {
|
||
return "http://r.weavesilk.com/?v=4&id=" + id;
|
||
};
|
||
weShouldEvenHaveAnIntroSilkAtAll = true;
|
||
introSilkId = null;
|
||
introStart = 25;
|
||
drawsPerFrame = 2;
|
||
drawsPerFrameRatio = 5 / 2;
|
||
introLength = 45 * drawsPerFrameRatio;
|
||
introEnd = introStart + introLength;
|
||
frame = function() {
|
||
frameCount++;
|
||
if (b.introSilkShowing() && weShouldEvenHaveAnIntroSilkAtAll) {
|
||
switch (false) {
|
||
case frameCount !== introStart:
|
||
introSilkId = silks.add('intro', {
|
||
symNumRotations: 6,
|
||
symMirror: true,
|
||
color: '#276f9b',
|
||
highlightColor: '#825ba1',
|
||
highlightMode: 'time',
|
||
timeColorScaleDomainHigh: 500,
|
||
symCenterX: w2,
|
||
symCenterY: h2,
|
||
startOpacity: 0.08,
|
||
noiseOffset: 10000 * Math.random(),
|
||
drawsPerFrame: drawsPerFrame
|
||
});
|
||
break;
|
||
case !((introStart <= frameCount && frameCount < introEnd) && frameCount % 2 === 0):
|
||
silks.addPoint(introSilkId, w2 + 5, h2 - 5, 0, 0);
|
||
break;
|
||
case frameCount !== introEnd:
|
||
silks.complete(introSilkId);
|
||
}
|
||
}
|
||
startTime = +new Date();
|
||
silks.frame((startTime - endTime) / 16);
|
||
endTime = +new Date();
|
||
return setTimeout(frame, 16 - (endTime - startTime));
|
||
};
|
||
sound = null;
|
||
b = {
|
||
isIPhone: isIPhone,
|
||
isRightSideUp: isRightSideUp,
|
||
clipText: ko.observable('Copy link'),
|
||
canUndo: silks.snapshotState.canUndo,
|
||
justCleared: silks.snapshotState.justCleared,
|
||
nextUndoIsRedo: silks.nextUndoIsRedo,
|
||
nextUndoIsNotRedo: ko.computed(function() {
|
||
return !silks.nextUndoIsRedo();
|
||
}),
|
||
introSilkShowing: ko.observable(true),
|
||
clear: function() {
|
||
var url;
|
||
if (!silks.snapshotState.justCleared()) {
|
||
ui.tips.showNext();
|
||
}
|
||
b.showingReplayThumbnail(false);
|
||
b.isReplay(false);
|
||
url = window.location.href;
|
||
url = url.substring(0, url.indexOf('?'));
|
||
if (sound != null) {
|
||
sound.playClearSound();
|
||
}
|
||
b.showIntro(false);
|
||
resetShareOptions();
|
||
return silks.clear();
|
||
},
|
||
undo: function() {
|
||
silks.undo();
|
||
if (silks.snapshotState.justCleared()) {
|
||
return ui.tips.show();
|
||
} else {
|
||
return ui.tips.hide();
|
||
}
|
||
},
|
||
silkActive: ko.observable(false),
|
||
showAbout: ko.observable(false),
|
||
showDownload: ko.observable(false),
|
||
showIntro: ko.observable(true),
|
||
isFullscreen: ko.observable($.Fullscreen.isFullscreen()),
|
||
toggleFullscreen: function() {
|
||
if ($.Fullscreen.isFullscreen()) {
|
||
$.Fullscreen.cancelFullscreen();
|
||
return b.isFullscreen(false);
|
||
} else {
|
||
$('html').requestFullscreen();
|
||
return b.isFullscreen(true);
|
||
}
|
||
},
|
||
showColorPicker: ui.showColorPicker,
|
||
showSymmetryControls: ui.showSymmetryControls,
|
||
mouseOverControls: ui.mouseOverControls,
|
||
mouseDownOnSlider: ui.mouseDownOnSlider,
|
||
mirror: ui.mirror,
|
||
toggleMirror: ui.toggleMirror,
|
||
spiral: ui.spiral,
|
||
toggleSpiral: ui.toggleSpiral,
|
||
shareLoading: ko.observable(false),
|
||
showShareOptions: ko.observable(false),
|
||
shareUrlDirect: ko.observable(''),
|
||
shareUrlThumbnail: ko.observable(''),
|
||
toggleSound: function() {},
|
||
toggleAbout: function() {
|
||
return b.showAbout(!b.showAbout());
|
||
},
|
||
toggleAllControls: function() {
|
||
b.showDownload(false);
|
||
b.showColorPicker(!b.showColorPicker());
|
||
return b.showSymmetryControls(!b.showSymmetryControls());
|
||
},
|
||
download: function() {
|
||
if (b.showDownload()) {
|
||
return b.showDownload(false);
|
||
} else {
|
||
b.showDownload(true);
|
||
b.showColorPicker(false);
|
||
b.showSymmetryControls(false);
|
||
return $('#download-image').attr('src', silks.getImageUrl());
|
||
}
|
||
},
|
||
shareButtonClick: function() {
|
||
if (b.shareDisabled()) {
|
||
return;
|
||
}
|
||
b.hideReplayThumbnail();
|
||
if (b.shareUrlDirect()) {
|
||
return b.showShareOptions(true);
|
||
} else {
|
||
b.shareLoading(true);
|
||
b.showShareOptions(false);
|
||
|
||
/*
|
||
xhr = new XMLHttpRequest()
|
||
fd = new FormData()
|
||
key = 'v4/uploads/' + 'f' + Math.floor(100000 * Math.random()).toString() + '.json'
|
||
d 'key:', key
|
||
|
||
fd.append('key', key)
|
||
fd.append('AWSAccessKeyId', 'AKIAJI7IO2AMLHSSRVNA')
|
||
fd.append('acl', "public-read")
|
||
fd.append('policy', "eyJleHBpcmF0aW9uIjogIjIwMjAtMDEtMDFUMDA6MDA6MDBaIiwKICAiY29uZGl0aW9ucyI6IFsgCiAgICB7ImJ1Y2tldCI6ICJ3ZWF2ZXNpbGsifSwgCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidjQvdXBsb2Fkcy8iXSwKICAgIHsiYWNsIjogInB1YmxpYy1yZWFkIn0sCiAgICBbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwgMCwgNzY4MDAwXQogIF0KfQ==")
|
||
fd.append('signature',"ZwPLaO3yeOQUWeNHmoHsuBeuI9M=")
|
||
fd.append('enctype', 'multipart/form-data')
|
||
fd.append('Content-Type', 'application/json')
|
||
|
||
blob = new Blob([JSON.stringify(silks.getReplay())], type: 'application/json')
|
||
|
||
* This file object NOT is retrieved from a file input.
|
||
fd.append('file', blob);
|
||
|
||
* ##
|
||
$.ajax 'http://weavesilk.s3.amazonaws.com', {
|
||
type: 'POST'
|
||
'key': key
|
||
'AWSAccessKeyId', 'AKIAJI7IO2AMLHSSRVNA'
|
||
'acl': 'public-read'
|
||
'policy': "eyJleHBpcmF0aW9uIjogIjIwMjAtMDEtMDFUMDA6MDA6MDBaIiwKICAiY29uZGl0aW9ucyI6IFsgCiAgICB7ImJ1Y2tldCI6ICJ3ZWF2ZXNpbGsifSwgCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidjQvdXBsb2Fkcy8iXSwKICAgIHsiYWNsIjogInB1YmxpYy1yZWFkIn0sCiAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiIl0sCiAgICBbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwgMCwgNzY4MDAwXQogIF0KfQ=="
|
||
'signature': "bxwpDfwam1YcipisxSXFXRl5w38="
|
||
contentType: false
|
||
|
||
* This file object NOT is retrieved from a file input.
|
||
'data': fd
|
||
}, ->
|
||
d 'success'
|
||
*# #
|
||
|
||
* Populate the Post paramters.
|
||
fd.append('key', key)
|
||
fd.append('AWSAccessKeyId', 'AKIAJI7IO2AMLHSSRVNA')
|
||
fd.append('acl', "public-read")
|
||
fd.append('policy', "eyJleHBpcmF0aW9uIjogIjIwMjAtMDEtMDFUMDA6MDA6MDBaIiwKICAiY29uZGl0aW9ucyI6IFsgCiAgICB7ImJ1Y2tldCI6ICJ3ZWF2ZXNpbGsifSwgCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidjQvdXBsb2Fkcy8iXSwKICAgIHsiYWNsIjogInB1YmxpYy1yZWFkIn0sCiAgICBbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwgMCwgNzY4MDAwXQogIF0KfQ==")
|
||
fd.append('signature',"ZwPLaO3yeOQUWeNHmoHsuBeuI9M=")
|
||
fd.append('enctype', 'multipart/form-data')
|
||
fd.append('Content-Type', 'application/json')
|
||
|
||
blob = new Blob([JSON.stringify(silks.getReplay())], type: 'application/json')
|
||
|
||
* This file object NOT is retrieved from a file input.
|
||
fd.append('file', blob);
|
||
xhr.setRequestHeader('enctype', 'multipart/form-data')
|
||
xhr.open('POST', 'http://weavesilk.s3.amazonaws.com', true);
|
||
xhr.send(fd)
|
||
* d 'opened'
|
||
|
||
* Keep track of upload progress so that we can message
|
||
* it to the user.
|
||
xhr.upload.addEventListener('progress', ((e) ->
|
||
d 'progress:', (e.loaded);
|
||
), false);
|
||
|
||
|
||
* If the upload completes we should decrement the uploads
|
||
* currently taking place.
|
||
xhr.onreadystatechange = ->
|
||
d 'statechange', xhr
|
||
if (xhr.readyState != 4)
|
||
return;
|
||
|
||
d 'Complete.'
|
||
*/
|
||
return $.ajax({
|
||
url: 'http://weavesilk-replays.herokuapp.com/v4/save_replay',
|
||
type: 'POST',
|
||
dataType: 'json',
|
||
data: {
|
||
json: JSON.stringify(silks.getReplay()),
|
||
thumb: silks.makeThumb()
|
||
},
|
||
success: function(data) {
|
||
var id, thumbUrl, url, _ref2;
|
||
d('Got', typeof data, 'data:', data);
|
||
_ref2 = data.result, id = _ref2.id, thumbUrl = _ref2.thumbUrl;
|
||
url = replayUrlForId(id);
|
||
b.shareUrlDirect(url);
|
||
b.shareUrlThumbnail(thumbUrl);
|
||
return b.showShareOptions(true);
|
||
},
|
||
error: function(data) {
|
||
alert('There was an error uploading your picture. Maybe try again?');
|
||
return b.showShareOptions(false);
|
||
},
|
||
complete: function() {
|
||
return b.shareLoading(false);
|
||
}
|
||
});
|
||
}
|
||
},
|
||
toggleControls: function() {
|
||
return b.controlsVisible(!b.controlsVisible());
|
||
}
|
||
};
|
||
b.showingReplayThumbnail = ko.observable(false);
|
||
b.hideReplayThumbnail = function() {
|
||
return b.showingReplayThumbnail(false);
|
||
};
|
||
b.isReplay = ko.observable(false);
|
||
b.isOnMobile = ko.observable(MobileDetect());
|
||
b.shareUrlEmail = ko.computed(function() {
|
||
var url;
|
||
url = b.shareUrlDirect();
|
||
return "mailto:?subject=" + (escape('Silk -- Interactive generative art')) + "&body=" + (escape('I just drew this on weavesilk.com: ' + url));
|
||
});
|
||
b.pristine = ko.observable(false || !b.showColorPicker());
|
||
b.muted = (_ref2 = sound != null ? sound.muteSound : void 0) != null ? _ref2 : ko.observable(true);
|
||
b.notMuted = ko.computed(function() {
|
||
return !b.muted();
|
||
});
|
||
b.toggleMute = function() {
|
||
d;
|
||
if (sound != null ? sound.muteSound() : void 0) {
|
||
if (sound != null) {
|
||
sound.setMuteMusic(false);
|
||
}
|
||
return sound != null ? sound.setMuteEffects(false) : void 0;
|
||
} else {
|
||
if (sound != null) {
|
||
sound.setMuteMusic(true);
|
||
}
|
||
return sound != null ? sound.setMuteEffects(true) : void 0;
|
||
}
|
||
};
|
||
resetShareOptions = function() {
|
||
b.showShareOptions(false);
|
||
return b.shareUrlDirect('');
|
||
};
|
||
b.notPristine = ko.computed(function() {
|
||
return !b.pristine();
|
||
});
|
||
b.announcementClosePressed = ko.observable(false);
|
||
b.closeAnnouncement = function() {
|
||
b.announcementClosePressed(true);
|
||
return _.save('announcementClosePressed', true);
|
||
};
|
||
b.hideAllAds = ko.observable(false);
|
||
b.showAnyAnnouncement = ko.computed(function() {
|
||
return b.notPristine() && !b.hideAllAds();
|
||
});
|
||
$('#about-button').dblclick(function() {
|
||
return b.hideAllAds(!b.hideAllAds());
|
||
});
|
||
b.numClears = ko.observable(0);
|
||
b.justCleared.subscribe(function(val) {
|
||
if (val) {
|
||
return b.numClears(1 + b.numClears());
|
||
}
|
||
});
|
||
b.showBigAnnouncementDefault = ko.computed(function() {
|
||
return b.showAnyAnnouncement() && (b.introSilkShowing() || (b.justCleared() && b.notPristine()) || b.isReplay() || b.isOnMobile());
|
||
});
|
||
b.notShowBigAnnouncementDefault = ko.computed(function() {
|
||
return !b.showBigAnnouncementDefault();
|
||
});
|
||
b.showBigAnnouncement = ko.computed(function() {
|
||
var isAndroid;
|
||
isAndroid = navigator.userAgent.match(/Android/i);
|
||
if (b.numClears() < 5) {
|
||
return b.showAnyAnnouncement() && !isAndroid;
|
||
} else {
|
||
return b.showBigAnnouncementDefault() && !isAndroid;
|
||
}
|
||
});
|
||
b.notPristineAndJustCleared = ko.computed(function() {
|
||
return !b.pristine() && b.justCleared();
|
||
});
|
||
b.shareDisabled = ko.computed(function() {
|
||
return b.justCleared() || b.shareLoading();
|
||
});
|
||
b.notShareDisabled = ko.computed(function() {
|
||
return !b.shareDisabled();
|
||
});
|
||
b.showShareButton = ko.computed(function() {
|
||
return b.notPristine() && !b.showShareOptions();
|
||
});
|
||
b.isNotFullscreen = ko.computed(function() {
|
||
return !b.isFullscreen();
|
||
});
|
||
b.shareUrlFacebook = ko.computed(function() {
|
||
var url;
|
||
url = encodeURI(b.shareUrlDirect());
|
||
return "https://www.facebook.com/dialog/feed?app_id=408271179236250&link=" + (escape(url)) + "&picture=" + (encodeURI(b.shareUrlThumbnail())) + "&name=" + 'Silk – Interactive Generative Art' + "&caption=" + (escape('weavesilk.com')) + "&description=" + (encodeURI('Create beautiful flowing art with Silk.')) + "&redirect_uri=" + (encodeURI(url));
|
||
});
|
||
b.shareUrlTwitter = ko.computed(function() {
|
||
var url;
|
||
url = encodeURI(b.shareUrlDirect());
|
||
return "https://twitter.com/share?url=" + (escape(url)) + "&text=" + (escape('Look what I drew with #weavesilk!'));
|
||
});
|
||
b.shareUrlPinterest = ko.computed(function() {
|
||
var url;
|
||
url = encodeURI(b.shareUrlDirect());
|
||
return "http://pinterest.com/pin/create/button/?url=" + url + "&media=" + (encodeURI(b.shareUrlThumbnail()));
|
||
});
|
||
silks.drawInputPreview = false;
|
||
pmouseX = pmouseY = 0;
|
||
$('#sparks').mousedown(function(e) {
|
||
if (sound != null) {
|
||
sound.start();
|
||
}
|
||
if (sound != null) {
|
||
sound.playDrawSound();
|
||
}
|
||
pmouseX = e.pageX;
|
||
pmouseY = e.pageY;
|
||
b.silkActive(true);
|
||
resetShareOptions();
|
||
ui.tips.hide();
|
||
b.showDownload(false);
|
||
b.showAbout(false);
|
||
b.showIntro(false);
|
||
return b.hideReplayThumbnail();
|
||
}).mousemove(function(e) {
|
||
var sq, vmx, vmy;
|
||
vmx = pmouseX - e.pageX;
|
||
vmy = pmouseY - e.pageY;
|
||
sq = vmx * vmx + vmy * vmy;
|
||
if (sq > 0) {
|
||
if (sound != null) {
|
||
sound.modulateDrawSound(Math.sqrt(sq));
|
||
}
|
||
} else {
|
||
if (sound != null) {
|
||
sound.modulateDrawSound(0);
|
||
}
|
||
}
|
||
pmouseX = e.pageX;
|
||
return pmouseY = e.pageY;
|
||
}).mouseup(function(e) {
|
||
b.pristine(false);
|
||
b.silkActive(false);
|
||
silks.drawInputPreview = true;
|
||
return sound != null ? sound.stopDrawSound() : void 0;
|
||
}).on('touchstart', function(e) {
|
||
pmouseX = e.pageX;
|
||
pmouseY = e.pageY;
|
||
b.silkActive(true);
|
||
resetShareOptions();
|
||
ui.tips.hide();
|
||
b.showDownload(false);
|
||
b.showAbout(false);
|
||
return b.showIntro(false);
|
||
}).on('touchend', function(e) {
|
||
b.pristine(false);
|
||
return b.silkActive(false);
|
||
});
|
||
ko.applyBindings(b);
|
||
if (MobileDetect()) {
|
||
$('#main-controls .silk-icon').css('display', 'none');
|
||
}
|
||
key('n, x, space', _.throttle(function() {
|
||
b.clear();
|
||
return b.showDownload(false);
|
||
}, 150));
|
||
key('z, u', _.throttle(b.undo, 150));
|
||
key('r', silks.replayReplay);
|
||
key('c', b.toggleAllControls);
|
||
urlParams = {};
|
||
(function() {
|
||
var decode, match, pl, query, search, _results;
|
||
pl = /\+/g;
|
||
search = /([^&=]+)=?([^&]*)/g;
|
||
decode = function(s) {
|
||
return decodeURIComponent(s.replace(pl, " "));
|
||
};
|
||
query = window.location.search.substring(1);
|
||
_results = [];
|
||
while (match = search.exec(query)) {
|
||
_results.push(urlParams[decode(match[1])] = decode(match[2]));
|
||
}
|
||
return _results;
|
||
})();
|
||
$('.direct-link').focus(function() {
|
||
return $(this).select();
|
||
});
|
||
b.showShareOptions.subscribe(function(val) {
|
||
if (!val) {
|
||
return $('.direct-link').blur();
|
||
}
|
||
});
|
||
if ((urlParams.id != null) && (urlParams.v != null)) {
|
||
b.pristine(false);
|
||
b.showIntro(false);
|
||
weShouldEvenHaveAnIntroSilkAtAll = false;
|
||
b.showingReplayThumbnail(true);
|
||
b.isReplay(true);
|
||
$.ajax({
|
||
url: "http://weavesilk.s3.amazonaws.com/v" + urlParams.v + "/uploads/replay/" + urlParams.id + ".json",
|
||
type: 'GET',
|
||
dataType: 'json',
|
||
success: function(data) {
|
||
var url;
|
||
d('Got data:', data);
|
||
silks.playReplay(data);
|
||
url = replayUrlForId(urlParams.id);
|
||
b.introSilkShowing(false);
|
||
b.showShareOptions(false);
|
||
b.shareUrlDirect(url);
|
||
return b.shareUrlThumbnail("http://weavesilk.s3.amazonaws.com/v4/uploads/thumb/" + urlParams.id + ".png");
|
||
}
|
||
});
|
||
}
|
||
return frame();
|
||
};
|
||
})(this));
|
||
|
||
}).call(this);
|