# Source: ScrollMagic/Scene/getters-setters.js

    var _validate = _util.extend(SCENE_OPTIONS.validate, {
    	// validation for duration handled internally for reference to private var _durationMethod
    	duration : function (val) {
    		if (_util.type.String(val) && val.match(/^(\.|\d)*\d+%$/)) {
    			// percentage value
    			var perc = parseFloat(val) / 100;
    			val = function () {
    				return _controller ? _controller.info("size") * perc : 0;
    			};
    		}
    		if (_util.type.Function(val)) {
    			// function
    			_durationUpdateMethod = val;
    			try {
    				val = parseFloat(_durationUpdateMethod.call(Scene));
    			} catch (e) {
    				val = -1; // will cause error below
    			}
    		}
    		// val has to be float
    		val = parseFloat(val);
    		if (!_util.type.Number(val) || val < 0) {
    			if (_durationUpdateMethod) {
    				_durationUpdateMethod = undefined;
    				throw ["Invalid return value of supplied function for option \"duration\":", val];
    			} else {
    				throw ["Invalid value for option \"duration\":", val];
    			}
    		}
    		return val;
    	}
    });

    /**
     * Checks the validity of a specific or all options and reset to default if neccessary.
     * @private
     */
    var validateOption = function (check) {
    	check = arguments.length ? [check] : Object.keys(_validate);
    	check.forEach(function (optionName, key) {
    		var value;
    		if (_validate[optionName]) { // there is a validation method for this option
    			try { // validate value
    				value = _validate[optionName](_options[optionName]);
    			} catch (e) { // validation failed -> reset to default
    				value = DEFAULT_OPTIONS[optionName];
    				// (BUILD) - REMOVE IN MINIFY - START
    				var logMSG = _util.type.String(e) ? [e] : e;
    				if (_util.type.Array(logMSG)) {
    					logMSG[0] = "ERROR: " + logMSG[0];
    					logMSG.unshift(1); // loglevel 1 for error msg
    					log.apply(this, logMSG);
    				} else {
    					log(1, "ERROR: Problem executing validation callback for option '" + optionName + "':", e.message);
    				}
    				// (BUILD) - REMOVE IN MINIFY - END
    			} finally {
    				_options[optionName] = value;
    			}
    		}
    	});
    };

    /**
     * Helper used by the setter/getters for scene options
     * @private
     */
    var changeOption = function(varname, newval) {
    	var
    		changed = false,
    		oldval = _options[varname];
    	if (_options[varname] != newval) {
    		_options[varname] = newval;
    		validateOption(varname); // resets to default if necessary
    		changed = oldval != _options[varname];
    	}
    	return changed;
    };

    // generate getters/setters for all options
    var addSceneOption = function (optionName) {
    	if (!Scene[optionName]) {
    		Scene[optionName] = function (newVal) {
    			if (!arguments.length) { // get
    				return _options[optionName];
    			} else {
    				if (optionName === "duration") { // new duration is set, so any previously set function must be unset
    					_durationUpdateMethod = undefined;
    				}
    				if (changeOption(optionName, newVal)) { // set
    					Scene.trigger("change", {what: optionName, newval: _options[optionName]});
    					if (SCENE_OPTIONS.shifts.indexOf(optionName) > -1) {
    						Scene.trigger("shift", {reason: optionName});
    					}
    				}
    			}
    			return Scene;
    		};
    	}
    };

    /**
     * **Get** or **Set** the duration option value.
     *
     * As a **setter** it accepts three types of parameters:
     * 1. `number`: Sets the duration of the scene to exactly this amount of pixels.
     *   This means the scene will last for exactly this amount of pixels scrolled. Sub-Pixels are also valid.
     *   A value of `0` means that the scene is 'open end' and no end will be triggered. Pins will never unpin and animations will play independently of scroll progress.
     * 2. `string`: Always updates the duration relative to parent scroll container.
     *   For example `"100%"` will keep the duration always exactly at the inner height of the scroll container.
     *   When scrolling vertically the width is used for reference respectively.
     * 3. `function`: The supplied function will be called to return the scene duration.
     *   This is useful in setups where the duration depends on other elements who might change size. By supplying a function you can return a value instead of updating potentially multiple scene durations.
     *   The scene can be referenced inside the callback using `this`.
     *   _**WARNING:** This is an easy way to kill performance, as the callback will be executed every time `Scene.refresh()` is called, which happens a lot. The interval is defined by the controller (see ScrollMagic.Controller option `refreshInterval`).
     *   It's recomended to avoid calculations within the function and use cached variables as return values.
     *   This counts double if you use the same function for multiple scenes._
     *
     * @method ScrollMagic.Scene#duration
     * @example
     * // get the current duration value
     * var duration = scene.duration();
     *
     * // set a new duration
     * scene.duration(300);
     *
     * // set duration responsively to container size
     * scene.duration("100%");
     *
     * // use a function to randomize the duration for some reason.
     * var durationValueCache;
     * function durationCallback () {
     *   return durationValueCache;
     * }
     * function updateDuration () {
     *   durationValueCache = Math.random() * 100;
     * }
     * updateDuration(); // set to initial value
     * scene.duration(durationCallback); // set duration callback
     *
     * @fires {@link Scene.change}, when used as setter
     * @fires {@link Scene.shift}, when used as setter
     * @param {(number|string|function)} [newDuration] - The new duration setting for the scene.
     * @returns {number} `get` -  Current scene duration.
     * @returns {Scene} `set` -  Parent object for chaining.
     */

    /**
     * **Get** or **Set** the offset option value.
     * @method ScrollMagic.Scene#offset
     * @example
     * // get the current offset
     * var offset = scene.offset();
     *
    	 * // set a new offset
     * scene.offset(100);
     *
     * @fires {@link Scene.change}, when used as setter
     * @fires {@link Scene.shift}, when used as setter
     * @param {number} [newOffset] - The new offset of the scene.
     * @returns {number} `get` -  Current scene offset.
     * @returns {Scene} `set` -  Parent object for chaining.
     */

    /**
     * **Get** or **Set** the triggerElement option value.
     * Does **not** fire `Scene.shift`, because changing the trigger Element doesn't necessarily mean the start position changes. This will be determined in `Scene.refresh()`, which is automatically triggered.
     * @method ScrollMagic.Scene#triggerElement
     * @example
     * // get the current triggerElement
     * var triggerElement = scene.triggerElement();
     *
    	 * // set a new triggerElement using a selector
     * scene.triggerElement("#trigger");
    	 * // set a new triggerElement using a DOM object
     * scene.triggerElement(document.getElementById("trigger"));
     *
     * @fires {@link Scene.change}, when used as setter
     * @param {(string|object)} [newTriggerElement] - The new trigger element for the scene.
     * @returns {(string|object)} `get` -  Current triggerElement.
     * @returns {Scene} `set` -  Parent object for chaining.
     */

    /**
     * **Get** or **Set** the triggerHook option value.
     * @method ScrollMagic.Scene#triggerHook
     * @example
     * // get the current triggerHook value
     * var triggerHook = scene.triggerHook();
     *
    	 * // set a new triggerHook using a string
     * scene.triggerHook("onLeave");
    	 * // set a new triggerHook using a number
     * scene.triggerHook(0.7);
     *
     * @fires {@link Scene.change}, when used as setter
     * @fires {@link Scene.shift}, when used as setter
     * @param {(number|string)} [newTriggerHook] - The new triggerHook of the scene. See {@link Scene} parameter description for value options.
     * @returns {number} `get` -  Current triggerHook (ALWAYS numerical).
     * @returns {Scene} `set` -  Parent object for chaining.
     */

    /**
     * **Get** or **Set** the reverse option value.
     * @method ScrollMagic.Scene#reverse
     * @example
     * // get the current reverse option
     * var reverse = scene.reverse();
     *
    	 * // set new reverse option
     * scene.reverse(false);
     *
     * @fires {@link Scene.change}, when used as setter
     * @param {boolean} [newReverse] - The new reverse setting of the scene.
     * @returns {boolean} `get` -  Current reverse option value.
     * @returns {Scene} `set` -  Parent object for chaining.
     */

    /**
     * **Get** or **Set** the loglevel option value.
     * @method ScrollMagic.Scene#loglevel
     * @example
     * // get the current loglevel
     * var loglevel = scene.loglevel();
     *
    	 * // set new loglevel
     * scene.loglevel(3);
     *
     * @fires {@link Scene.change}, when used as setter
     * @param {number} [newLoglevel] - The new loglevel setting of the scene. `[0-3]`
     * @returns {number} `get` -  Current loglevel.
     * @returns {Scene} `set` -  Parent object for chaining.
     */

    /**
     * **Get** the associated controller.
     * @method ScrollMagic.Scene#controller
     * @example
     * // get the controller of a scene
     * var controller = scene.controller();
     *
     * @returns {ScrollMagic.Controller} Parent controller or `undefined`
     */
    this.controller = function () {
    	return _controller;
    };

    /**
     * **Get** the current state.
     * @method ScrollMagic.Scene#state
     * @example
     * // get the current state
     * var state = scene.state();
     *
     * @returns {string} `"BEFORE"`, `"DURING"` or `"AFTER"`
     */
    this.state = function () {
    	return _state;
    };

    /**
     * **Get** the current scroll offset for the start of the scene.
     * Mind, that the scrollOffset is related to the size of the container, if `triggerHook` is bigger than `0` (or `"onLeave"`).
     * This means, that resizing the container or changing the `triggerHook` will influence the scene's start offset.
     * @method ScrollMagic.Scene#scrollOffset
     * @example
     * // get the current scroll offset for the start and end of the scene.
     * var start = scene.scrollOffset();
     * var end = scene.scrollOffset() + scene.duration();
     * console.log("the scene starts at", start, "and ends at", end);
     *
     * @returns {number} The scroll offset (of the container) at which the scene will trigger. Y value for vertical and X value for horizontal scrolls.
     */
    this.scrollOffset = function () {
    	return _scrollOffset.start;
    };

    /**
     * **Get** the trigger position of the scene (including the value of the `offset` option).
     * @method ScrollMagic.Scene#triggerPosition
     * @example
     * // get the scene's trigger position
     * var triggerPosition = scene.triggerPosition();
     *
     * @returns {number} Start position of the scene. Top position value for vertical and left position value for horizontal scrolls.
     */
    this.triggerPosition = function () {
    	var pos = _options.offset; // the offset is the basis
    	if (_controller) {
    		// get the trigger position
    		if (_options.triggerElement) {
    			// Element as trigger
    			pos += _triggerPos;
    		} else {
    			// return the height of the triggerHook to start at the beginning
    			pos += _controller.info("size") * Scene.triggerHook();
    		}
    	}
    	return pos;
    };