/* Copyright (c) 2012, Motorola Mobility LLC. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Motorola Mobility LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Style component: Edits and manages a single style rule for a Layer in the Timeline. * Public Properties: * editorProperty: The CSS property for the style. * editorValue: The value for the editorProperty. * whichView: Which view to show, the hintable view (where a new property can be typed in) * or the propval view (where the property's value can be set with the tweener). * Valid values are "hintable" and "propval", defaults to "hintable". * */ var Montage = require("montage/core/core").Montage; var Component = require("montage/ui/component").Component; var ElementsMediator = require("js/mediators/element-mediator").ElementMediator var LayerStyle = exports.LayerStyle = Montage.create(Component, { /* === BEGIN: Models === */ _parentLayerComponent: { value: null }, _styleContainer: { value: null }, styleContainer: { serializable: true, get: function() { return this._styleContainer; }, set: function(newVal) { this._styleContainer = newVal; } }, _styleHintable: { value: null }, styleHintable: { serializable: true, get: function() { return this._styleHintable; }, set: function(newVal) { this._styleHintable = newVal; } }, _styleProperty: { value: null }, styleProperty: { serializable: true, get: function() { return this._styleProperty; }, set: function(newVal) { this._styleProperty = newVal; } }, _valueEditorHottext: { value: null }, valueEditorHottext: { serializable: true, get: function() { return this._valueEditorHottext; }, set: function(newVal) { this._valueEditorHottext = newVal; } }, _dtextProperty: { value: null }, dtextProperty: { serializable: true, get: function() { return this._dtextProperty; }, set: function(newVal) { this._dtextProperty = newVal; } }, _isSelected: { value: false }, isSelected: { serializable: true, get: function() { return this._isSelected; }, set: function(newVal) { if (newVal !== this._isSelected) { this._isSelected = newVal; this.needsDraw = true; } } }, _isActive: { value: false }, isActive: { get: function() { return this._isActive; }, set: function(newVal) { this._isActive = newVal; } }, // Property for this editor _editorProperty: { value: "" }, editorProperty: { serializable: true, get: function() { return this._editorProperty; }, set: function(newVal) { this._editorProperty = newVal; this.needsDraw = true; } }, // Value for the property for this editor. _editorValue: { value: "" }, editorValue: { serializable: true, get: function() { return this._editorValue; }, set: function(newVal) { this._editorValue = newVal; this.needsDraw = true; } }, // The tweener used to change the value for this property. _ruleTweener: { value: false }, ruleTweener: { serializable: true, get: function() { return this._ruleTweener; }, set: function(newVal) { this._ruleTweener = newVal; this.needsDraw = true; } }, // The hintable we use to change the Property _myHintable: { value: "" }, myHintable: { get: function() { return this._myHintable; }, set: function(newVal) { this._myHintable = newVal; }, serializable: true }, _myHintableValue : { value: null }, myHintableValue: { get: function() { return this._myHintableValue; }, set: function(newVal) { this._myHintableValue = newVal; } }, // swapViews: Is a view swap happening? _swapViews : { value: true }, // whichView: which view should we show: hintable or propval _whichView : { value: "hintable" }, whichView: { serializable: true, get: function() { return this._whichView; }, set: function(newVal) { if (this._whichView !== newVal) { if ((newVal !== "hintable") && (newVal !== "propval")) { console.log("Error: Unknown view -"+newVal+"- requested for style.js."); return; } this._whichView = newVal; this._swapViews = true; this.needsDraw = true; } } }, // styleID: the id for this style; // Used to publish events _styleID : { value: null }, styleID: { serializable: true, get: function() { return this._styleID; }, set: function(newVal) { this._styleID = newVal; this.needsDraw = true; } }, addedColorChips: { value: false }, _colorelement: { writable:true }, colorelement: { enumerable: true, get: function () { return this._colorelement; }, set: function (value) { if (value !== this._colorelement) { this._colorelement = value; } } }, _fill: { enumerable: false, value: { colorMode: 'rgb', color: { r: 255, g: 255, b: 255, a: 1, css: 'rgb(255,255,255)', mode: 'rgb', wasSetByCode: true, type: 'change' }, webGlColor: [1, 1, 1, 1] } }, fill: { enumerable: true, get: function () { return this._fill; }, set: function (value) { if (value !== this._fill) { this._fill = value; } } }, handleMousedown: { value: function(event) { this.isActive = true; } }, /* === END: Models === */ /* === BEGIN : Draw cycle === */ prepareForDraw: { value: function() { this.init(); } }, willDraw: { value: function() { if (this._parentLayerComponent === null) { this._parentLayerComponent = this.parentComponent.parentComponent.parentComponent.parentComponent; } } }, draw: { value: function() { // Show the right view if (this._swapViews === true) { this._showView(); } // Is this style selected? if (this.isSelected) { this.element.classList.add("style-selected"); } else { this.element.classList.remove("style-selected"); } } }, didDraw: { value: function() { if (this._swapViews === true) { // View swap has been completed. this._swapViews === false; } } }, /* === END: Draw cycle === */ /* === BEGIN: controllers === */ // handleStylePropertyDblClick: What happens when the user double-clicks on the style property handleStylePropertyDblclick: { value: function(event) { this.whichView = "hintable"; } }, // handleHintableStop: What happens when the hintable issues its stop event handleHintableStop: { value: function(event) { // this should be handled via binding, but somehow is not. Setting manually for now. this.editorProperty = this.myHintable.value; // Change views. this.whichView = "propval"; } }, handleFillColorChange: { value: function (event) { if(this.application.ninja.timeline.selectedStyle === "color" ||this.application.ninja.timeline.selectedStyle === this.editorProperty){ var fillColorObject={}; fillColorObject.color=event._event.color; fillColorObject.mode=event._event.colorMode; ElementsMediator.setColor([this._parentLayerComponent.layerData.stageElement], fillColorObject, this._isFill, "Change", "timeline",null,this._borderSide) } } }, handleHottextChange:{ value:function(event){ if(this.application.ninja.timeline.selectedStyle === this.editorProperty){ this.application.ninja.elementMediator.setProperty([this._parentLayerComponent.layerData.stageElement], this.editorProperty, [this.editorValue + event.target._units] , "Change", "timeline"); } } }, handleHottextChanging:{ value:function(event){ if(this.application.ninja.timeline.selectedStyle === this.editorProperty){ this.application.ninja.elementMediator.setProperty([this._parentLayerComponent.layerData.stageElement], this.editorProperty, [this.editorValue + event.target._units] , "Changing", "timeline"); } } }, handleBlur:{ value:function(event){ if(this.application.ninja.timeline.selectedStyle === this.editorProperty){ this.application.ninja.elementMediator.setProperty([this._parentLayerComponent.layerData.stageElement], this.editorProperty, [event.target.value] , "Change", "timeline"); } } }, // Init: Initialize the component with some useful selectors and other defaults. init : { value: function() { var arrHints = [], i = 0; // Get the array of hints from _myTweenables: for (i = 0; i < this._myTweenables.length; i++) { arrHints.push(this._myTweenables[i].property) } // Set useful information for the hintable this.myHintable.editingClass = "editable2"; this.myHintable.hints = arrHints; // Bind a handler to the Hintable's change event this.myHintable.identifier = "hintable"; this.myHintable.addEventListener("stop", this, false); // Add the click handler to the styleProperty: When the user double-clicks on it, we want to start the editor. this.styleProperty.identifier = "styleProperty"; this.styleProperty.addEventListener("dblclick", this, false); // Get some selectors that we'll be using this.editorHottextContainer = this.element.querySelector(".editor-hottext"); this.editorInputContainer = this.element.querySelector(".editor-input"); this.editorColorContainer = this.element.querySelector(".editor-color"); this.containerHintable = this.element.querySelector(".row-hintable"); this.containerPropvals = this.element.querySelector(".container-propvals"); this.valueEditorInput = this.element.querySelector(".editor-input input"); // mousedown listener to handle this.element.addEventListener("mousedown", this, false); } }, // showView: Show the appropriate view _showView : { value: function() { if (this.whichView === "hintable") { this.containerHintable.classList.remove("hideme"); this.containerPropvals.classList.add("hideme"); this.myHintable.start(); } else { this.containerHintable.classList.add("hideme"); this.containerPropvals.classList.remove("hideme"); this._showTweener(); } } }, // showTweener: show the appropriate tweener _showTweener : { value: function() { // Which tweener should we show? // First, get the appropriate editor type from the data structure. var tweenable = {}, i = 0; tweenable.tweener = "input"; for (i = 0; i < this._myTweenables.length; i++) { if (this._myTweenables[i].property === this.editorProperty) { tweenable = this._myTweenables[i]; } } if (tweenable.tweener === "hottext" ) { this.editorInputContainer.classList.add("hideme"); this.editorColorContainer.classList.add("hideme"); this.editorHottextContainer.classList.remove("hideme"); this.valueEditorHottext.acceptableUnits = [tweenable.units]; this.valueEditorHottext.units = tweenable.units; this.valueEditorHottext.minValue = tweenable.min; this.valueEditorHottext.maxValue = tweenable.max; this.valueEditorHottext.identifier="hottext"; el = this._parentLayerComponent.layerData.stageElement; this.editorValue = parseFloat(ElementsMediator.getProperty(el, this.editorProperty)); this.valueEditorHottext.value = this.editorValue this.valueEditorHottext.addEventListener("change",this,false); this.valueEditorHottext.addEventListener("changing",this,false); this.valueEditorHottext.needsDraw = true; } else if (tweenable.tweener === "color" ) { this.editorInputContainer.classList.add("hideme"); this.editorColorContainer.classList.remove("hideme"); this.editorHottextContainer.classList.add("hideme"); if(tweenable.colorType === "fill"){ this._isFill = true; }else{ if(tweenable.colorType === "stroke"){ this._isFill = false; this._borderSide = tweenable.strokePosition } } if (this.addedColorChips === false && this.application.ninja.colorController.colorPanelDrawn) { // setup fill color this._fillColorCtrl.props = { side: 'top', align: 'center', wheel: true, palette: true, gradient: false, image: false, nocolor: true, offset: -80 }; this.application.ninja.colorController.addButton("chip", this._fillColorCtrl); this.colorelement = this._fillColorCtrl; var currentValue = ElementsMediator.getColor(this._parentLayerComponent.layerData.stageElement,this._isFill,this._borderSide) this.application.ninja.timeline.selectedStyle = this.editorProperty; this._fillColorCtrl.addEventListener("change", this.handleFillColorChange.bind(this), false); if(currentValue){ this._fillColorCtrl.color(currentValue.colorMode, currentValue.color); this.addedColorChips = true; } } // TODO: set up color chip here. } else if (tweenable.tweener === "input"){ this.editorInputContainer.classList.remove("hideme"); this.editorColorContainer.classList.add("hideme"); this.editorHottextContainer.classList.add("hideme"); this.valueEditorInput.value = this.editorValue; this.valueEditorInput.addEventListener("blur",this,false); } else { this.log("Warning: unknown tweenable -"+tweenable.tweener+"- specified in style.js.") } } }, /* === END: Controllers === */ _myTweenables: { value: [ { "property" : "background-color", "tweener" : "color", "units" : "", "min" : "", "max" : "", "default" :"#FFFFFF", "colorType" :"fill" }, { "property" : "background-position-x", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "background-position-y", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "border-color", "tweener" : "color", "units" : "", "min" : "", "max" : "", "default" : "#FFFFFF", "colorType" : "stroke", "strokePosition" : false }, { "property" : "border-width", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "border-bottom-color", "tweener" : "color", "units" : "", "default" : "#FFFFFF", "colorType" : "stroke", "strokePosition" : "bottom" }, { "property" : "border-bottom-width", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "border-left-color", "tweener" : "color", "units" : "", "default" : "#FFFFFF", "colorType" : "stroke", "strokePosition" : "left" }, { "property" : "border-left-width", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "border-top-color", "tweener" : "color", "units" : "", "default" : "#FFFFFF", "colorType" : "stroke", "strokePosition" : "top" }, { "property" : "border-top-width", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "border-right-color", "tweener" : "color", "units" : "", "default" : "#FFFFFF", "colorType" : "stroke", "strokePosition" : "right" }, { "property" : "border-right-width", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "border-radius", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "bottom", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "color", "tweener" : "color", "units" : "", "default" : "#FFFFFF" }, { "property" : "margin", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "margin-left", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "margin-right", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "margin-top", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "margin-bottom", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "padding", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "padding-left", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "padding-right", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "padding-top", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "padding-bottom", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "max-height", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "max-width", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "min-height", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "min-width", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "opacity", "tweener" : "hottext", "units" : "", "min" : 0, "max" : 100, "default" : 100 }, { "property" : "text-indent", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "top", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "right", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "left", "tweener" : "hottext", "units" : "px", "min" : -9999, "max" : 9999, "default" : 0 }, { "property" : "width", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 }, { "property" : "height", "tweener" : "hottext", "units" : "px", "min" : 0, "max" : 9999, "default" : 0 } ] } /* End: Logging routines */ });