<commit_before>import { makeLowercaseSetterAliases } from './Utility'
import TreeNode from './TreeNode'
import XYZValues from './XYZValues'
import Observable from './Observable'
import Motor from './Motor'

// fallback to experimental CSS transform if browser doesn't have it (fix for Safari 9)
if (typeof document.createElement('div').style.transform == 'undefined') {
    Object.defineProperty(CSSStyleDeclaration.prototype, 'transform', {
        set(value) {
            this.webkitTransform = value
        },
        get() {
            return this.webkitTransform
        },
        enumerable: true,
    })
}

class XYZNonNegativeValues extends XYZValues {

    constructor(x = 0, y = 0, z = 0) {
        super(x, y, z)
    }

    _checkForNegative(axisName, value) {
        if(value < 0) {
            throw new Error(axisName + " value was " + value + ". Size values cannot be negative.")
        }
    }

}

const {set: superXSet} = Object.getOwnPropertyDescriptor(XYZValues.prototype, 'x')
const {set: superYSet} = Object.getOwnPropertyDescriptor(XYZValues.prototype, 'y')
const {set: superZSet} = Object.getOwnPropertyDescriptor(XYZValues.prototype, 'z')


Object.defineProperties(XYZNonNegativeValues.prototype, {
    x: {
        set(value) {
            this._checkForNegative("X", value)
            superXSet.call(this, value)
        }
    },

    y: {
        set(value) {
            this._checkForNegative("Y", value)
            superYSet.call(this, value)
      }
    },

    z: {
        set(value) {
            this._checkForNegative("Z", value)
            superZSet.call(this, value)
      }
    },
})



const instanceofSymbol = Symbol('instanceofSymbol')

const SizeableMixin = base => {

    // Sizeable extends TreeNode because Sizeable knows about its _parent when
    // calculating proportionalSize. Also Transformable knows about it's parent
    // in order to calculate it's world matrix based on it's parent's.
    class Sizeable extends TreeNode.mixin(Observable.mixin(base)) {

        constructor(options = {}) {
            super(options)

            this._propertyFunctions = null
            this._calculatedSize = { x:0, y:0, z:0 }
            this._properties = {}
            this._setDefaultProperties()
            this._setPropertyObservers()
            this.properties = options
        }

        _setDefaultProperties() {
            Object.assign(this._properties, {
                sizeMode:         new XYZValues('absolute', 'absolute', 'absolute'),
                absoluteSize:     new XYZNonNegativeValues(0, 0, 0),
                proportionalSize: new XYZNonNegativeValues(1, 1, 1),
            })
        }

        _setPropertyObservers() {
            this._properties.sizeMode.on('valuechanged',
                () => this.triggerEvent('propertychange', 'sizeMode'))
            this._properties.absoluteSize.on('valuechanged',
                () => this.triggerEvent('propertychange', 'absoluteSize'))
            this._properties.proportionalSize.on('valuechanged',
                () => this.triggerEvent('propertychange', 'proportionalSize'))
        }

        _calcSize() {
            const calculatedSize = this._calculatedSize
            const previousSize = {...calculatedSize}
            const props = this._properties
            const parentSize = this._getParentSize()

            if (props.sizeMode._x == 'absolute') {
                calculatedSize.x = props.absoluteSize._x
            }
            else { // proportional
                calculatedSize.x = parentSize.x * props.proportionalSize._x
            }

            if (props.sizeMode._y == 'absolute') {
                calculatedSize.y = props.absoluteSize._y
            }
            else { // proportional
                calculatedSize.y = parentSize.y * props.proportionalSize._y
            }

            if (props.sizeMode._z == 'absolute') {
                calculatedSize.z = props.absoluteSize._z
            }
            else { // proportional
                calculatedSize.z = parentSize.z * props.proportionalSize._z
            }

            if (
                previousSize.x !== calculatedSize.x
                || previousSize.y !== calculatedSize.y
                || previousSize.z !== calculatedSize.z
            ) {
                this.triggerEvent('sizechange', {...calculatedSize})
            }
        }

        _getParentSize() {
            return this._parent ? this._parent._calculatedSize : {x:0,y:0,z:0}
        }

        _setPropertyXYZ(Class, name, newValue) {
            if (!(
                newValue instanceof Object ||
                newValue instanceof Array ||
                newValue instanceof Function
            )) {
                throw new TypeError(`Invalid value for ${Class.name}#${name}.`)
            }

            let change = false

            if (newValue instanceof Function) {
                // remove previous task if any.
                if (!this._propertyFunctions) this._propertyFunctions = new Map

                if (this._propertyFunctions.has(name))
                    Motor.removeRenderTask(this._propertyFunctions.get(name))

                this._propertyFunctions.set(name,
                    Motor.addRenderTask(time => {
                        const result = newValue(
                            this._properties[name].x,
                            this._properties[name].y,
                            this._properties[name].z,
                            time
                        )

                        if (result === false) {
                            this._propertyFunctions.delete(name)
                            return false
                        }

                        this[name] = result
                    })
                )
            }
            else if (newValue instanceof Array) {
                if (typeof newValue[0] != 'undefined') { this._properties[name].x = newValue[0]; change = true }
                if (typeof newValue[1] != 'undefined') { this._properties[name].y = newValue[1]; change = true }
                if (typeof newValue[2] != 'undefined') { this._properties[name].z = newValue[2]; change = true }
            }
            else {
                if (typeof newValue.x != 'undefined') { this._properties[name].x = newValue.x; change = true }
                if (typeof newValue.y != 'undefined') { this._properties[name].y = newValue.y; change = true }
                if (typeof newValue.z != 'undefined') { this._properties[name].z = newValue.z; change = true }
            }

            if (change) this.triggerEvent('propertychange', name)
        }

        _setPropertySingle(Class, name, newValue, type) {
            if (!(typeof newValue == type || newValue instanceof Function))
                throw new TypeError(`Invalid value for ${Class.name}#${name}.`)

            if (newValue instanceof Function) {
                // remove previous task if any.
                Motor.addRenderTask(time => {
                    const result = newValue(<commit_msg>No need to trigger the propertychange event in setPropertyXYZ anymore, it is triggerd as reaction to using the actual x/y/z setters on valuechange.<commit_after>import { makeLowercaseSetterAliases } from './Utility'
import TreeNode from './TreeNode'
import XYZValues from './XYZValues'
import Observable from './Observable'
import Motor from './Motor'

// fallback to experimental CSS transform if browser doesn't have it (fix for Safari 9)
if (typeof document.createElement('div').style.transform == 'undefined') {
    Object.defineProperty(CSSStyleDeclaration.prototype, 'transform', {
        set(value) {
            this.webkitTransform = value
        },
        get() {
            return this.webkitTransform
        },
        enumerable: true,
    })
}

class XYZNonNegativeValues extends XYZValues {

    constructor(x = 0, y = 0, z = 0) {
        super(x, y, z)
    }

    _checkForNegative(axisName, value) {
        if(value < 0) {
            throw new Error(axisName + " value was " + value + ". Size values cannot be negative.")
        }
    }

}

const {set: superXSet} = Object.getOwnPropertyDescriptor(XYZValues.prototype, 'x')
const {set: superYSet} = Object.getOwnPropertyDescriptor(XYZValues.prototype, 'y')
const {set: superZSet} = Object.getOwnPropertyDescriptor(XYZValues.prototype, 'z')


Object.defineProperties(XYZNonNegativeValues.prototype, {
    x: {
        set(value) {
            this._checkForNegative("X", value)
            superXSet.call(this, value)
        }
    },

    y: {
        set(value) {
            this._checkForNegative("Y", value)
            superYSet.call(this, value)
      }
    },

    z: {
        set(value) {
            this._checkForNegative("Z", value)
            superZSet.call(this, value)
      }
    },
})



const instanceofSymbol = Symbol('instanceofSymbol')

const SizeableMixin = base => {

    // Sizeable extends TreeNode because Sizeable knows about its _parent when
    // calculating proportionalSize. Also Transformable knows about it's parent
    // in order to calculate it's world matrix based on it's parent's.
    class Sizeable extends TreeNode.mixin(Observable.mixin(base)) {

        constructor(options = {}) {
            super(options)

            this._propertyFunctions = null
            this._calculatedSize = { x:0, y:0, z:0 }
            this._properties = {}
            this._setDefaultProperties()
            this._setPropertyObservers()
            this.properties = options
        }

        _setDefaultProperties() {
            Object.assign(this._properties, {
                sizeMode:         new XYZValues('absolute', 'absolute', 'absolute'),
                absoluteSize:     new XYZNonNegativeValues(0, 0, 0),
                proportionalSize: new XYZNonNegativeValues(1, 1, 1),
            })
        }

        _setPropertyObservers() {
            this._properties.sizeMode.on('valuechanged',
                () => this.triggerEvent('propertychange', 'sizeMode'))
            this._properties.absoluteSize.on('valuechanged',
                () => this.triggerEvent('propertychange', 'absoluteSize'))
            this._properties.proportionalSize.on('valuechanged',
                () => this.triggerEvent('propertychange', 'proportionalSize'))
        }

        _calcSize() {
            const calculatedSize = this._calculatedSize
            const previousSize = {...calculatedSize}
            const props = this._properties
            const parentSize = this._getParentSize()

            if (props.sizeMode._x == 'absolute') {
                calculatedSize.x = props.absoluteSize._x
            }
            else { // proportional
                calculatedSize.x = parentSize.x * props.proportionalSize._x
            }

            if (props.sizeMode._y == 'absolute') {
                calculatedSize.y = props.absoluteSize._y
            }
            else { // proportional
                calculatedSize.y = parentSize.y * props.proportionalSize._y
            }

            if (props.sizeMode._z == 'absolute') {
                calculatedSize.z = props.absoluteSize._z
            }
            else { // proportional
                calculatedSize.z = parentSize.z * props.proportionalSize._z
            }

            if (
                previousSize.x !== calculatedSize.x
                || previousSize.y !== calculatedSize.y
                || previousSize.z !== calculatedSize.z
            ) {
                this.triggerEvent('sizechange', {...calculatedSize})
            }
        }

        _getParentSize() {
            return this._parent ? this._parent._calculatedSize : {x:0,y:0,z:0}
        }

        _setPropertyXYZ(Class, name, newValue) {
            if (!(
                newValue instanceof Object ||
                newValue instanceof Array ||
                newValue instanceof Function
            )) {
                throw new TypeError(`Invalid value for ${Class.name}#${name}.`)
            }

            if (newValue instanceof Function) {
                // remove previous task if any.
                if (!this._propertyFunctions) this._propertyFunctions = new Map

                if (this._propertyFunctions.has(name))
                    Motor.removeRenderTask(this._propertyFunctions.get(name))

                this._propertyFunctions.set(name,
                    Motor.addRenderTask(time => {
                        const result = newValue(
                            this._properties[name].x,
                            this._properties[name].y,
                            this._properties[name].z,
                            time
                        )

                        if (result === false) {
                            this._propertyFunctions.delete(name)
                            return false
                        }

                        this[name] = result
                    })
                )
            }
            else if (newValue instanceof Array) {
                if (typeof newValue[0] != 'undefined') this._properties[name].x = newValue[0]
                if (typeof newValue[1] != 'undefined') this._properties[name].y = newValue[1]
                if (typeof newValue[2] != 'undefined') this._properties[name].z = newValue[2]
            }
            else {
                if (typeof newValue.x != 'undefined') this._properties[name].x = newValue.x
                if (typeof newValue.y != 'undefined') this._properties[name].y = newValue.y
                if (typeof newValue.z != 'undefined') this._properties[name].z = newValue.z
            }
        }

        _setPropertySingle(Class, name, newValue, type) {
            if (!(typeof newValue == type || newValue instanceof Function))
                throw new TypeError(`Invalid value for ${Class.name}#${name}.`)

            if (newValue instanceof Function) {
                // remove previous task if any.
                Motor.addRenderTask(time => {
                    const result = newValue(
{'fraction_non_alphanumeric': 0.1063890270769668, 'fraction_numerical': 0.0032691351005614384, 'mean_word_length': 1.6983700862895494, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4513227', 'n_tokens_mistral': 3690, 'n_tokens_neox': 3494, 'n_words': 784}