get: makeGetter()

in seriously.js [1947:2443]


								get: makeGetter(name),
								set: makeSetter(name)
							});
						}
					} else {
						//todo: this is temporary. get rid of it.
						throw new Error('Cannot overwrite Seriously.' + name);
					}
				}
			}

			Object.defineProperties(this, {
				effect: {
					enumerable: true,
					configurable: true,
					get: function () {
						return me.hook;
					}
				},
				title: {
					enumerable: true,
					configurable: true,
					get: function () {
						return me.effect.title || me.hook;
					}
				},
				width: {
					enumerable: true,
					configurable: true,
					get: function () {
						return me.width;
					}
				},
				height: {
					enumerable: true,
					configurable: true,
					get: function () {
						return me.height;
					}
				},
				id: {
					enumerable: true,
					configurable: true,
					get: function () {
						return me.id;
					}
				}
			});

			this.render = function () {
				me.render();
				return this;
			};

			this.readPixels = function (x, y, width, height, dest) {
				return me.readPixels(x, y, width, height, dest);
			};

			this.on = function (eventName, callback) {
				me.on(eventName, callback);
			};

			this.off = function (eventName, callback) {
				me.off(eventName, callback);
			};

			this.inputs = function (name) {
				var result,
					input,
					inputs,
					i,
					key;

				inputs = me.effect.inputs;

				if (name) {
					input = inputs[name];
					if (!input) {
						return null;
					}

					result = {
						type: input.type,
						defaultValue: input.defaultValue,
						title: input.title || name
					};

					if (input.type === 'number') {
						result.min = input.min;
						result.max = input.max;
						result.step = input.step;
					} else if (input.type === 'enum') {
						//make a copy
						result.options = extend({}, input.options);
					} else if (input.type === 'vector') {
						result.dimensions = input.dimensions;
					}

					if (input.description) {
						result.description = input.description;
					}

					return result;
				}

				result = {};
				for (key in inputs) {
					if (inputs.hasOwnProperty(key)) {
						result[key] = this.inputs(key);
					}
				}
				return result;
			};

			this.alias = function (inputName, aliasName) {
				me.alias(inputName, aliasName);
				return this;
			};

			this.matte = function (polygons) {
				me.matte(polygons);
			};

			this.destroy = function () {
				var i,
					descriptor;

				me.destroy();

				for (i in this) {
					if (this.hasOwnProperty(i) && i !== 'isDestroyed' && i !== 'id') {
						descriptor = Object.getOwnPropertyDescriptor(this, i);
						if (descriptor.get || descriptor.set ||
								typeof this[i] !== 'function') {
							delete this[i];
						} else {
							this[i] = nop;
						}
					}
				}
			};

			this.isDestroyed = function () {
				return me.isDestroyed;
			};

			this.isReady = function () {
				return me.ready;
			};
		};

		EffectNode = function (hook, options) {
			var key, name, input,
				defaultValue,
				defaults,
				defaultSources = {};

			Node.call(this, options);
			this.gl = gl;

			this.effectRef = seriousEffects[hook];
			this.sources = {};
			this.targets = [];
			this.inputElements = {};
			this.dirty = true;
			this.shaderDirty = true;
			this.hook = hook;
			this.options = options;
			this.transform = null;

			this.effect = extend({}, this.effectRef);
			if (this.effectRef.definition) {
				/*
				todo: copy over inputs object separately in case some are specified
				in advance and some are specified in definition function
				*/
				extend(this.effect, this.effectRef.definition.call(this, options));
			}
			validateInputSpecs(this.effect);

			this.uniforms.transform = identity;
			this.inputs = {};
			defaults = defaultInputs[hook];
			for (name in this.effect.inputs) {
				if (this.effect.inputs.hasOwnProperty(name)) {
					input = this.effect.inputs[name];

					if (input.defaultValue === undefined || input.defaultValue === null) {
						if (input.type === 'number') {
							input.defaultValue = Math.min(Math.max(0, input.min), input.max);
						} else if (input.type === 'color') {
							input.defaultValue = [0, 0, 0, 0];
						} else if (input.type === 'boolean') {
							input.defaultValue = false;
						} else if (input.type === 'string') {
							input.defaultValue = '';
						} else if (input.type === 'enum') {
							input.defaultValue = input.firstValue;
						}
					}

					defaultValue = input.validate.call(this, input.defaultValue, input);
					if (defaults && defaults[name] !== undefined) {
						defaultValue = input.validate.call(this, defaults[name], input, input.defaultValue, defaultValue);
						defaults[name] = defaultValue;
						if (input.type === 'image') {
							defaultSources[name] = defaultValue;
						}
					}

					this.inputs[name] = defaultValue;
					if (input.uniform) {
						this.uniforms[input.uniform] = input.defaultValue;
					}
				}
			}

			if (gl) {
				this.initialize();
				if (this.effect.commonShader) {
					/*
					this effect is unlikely to need to be modified again
					by changing parameters, so build it now to avoid jank later
					*/
					this.buildShader();
				}
			}

			this.updateReady();
			this.inPlace = this.effect.inPlace;

			this.pub = new Effect(this);

			nodes.push(this);
			nodesById[this.id] = this;
			effects.push(this);

			allEffectsByHook[hook].push(this);

			for (name in defaultSources) {
				if (defaultSources.hasOwnProperty(name)) {
					this.setInput(name, defaultSources[name]);
				}
			}
		};

		extend(EffectNode, Node);

		EffectNode.prototype.initialize = function () {
			if (!this.initialized) {
				var that = this;

				this.baseShader = baseShader;

				if (this.shape) {
					this.model = makeGlModel(this.shape, this.gl);
				} else {
					this.model = rectangleModel;
				}

				if (typeof this.effect.initialize === 'function') {
					this.effect.initialize.call(this, function () {
						that.initFrameBuffer(true);
					}, gl);
				} else {
					this.initFrameBuffer(true);
				}

				if (this.frameBuffer) {
					this.texture = this.frameBuffer.texture;
				}

				this.initialized = true;
			}
		};

		EffectNode.prototype.resize = function () {
			var i;

			Node.prototype.resize.call(this);

			if (this.effect.resize) {
				this.effect.resize.call(this);
			}

			for (i = 0; i < this.targets.length; i++) {
				this.targets[i].resize();
			}
		};

		EffectNode.prototype.updateReady = function () {
			var i,
				input,
				key,
				effect,
				ready = true,
				method;

			effect = this.effect;
			for (key in effect.inputs) {
				if (effect.inputs.hasOwnProperty(key)) {
					input = this.effect.inputs[key];
					if (input.type === 'image' &&
							(!this.sources[key] || !this.sources[key].ready) &&
							(!effect.requires || effect.requires.call(this, key, this.inputs))
							) {
						ready = false;
						break;
					}
				}
			}

			if (this.ready !== ready) {
				this.ready = ready;
				this.emit(ready ? 'ready' : 'unready');
				method = ready ? 'setReady' : 'setUnready';

				if (this.targets) {
					for (i = 0; i < this.targets.length; i++) {
						this.targets[i][method]();
					}
				}
			}
		};

		EffectNode.prototype.setReady = EffectNode.prototype.updateReady;

		EffectNode.prototype.setUnready = EffectNode.prototype.updateReady;

		EffectNode.prototype.addTarget = function (target) {
			var i;
			for (i = 0; i < this.targets.length; i++) {
				if (this.targets[i] === target) {
					return;
				}
			}

			this.targets.push(target);
		};

		EffectNode.prototype.removeTarget = function (target) {
			var i = this.targets && this.targets.indexOf(target);
			if (i >= 0) {
				this.targets.splice(i, 1);
			}
		};

		EffectNode.prototype.removeSource = function (source) {
			var i, pub = source && source.pub;

			for (i in this.inputs) {
				if (this.inputs.hasOwnProperty(i) &&
					(this.inputs[i] === source || this.inputs[i] === pub)) {
					this.inputs[i] = null;
				}
			}

			for (i in this.sources) {
				if (this.sources.hasOwnProperty(i) &&
					(this.sources[i] === source || this.sources[i] === pub)) {
					this.sources[i] = null;
				}
			}
		};

		EffectNode.prototype.buildShader = function () {
			var shader, effect = this.effect;
			if (this.shaderDirty) {
				if (effect.commonShader && commonShaders[this.hook]) {
					if (!this.shader) {
						commonShaders[this.hook].count++;
					}
					this.shader = commonShaders[this.hook].shader;
				} else if (effect.shader) {
					if (this.shader && !effect.commonShader) {
						this.shader.destroy();
					}
					shader = effect.shader.call(this, this.inputs, {
						vertex: baseVertexShader,
						fragment: baseFragmentShader
					}, Seriously.util);

					if (shader instanceof ShaderProgram) {
						this.shader = shader;
					} else if (shader && shader.vertex && shader.fragment) {
						this.shader = new ShaderProgram(gl, shader.vertex, shader.fragment);
					} else {
						this.shader = baseShader;
					}

					if (effect.commonShader) {
						commonShaders[this.hook] = {
							count: 1,
							shader: this.shader
						};
					}
				} else {
					this.shader = baseShader;
				}

				this.shaderDirty = false;
			}
		};

		EffectNode.prototype.render = function () {
			var key,
				frameBuffer,
				effect = this.effect,
				that = this,
				inPlace;

			function drawFn(shader, model, uniforms, frameBuffer, node, options) {
				draw(shader, model, uniforms, frameBuffer, node || that, options);
			}

			if (!gl) {
				return;
			}

			if (!this.initialized) {
				this.initialize();
			}

			if (this.shaderDirty) {
				this.buildShader();
			}

			if (this.dirty && this.ready) {
				for (key in this.sources) {
					if (this.sources.hasOwnProperty(key) &&
						(!effect.requires || effect.requires.call(this, key, this.inputs))) {

						//todo: set source texture in case it changes?
						//sourcetexture = this.sources[i].render() || this.sources[i].texture

						inPlace = typeof this.inPlace === 'function' ? this.inPlace(key) : this.inPlace;
						this.sources[key].render(!inPlace);
					}
				}

				if (this.frameBuffer) {
					frameBuffer = this.frameBuffer.frameBuffer;
				}

				if (typeof effect.draw === 'function') {
					effect.draw.call(this, this.shader, this.model, this.uniforms, frameBuffer, drawFn);
					this.emit('render');
				} else if (frameBuffer) {
					draw(this.shader, this.model, this.uniforms, frameBuffer, this);
					this.emit('render');
				}

				this.dirty = false;
			}

			return this.texture;
		};

		EffectNode.prototype.setInput = function (name, value) {
			var input, uniform,
				sourceKeys,
				source,
				me = this,
				defaultValue;

			function disconnectSource() {
				var previousSource = me.sources[name],
					key;

				/*
				remove this node from targets of previously connected source node,
				but only if the source node is not being used as another input
				*/
				if (previousSource) {
					for (key in me.sources) {
						if (key !== name &&
								me.sources.hasOwnProperty(key) &&
								me.sources[key] === previousSource) {
							return;
						}
					}
					previousSource.removeTarget(me);
				}
			}

			if (this.effect.inputs.hasOwnProperty(name)) {
				input = this.effect.inputs[name];
				if (input.type === 'image') {
					//&& !(value instanceof Effect) && !(value instanceof Source)) {

					if (value) {
						value = findInputNode(value);

						if (value !== this.sources[name]) {
							disconnectSource();

							if (traceSources(value, this)) {