waves/public/assets/g/weavesilk/js/site.js
2025-04-09 17:11:14 -05:00

2882 lines
96 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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);