// // TangleKit.js // Tangle 0.1.0 // // Created by Bret Victor on 6/10/11. // (c) 2011 Bret Victor. MIT open-source license. // (function () { //---------------------------------------------------------- // // TKIf // // Shows the element if value is true (non-zero), hides if false. // // Attributes: data-invert (optional): show if false instead. Tangle.classes.TKIf = { initialize: function (element, options, tangle, variable) { this.isInverted = !!options.invert; }, update: function (element, value) { if (this.isInverted) { value = !value; } element.style.display = !value ? "none" : "inline"; // todo, block or inline? } }; //---------------------------------------------------------- // // TKSwitch // // Shows the element's nth child if value is n. // // False or true values will show the first or second child respectively. Tangle.classes.TKSwitch = { update: function (element, value) { element.getChildren().each( function (child, index) { child.style.display = (index != value) ? "none" : "inline"; }); } }; //---------------------------------------------------------- // // TKSwitchPositiveNegative // // Shows the element's first child if value is positive or zero. // Shows the element's second child if value is negative. Tangle.classes.TKSwitchPositiveNegative = { update: function (element, value) { Tangle.classes.TKSwitch.update(element, value < 0); } }; //---------------------------------------------------------- // // TKToggle // // Click to toggle value between 0 and 1. Tangle.classes.TKToggle = { initialize: function (element, options, tangle, variable) { element.addEvent("click", function (event) { var isActive = tangle.getValue(variable); tangle.setValue(variable, isActive ? 0 : 1); }); } }; //---------------------------------------------------------- // // TKNumberField // // An input box where a number can be typed in. // // Attributes: data-size (optional): width of the box in characters Tangle.classes.TKNumberField = { initialize: function (element, options, tangle, variable) { this.input = new Element("input", { type: "text", "class":"TKNumberFieldInput", size: options.size || 6 }).inject(element, "top"); var inputChanged = (function () { var value = this.getValue(); tangle.setValue(variable, value); }).bind(this); this.input.addEvent("keyup", inputChanged); this.input.addEvent("blur", inputChanged); this.input.addEvent("change", inputChanged); }, getValue: function () { var value = parseFloat(this.input.get("value")); return isNaN(value) ? 0 : value; }, update: function (element, value) { var currentValue = this.getValue(); if (value !== currentValue) { this.input.set("value", "" + value); } } }; //---------------------------------------------------------- // // TKAdjustableNumber // // Drag a number to adjust. // // Attributes: data-min (optional): minimum value // data-max (optional): maximum value // data-step (optional): granularity of adjustment (can be fractional) var isAnyAdjustableNumberDragging = false; // hack for dragging one value over another one Tangle.classes.TKAdjustableNumber = { initialize: function (element, options, tangle, variable) { this.element = element; this.tangle = tangle; this.variable = variable; this.min = (options.min !== undefined) ? parseFloat(options.min) : 1; this.max = (options.max !== undefined) ? parseFloat(options.max) : 10; this.step = (options.step !== undefined) ? parseFloat(options.step) : 1; this.initializeHover(); this.initializeHelp(); this.initializeDrag(); }, // hover initializeHover: function () { this.isHovering = false; this.element.addEvent("mouseenter", (function () { this.isHovering = true; this.updateRolloverEffects(); }).bind(this)); this.element.addEvent("mouseleave", (function () { this.isHovering = false; this.updateRolloverEffects(); }).bind(this)); }, updateRolloverEffects: function () { this.updateStyle(); this.updateCursor(); this.updateHelp(); }, isActive: function () { return this.isDragging || (this.isHovering && !isAnyAdjustableNumberDragging); }, updateStyle: function () { if (this.isDragging) { this.element.addClass("TKAdjustableNumberDown"); } else { this.element.removeClass("TKAdjustableNumberDown"); } if (!this.isDragging && this.isActive()) { this.element.addClass("TKAdjustableNumberHover"); } else { this.element.removeClass("TKAdjustableNumberHover"); } }, updateCursor: function () { var body = document.getElement("body"); if (this.isActive()) { body.addClass("TKCursorDragHorizontal"); } else { body.removeClass("TKCursorDragHorizontal"); } }, // help initializeHelp: function () { this.helpElement = (new Element("div", { "class": "TKAdjustableNumberHelp" })).inject(this.element, "top"); this.helpElement.setStyle("display", "none"); this.helpElement.set("text", "drag"); }, updateHelp: function () { var size = this.element.getSize(); var top = -size.y + 7; var left = Math.round(0.5 * (size.x - 20)); var display = (this.isHovering && !isAnyAdjustableNumberDragging) ? "block" : "none"; this.helpElement.setStyles({ left:left, top:top, display:display }); }, // drag initializeDrag: function () { this.isDragging = false; new BVTouchable(this.element, this); }, touchDidGoDown: function (touches) { this.valueAtMouseDown = this.tangle.getValue(this.variable); this.isDragging = true; isAnyAdjustableNumberDragging = true; this.updateRolloverEffects(); this.updateStyle(); }, touchDidMove: function (touches) { var value = this.valueAtMouseDown + touches.translation.x / 5 * this.step; value = ((value / this.step).round() * this.step).limit(this.min, this.max); this.tangle.setValue(this.variable, value); this.updateHelp(); }, touchDidGoUp: function (touches) { this.helpElement.setStyle("display", "none"); this.isDragging = false; isAnyAdjustableNumberDragging = false; this.updateRolloverEffects(); this.updateStyle(); } }; //---------------------------------------------------------- // // formats // // Most of these are left over from older versions of Tangle, // before parameters and printf were available. They should // be redesigned. // function formatValueWithPrecision (value,precision) { if (Math.abs(value) >= 100) { precision--; } if (Math.abs(value) >= 10) { precision--; } return "" + value.round(Math.max(precision,0)); } Tangle.formats.p3 = function (value) { return formatValueWithPrecision(value,3); }; Tangle.formats.neg_p3 = function (value) { return formatValueWithPrecision(-value,3); }; Tangle.formats.p2 = function (value) { return formatValueWithPrecision(value,2); }; Tangle.formats.e6 = function (value) { return "" + (value * 1e-6).round(); }; Tangle.formats.abs_e6 = function (value) { return "" + (Math.abs(value) * 1e-6).round(); }; Tangle.formats.freq = function (value) { if (value < 100) { return "" + value.round(1) + " Hz"; } if (value < 1000) { return "" + value.round(0) + " Hz"; } return "" + (value / 1000).round(2) + " KHz"; }; Tangle.formats.dollars = function (value) { return "$" + value.round(0); }; Tangle.formats.free = function (value) { return value ? ("$" + value.round(0)) : "free"; }; Tangle.formats.percent = function (value) { return "" + (100 * value).round(0) + "%"; }; //---------------------------------------------------------- })();