<commit_before>import Ember from 'ember';

const { Mixin, computed, run } = Ember;

export default Mixin.create({
  classNames: ['form-group'],
  classNameBindings: ['validationState'],
  valid: false,
  _edited: false,
  name: null, //passed in
  property: null, //passed in
  validationState: computed('valid', '_edited', function() {
    if (!this.get('_edited')) { return ''; }
    if (!this.get('valid')) { return 'has-error'; }
    return 'has-success';
  }),

  focusOut() {
    this.set('_edited', true);
  },

  didInsertElement() {
    run.schedule("afterRender", () => {
      this.attrs.validate(this.get('name'), this.get('property'));
    });
  },

  _validate(value) {
    let { isValid, messages } = this.attrs.validate(this.get('name'), value);
    this.set('valid', isValid);
    this.set('errorMessages', messages);
    if (this.attrs['on-update']){
      this.attrs['on-update'](value);
    }
  },
});
<commit_msg>make validations optional<commit_after>import Ember from 'ember';

const { Mixin, computed, run } = Ember;

export default Mixin.create({
  classNames: ['form-group'],
  classNameBindings: ['validationState'],
  valid: false,
  _edited: false,
  name: null, //passed in
  property: null, //passed in
  validationState: computed('valid', '_edited', function() {
    if (!this.get('_edited')) { return ''; }
    if (!this.get('valid')) { return 'has-error'; }
    return 'has-success';
  }),

  focusOut() {
    this.set('_edited', true);
  },

  didInsertElement() {
    run.schedule("afterRender", () => {
      if (!this.attrs.validate) {
        return this.set('valid', true);
      }

      this.attrs.validate(this.get('name'), this.get('property'));
    });
  },

  _validate(value) {
    if (!this.attrs.validate) {
      return this.set('valid', true);
    }

    let { isValid, messages } = this.attrs.validate(this.get('name'), value);
    this.set('valid', isValid);
    this.set('errorMessages', messages);
    if (this.attrs['on-update']){
      this.attrs['on-update'](value);
    }
  },
});
<|endoftext|>


<commit_before>import  * as THREE from '../node_modules/three/build/three.module.js'
import  Utilies from './GeometryUtilities.js'
const { getBufferGeometryVertexElem, getGeometryVertexElem, getLongestEdgeIndex, getAverage, shrinkBoundsTo, shrinkSphereTo } = Utilies;

// Settings
const maxLeafNodes = 10;
const maxMatchingTriangles = 0.5;
const Strategies = {
    get CENTER()    { return 0; },
    get AVERAGE()   { return 1; },
    get SAH()       { return 2; }
}

const xyzFields = ['x', 'y', 'z'];

// Classes
class MeshBVHNode {
    constructor() {
        this.boundingBox = null;
        this.boundingSphere = null;
        this.geometry = null;
        this.children = [];
        this.tris = [];
    }

    collectCandidates(origray) {
        const candidates = [];
        const recurse = (node, ray) => {

            if (!ray.intersectsSphere(node.boundingSphere) || !ray.intersectsBox(node.boundingBox)) return;

            if (node.children.length) node.children.forEach(c => recurse(c, ray))
            else candidates.push(...node.tris)
        }
        recurse(this, origray);

        return candidates;
    }
}

class MeshBVH {
    static get Strategy() { return Strategies; }

    constructor(geo, strategy = 0) {
        if (geo.isBufferGeometry || geo.isGeometry) {
            this._root = this._buildTree(geo, strategy);
        } else {
            throw new Error('Object is not Geometry or BufferGeometry');
        }
    }

    /* Public API */
    collectCandidates(origray) {
        return this._root.collectCandidates(origray);
    }

    /* Private Functions */
    _buildTree(geo, strategy) {
        const vertexElem = geo.isBufferGeometry ? getBufferGeometryVertexElem : getGeometryVertexElem;

        // a list of every available triangle index
        const origTris =
            geo.isBufferGeometry ?
                new Array(geo.index ? (geo.index.count / 3) : (pos.length / 9)) :
                Array(geo.faces.length);

        for (let i = 0; i < origTris.length; i ++) origTris[i] = i;

        // SAH Initialization
        const sahplanes = [new Array(origTris.length * 2), new Array(origTris.length * 2), new Array(origTris.length * 2)];
        for (let i = 0; i < origTris.length; i ++) {
            const tri = origTris[i];
            const tri2 = tri * 2;

            for (let el = 0; el < 3; el ++) {
                let min = Infinity;
                let max = -Infinity;
                for (let v = 0; v < 3; v ++) {
                    const val = vertexElem(geo, tri, v, el);
                    min = Math.min(val, min);
                    max = Math.max(val, max);
                }

                sahplanes[el][tri2 + 0] = { p: min, minSide: true, tri };
                sahplanes[el][tri2 + 1] = { p: max, minSide: false, tri };
            }
        }

        const splitStrategy = (bounds, sphere, avg, tris, geometry) => {
            let axis = -1;
            let pos = 0;

            // Center
            if (strategy === Strategies.CENTER) {
                axis = getLongestEdgeIndex(bounds);
                const field = xyzFields[axis];
                pos = sphere.center[field];
            } else if (strategy === Strategies.AVERAGE) {
                axis = getLongestEdgeIndex(bounds);
                pos = avg[xyzFields[axis]];
            } else if (strategy === Strategies.SAH) {

                // Surface Area Heuristic
                // In order to make this code more terse, the x, y, and z
                // variables of various structures have been stuffed into
                // 0, 1, and 2 array indices so they can be easily computed
                // and accessed within array iteration

                // Cost values defineed for operations. We're using bounds for traversal, so
                // the cost of traversing one more layer is more than intersecting a triangle.
                const TRAVERSAL_COST = 3;
                const INTERSECTION_COST = 1;

                // Define the width, height, and depth of the bounds as a box
                const dim = [
                    bounds.max.x - bounds.min.x,
                    bounds.max.y - bounds.min.y,
                    bounds.max.z - bounds.min.z
                ];
                const sa = 2 * (dim[0] * dim[1] + dim[0] * dim[2] + dim[1] * dim[2]);

                // Get the precalculated planes based for the triangles we're
                // testing here
                const filteredLists =[[], [], []];
                tris.forEach(t => {
                    const t2 = t * 2;

                    for (let i = 0; i < 3; i ++) {
                        filteredLists[i].push(sahplanes[i][t2 + 0]);
                        filteredLists[i].push(sahplanes[i][t2 + 1]);
                    }
                });
                filteredLists.forEach(planes => planes.sort((a, b) => a.p - b.p));

                // this bounds surface area, left bound SA, left triangles, right bound SA, right triangles
                const getCost = (sa, sal, nl, sar, nr) =>
                    TRAVERSAL_COST + INTERSECTION_COST * ((sal / sa) * nl + (sar / sa) * nr);

                // the cost of _not_ splitting into smaller bounds
                const noSplitCost = INTERSECTION_COST * tris.length;

                axis = -1;
                let bestCost = noSplitCost;
                for (let i = 0; i < 3; i ++) {

                    // o1 and o2 represent the _other_ two axes in the
                    // the space. So if we're checking the x (0) dimension,
                    // then o1 and o2 would be y and z (1 and 2)
                    const o1 = (i + 1) % 3;
                    const o2 = (i + 2) % 3;

                    const bmin = bounds.min[xyzFields[i]];
                    const bmax = bounds.max[xyzFields[i]];
                    const planes = filteredLists[i];

                    // The number of left and right triangles on either side
                    // given the current split
                    let nl = 0, nr = tris.length;
                    for (let p = 0; p < planes.length; p ++) {
                        const pinfo = planes[p];

                        // As the plane moves, we have to increment or decrement the
                        // number of triangles on either side of the plane
                        nl += pinfo.minSide ? 1 : 0;
                        nr -= !pinfo.minSide ? 1 : 0;

                        // the distance from the plane to the edge of the broader bounds
                        const ldim = pinfo.p - bmin;
                        const rdim = bmax - pinfo.p;

                        // same for the other two dimensions
                        let ldimo1 = dim[o1], rdimo1 = dim[o1];
                        let ldimo2 = dim[o2], rdimo2 = dim[o2];

                        // // compute the other bounding planes for the box
                        // // if only the current triangles are considered to
                        // // be in the box
                        // // This is really slow and probably not really worth it
                        // const o1planes = sahplanes[o1];
                        // const o2planes = sahplanes[o2];
                        // let lmin = Infinity, lmax = -Infinity;
                        // let rmin = Infinity, rmax = -Infinity;
                        // planes.forEach((p, i) => {
                        //     const tri2 = p.tri * 2;
                        //     const inf1 = o1planes[tri2 + 0];
                        //     const inf2 = o1planes[tri2 + 1];
                        //     if (i <= nl) {
                        //         lmin = Math.min(inf1.p, inf2.p, lmin);
                        //         lmax = Math.max(inf1.p, inf2.p, lmax);
                        //     }
                        //     if (i >= nr) {
                        //         rmin = Math.min(inf1.p, inf2.p, rmin);
                        //         rmax = Math.max(inf1.p, inf2.p, rmax);
                        //     }
                        // })
                        // ldimo1 = Math.min(lmax - lmin, ldimo1);
                        // rdimo1 = Math.min(rmax - rmin, rdimo1);

                        // planes.forEach((p, i) => {
                        //     const tri2 = p.tri * 2;
                        //     const inf1 = o2planes[tri2 + 0];
                        //     const inf2 = o2planes[tri2 + 1];
                        //     if (i <= nl) {
                        //         lmin = Math.min(inf1.p, inf2.p, lmin);
                        //         lmax = Math.max(inf1.p, inf2.p, lmax);
                        //     }
                        //     if (i >= nr) {
                        //         rmin = Math.min(inf1.p, inf2.p, rmin);
                        //         rmax = Math.max(inf1.p, inf2.p, rmax);
                        //     }
                        // })
                        // ldimo2 = Math.min(lmax - lmin, ldimo2);
                        // rdimo2 = Math.min(rmax - rmin, rdimo2);

                        // surface areas and cost
                        const sal = 2 * (ldimo1 * ldimo2 + ldimo1 * ldim + ldimo2 * ldim);
                        const sar = 2 * (rdimo1 * rdimo2 + rdimo1 * rdim + rdimo2 * rdim);
                        const cost = getCost(sa, sal, nl, sar, nr);

                        if (cost < bestCost) {
                            axis = i;
                            pos = pinfo.p;
                            bestCost = cost;
                        }
                    }
                }
            }

            return { axis, pos };
        }

        // use a queue to run the node creation functions
        // because otherwise we run the risk of a stackoverflow
        // In the case of buffer geometry it also seems to be
        // faster than recursing
        const avgtemp = new THREE.Vector3();
        const vectemp = new THREE.Vector3();
        const queue = [];
        const createNode = (tris, bb) => {
            const node = new MeshBVHNode();
            node.geometry = geo;

            // get the bounds of the triangles
            node.boundingBox = bb;

            // Create the bounding sphere with the minium radius
            // It's possible that the bounds sphere will have a smaller
            // radius because the bounds do not encapsulate full triangles
            // on an edge
            node.boundingSphere = new THREE.Sphere();
            bb.getCenter(node.boundingSphere.center);
            node.boundingSphere.radius = vectemp.subVectors(bb.max, node.boundingSphere.center).length();
            shrinkSphereTo(tris, node.boundingSphere, geo, vertexElem);

            // early out wif we've met our capacity
            if (tris.length <= maxLeafNodes) {
                node.tris = tris;
                return node;
            }

            // Find where to split the volume
            getAverage(tris, avgtemp, geo, vertexElem);
            const split = splitStrategy(node.boundingBox, node.boundingSphere, avgtemp, tris, geo);
            if (split.axis === -1) {
                node.tris = tris;
                return node;
            }

            // Collect the nodes for either side
            const left = [], right = [];
            let sharedCount = 0;
            for (let i = 0; i < tris.length; i ++) {
                const tri = tris[i];

                let inLeft = false;
                let inRight = false;

                for (let v = 0; v < 3; v ++) {
                    const val = vertexElem(geo, tri, v, split.axis);

                    inLeft = inLeft || val <= split.pos;
                    inRight = inRight || val >= split.pos;
                }

                if (inLeft) left.push(tri);
                if (inRight) right.push(tri);
                if (inLeft && inRight) sharedCount ++;
            }

            // create the two new child nodes
            if (!left.length || !right.length || right.length === sharedCount || left.length === sharedCount) {
                node.tris = tris;
            } else {
                // create the bounds for the left child, keeping it within
                // the bounds of the parent and split plane
                const bl = new THREE.Box3().copy(bb);
                bl.max[xyzFields[split.axis]] = split.pos;
                shrinkBoundsTo(left, bl, geo, vertexElem);
                queue.push(() => node.children.push(createNode(left, bl)));

                // repeat for right
                const br = new THREE.Box3().copy(bb);
                br.min[xyzFields[split.axis]] = split.pos;
                shrinkBoundsTo(right, br, geo, vertexElem);
                queue.push(() => node.children.push(createNode(right, br)));
            }

            return node;
        }

        if (!geo.boundingBox) geo.computeBoundingBox();

        const n = createNode(origTris, (new THREE.Box3()).copy(geo.boundingBox));
        while (queue.length) queue.pop()();
        return n;
    }
}

export default MeshBVH;
<commit_msg>Only generate the sah planes when required<commit_after>import  * as THREE from '../node_modules/three/build/three.module.js'
import  Utilies from './GeometryUtilities.js'
const { getBufferGeometryVertexElem, getGeometryVertexElem, getLongestEdgeIndex, getAverage, shrinkBoundsTo, shrinkSphereTo } = Utilies;

// Settings
const maxLeafNodes = 10;
const maxMatchingTriangles = 0.5;
const Strategies = {
    get CENTER()    { return 0; },
    get AVERAGE()   { return 1; },
    get SAH()       { return 2; }
}

const xyzFields = ['x', 'y', 'z'];

// Classes
class MeshBVHNode {
    constructor() {
        this.boundingBox = null;
        this.boundingSphere = null;
        this.geometry = null;
        this.children = [];
        this.tris = [];
    }

    collectCandidates(origray) {
        const candidates = [];
        const recurse = (node, ray) => {

            if (!ray.intersectsSphere(node.boundingSphere) || !ray.intersectsBox(node.boundingBox)) return;

            if (node.children.length) node.children.forEach(c => recurse(c, ray))
            else candidates.push(...node.tris)
        }
        recurse(this, origray);

        return candidates;
    }
}

class MeshBVH {
    static get Strategy() { return Strategies; }

    constructor(geo, strategy = 0) {
        if (geo.isBufferGeometry || geo.isGeometry) {
            this._root = this._buildTree(geo, strategy);
        } else {
            throw new Error('Object is not Geometry or BufferGeometry');
        }
    }

    /* Public API */
    collectCandidates(origray) {
        return this._root.collectCandidates(origray);
    }

    /* Private Functions */
    _buildTree(geo, strategy) {
        const vertexElem = geo.isBufferGeometry ? getBufferGeometryVertexElem : getGeometryVertexElem;

        // a list of every available triangle index
        const origTris =
            geo.isBufferGeometry ?
                new Array(geo.index ? (geo.index.count / 3) : (pos.length / 9)) :
                Array(geo.faces.length);

        for (let i = 0; i < origTris.length; i ++) origTris[i] = i;

        // SAH Initialization
        let sahplanes = null;
        if (strategy === Strategies.SAH) {
            const sahplanes = [new Array(origTris.length * 2), new Array(origTris.length * 2), new Array(origTris.length * 2)];
            for (let i = 0; i < origTris.length; i ++) {
                const tri = origTris[i];
                const tri2 = tri * 2;

                for (let el = 0; el < 3; el ++) {
                    let min = Infinity;
                    let max = -Infinity;
                    for (let v = 0; v < 3; v ++) {
                        const val = vertexElem(geo, tri, v, el);
                        min = Math.min(val, min);
                        max = Math.max(val, max);
                    }

                    sahplanes[el][tri2 + 0] = { p: min, minSide: true, tri };
                    sahplanes[el][tri2 + 1] = { p: max, minSide: false, tri };
                }
            }
        }

        const splitStrategy = (bounds, sphere, avg, tris, geometry) => {
            let axis = -1;
            let pos = 0;

            // Center
            if (strategy === Strategies.CENTER) {
                axis = getLongestEdgeIndex(bounds);
                const field = xyzFields[axis];
                pos = sphere.center[field];
            } else if (strategy === Strategies.AVERAGE) {
                axis = getLongestEdgeIndex(bounds);
                pos = avg[xyzFields[axis]];
            } else if (strategy === Strategies.SAH) {

                // Surface Area Heuristic
                // In order to make this code more terse, the x, y, and z
                // variables of various structures have been stuffed into
                // 0, 1, and 2 array indices so they can be easily computed
                // and accessed within array iteration

                // Cost values defineed for operations. We're using bounds for traversal, so
                // the cost of traversing one more layer is more than intersecting a triangle.
                const TRAVERSAL_COST = 3;
                const INTERSECTION_COST = 1;

                // Define the width, height, and depth of the bounds as a box
                const dim = [
                    bounds.max.x - bounds.min.x,
                    bounds.max.y - bounds.min.y,
                    bounds.max.z - bounds.min.z
                ];
                const sa = 2 * (dim[0] * dim[1] + dim[0] * dim[2] + dim[1] * dim[2]);

                // Get the precalculated planes based for the triangles we're
                // testing here
                const filteredLists =[[], [], []];
                tris.forEach(t => {
                    const t2 = t * 2;

                    for (let i = 0; i < 3; i ++) {
                        filteredLists[i].push(sahplanes[i][t2 + 0]);
                        filteredLists[i].push(sahplanes[i][t2 + 1]);
                    }
                });
                filteredLists.forEach(planes => planes.sort((a, b) => a.p - b.p));

                // this bounds surface area, left bound SA, left triangles, right bound SA, right triangles
                const getCost = (sa, sal, nl, sar, nr) =>
                    TRAVERSAL_COST + INTERSECTION_COST * ((sal / sa) * nl + (sar / sa) * nr);

                // the cost of _not_ splitting into smaller bounds
                const noSplitCost = INTERSECTION_COST * tris.length;

                axis = -1;
                let bestCost = noSplitCost;
                for (let i = 0; i < 3; i ++) {

                    // o1 and o2 represent the _other_ two axes in the
                    // the space. So if we're checking the x (0) dimension,
                    // then o1 and o2 would be y and z (1 and 2)
                    const o1 = (i + 1) % 3;
                    const o2 = (i + 2) % 3;

                    const bmin = bounds.min[xyzFields[i]];
                    const bmax = bounds.max[xyzFields[i]];
                    const planes = filteredLists[i];

                    // The number of left and right triangles on either side
                    // given the current split
                    let nl = 0, nr = tris.length;
                    for (let p = 0; p < planes.length; p ++) {
                        const pinfo = planes[p];

                        // As the plane moves, we have to increment or decrement the
                        // number of triangles on either side of the plane
                        nl += pinfo.minSide ? 1 : 0;
                        nr -= !pinfo.minSide ? 1 : 0;

                        // the distance from the plane to the edge of the broader bounds
                        const ldim = pinfo.p - bmin;
                        const rdim = bmax - pinfo.p;

                        // same for the other two dimensions
                        let ldimo1 = dim[o1], rdimo1 = dim[o1];
                        let ldimo2 = dim[o2], rdimo2 = dim[o2];

                        // // compute the other bounding planes for the box
                        // // if only the current triangles are considered to
                        // // be in the box
                        // // This is really slow and probably not really worth it
                        // const o1planes = sahplanes[o1];
                        // const o2planes = sahplanes[o2];
                        // let lmin = Infinity, lmax = -Infinity;
                        // let rmin = Infinity, rmax = -Infinity;
                        // planes.forEach((p, i) => {
                        //     const tri2 = p.tri * 2;
                        //     const inf1 = o1planes[tri2 + 0];
                        //     const inf2 = o1planes[tri2 + 1];
                        //     if (i <= nl) {
                        //         lmin = Math.min(inf1.p, inf2.p, lmin);
                        //         lmax = Math.max(inf1.p, inf2.p, lmax);
                        //     }
                        //     if (i >= nr) {
                        //         rmin = Math.min(inf1.p, inf2.p, rmin);
                        //         rmax = Math.max(inf1.p, inf2.p, rmax);
                        //     }
                        // })
                        // ldimo1 = Math.min(lmax - lmin, ldimo1);
                        // rdimo1 = Math.min(rmax - rmin, rdimo1);

                        // planes.forEach((p, i) => {
                        //     const tri2 = p.tri * 2;
                        //     const inf1 = o2planes[tri2 + 0];
                        //     const inf2 = o2planes[tri2 + 1];
                        //     if (i <= nl) {
                        //         lmin = Math.min(inf1.p, inf2.p, lmin);
                        //         lmax = Math.max(inf1.p, inf2.p, lmax);
                        //     }
                        //     if (i >= nr) {
                        //         rmin = Math.min(inf1.p, inf2.p, rmin);
                        //         rmax = Math.max(inf1.p, inf2.p, rmax);
                        //     }
                        // })
                        // ldimo2 = Math.min(lmax - lmin, ldimo2);
                        // rdimo2 = Math.min(rmax - rmin, rdimo2);

                        // surface areas and cost
                        const sal = 2 * (ldimo1 * ldimo2 + ldimo1 * ldim + ldimo2 * ldim);
                        const sar = 2 * (rdimo1 * rdimo2 + rdimo1 * rdim + rdimo2 * rdim);
                        const cost = getCost(sa, sal, nl, sar, nr);

                        if (cost < bestCost) {
                            axis = i;
                            pos = pinfo.p;
                            bestCost = cost;
                        }
                    }
                }
            }

            return { axis, pos };
        }

        // use a queue to run the node creation functions
        // because otherwise we run the risk of a stackoverflow
        // In the case of buffer geometry it also seems to be
        // faster than recursing
        const avgtemp = new THREE.Vector3();
        const vectemp = new THREE.Vector3();
        const queue = [];
        const createNode = (tris, bb) => {
            const node = new MeshBVHNode();
            node.geometry = geo;

            // get the bounds of the triangles
            node.boundingBox = bb;

            // Create the bounding sphere with the minium radius
            // It's possible that the bounds sphere will have a smaller
            // radius because the bounds do not encapsulate full triangles
            // on an edge
            node.boundingSphere = new THREE.Sphere();
            bb.getCenter(node.boundingSphere.center);
            node.boundingSphere.radius = vectemp.subVectors(bb.max, node.boundingSphere.center).length();
            shrinkSphereTo(tris, node.boundingSphere, geo, vertexElem);

            // early out wif we've met our capacity
            if (tris.length <= maxLeafNodes) {
                node.tris = tris;
                return node;
            }

            // Find where to split the volume
            getAverage(tris, avgtemp, geo, vertexElem);
            const split = splitStrategy(node.boundingBox, node.boundingSphere, avgtemp, tris, geo);
            if (split.axis === -1) {
                node.tris = tris;
                return node;
            }

            // Collect the nodes for either side
            const left = [], right = [];
            let sharedCount = 0;
            for (let i = 0; i < tris.length; i ++) {
                const tri = tris[i];

                let inLeft = false;
                let inRight = false;

                for (let v = 0; v < 3; v ++) {
                    const val = vertexElem(geo, tri, v, split.axis);

                    inLeft = inLeft || val <= split.pos;
                    inRight = inRight || val >= split.pos;
                }

                if (inLeft) left.push(tri);
                if (inRight) right.push(tri);
                if (inLeft && inRight) sharedCount ++;
            }

            // create the two new child nodes
            if (!left.length || !right.length || right.length === sharedCount || left.length === sharedCount) {
                node.tris = tris;
            } else {
                // create the bounds for the left child, keeping it within
                // the bounds of the parent and split plane
                const bl = new THREE.Box3().copy(bb);
                bl.max[xyzFields[split.axis]] = split.pos;
                shrinkBoundsTo(left, bl, geo, vertexElem);
                queue.push(() => node.children.push(createNode(left, bl)));

                // repeat for right
                const br = new THREE.Box3().copy(bb);
                br.min[xyzFields[split.axis]] = split.pos;
                shrinkBoundsTo(right, br, geo, vertexElem);
                queue.push(() => node.children.push(createNode(right, br)));
            }

            return node;
        }

        if (!geo.boundingBox) geo.computeBoundingBox();

        const n = createNode(origTris, (new THREE.Box3()).copy(geo.boundingBox));
        while (queue.length) queue.pop()();
        return n;
    }
}

export default MeshBVH;
<|endoftext|>


<commit_before>define(['js/html/DomElement'], function(DomElement) {
    return DomElement.inherit("js/svg/SvgElement", {
        _renderAttributes: function() {
            this.callBase();
        }
    });
});<commit_msg>added SvgElement with namespaces<commit_after>define(['js/html/DomElement'], function(DomElement) {

    var SvgElement = DomElement.inherit("js/svg/SvgElement", {

        ctor: function() {
            // default namespace
            this.$namespace = SvgElement.SVG_NAMESPACE;

            this.callBase();
        },

        // render each href Element in xlink namespace
        _renderHref: function(href, oldHref) {
            this._setAttribute("href", href, SvgElement.XLINK_NAMESPACE);
        }
    });

    SvgElement.XLINK_NAMESPACE = "http://www.w3.org/1999/xlink";
    SvgElement.SVG_NAMESPACE = "http://www.w3.org/2000/svg";

    return SvgElement;
});<|endoftext|>


<commit_before>// Copyright 2018-2020, University of Colorado Boulder

/**
 * ObjectIO is the root of the IO Type hierarchy.  All IO Types extend from ObjectIO.  This type can be subtyped or
 * used directly for types that only need basic serialization static methods.
 *
 * This type purposefully does not have a `parametricTypes` static member. The presence of this field marks that it is
 * a parametric type. ObjectIO is not.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Andrew Adare (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */

import validate from '../../../axon/js/validate.js';
import ValidatorDef from '../../../axon/js/ValidatorDef.js';
import merge from '../../../phet-core/js/merge.js';
import PhetioConstants from '../PhetioConstants.js';
import tandemNamespace from '../tandemNamespace.js';

/**
 * @param {Object} phetioObject
 * @param {string} phetioID
 * @constructor
 * @abstract
 */
class ObjectIO {

  constructor( phetioObject, phetioID ) {
    assert && assert( phetioObject, 'phetioObject should be truthy' );
    assert && assert( phetioID, 'phetioID should be truthy' );

    // @public (read-only)
    this.phetioObject = phetioObject;

    // @public (read-only)
    this.phetioID = phetioID;

    // Use the validator defined on the constructor to make sure the phetioObject is valid
    validate( phetioObject, this.constructor.validator );
  }

  /**
   * Return the serialized form of the wrapped PhetioObject. Most often this looks like an object literal that holds the
   * data about the PhetioObject instance.  This can be overridden by subclasses, or types can use ObjectIO type directly
   * to use this implementation. This implementation should call `validate()` on the parameter with its `validator` field.
   * @param {*} o
   * @returns {*}
   * @public
   */
  static toStateObject( o ) {
    return o;
  }

  /**
   * For data-type serialization. Decodes the object from a state into an instance. This can be overridden by subclasses,
   * or types can use ObjectIO type directly to use this implementation.
   * @param {*} stateObject - whatever was returned from the toStateObject method
   * @returns {*} - the deserialized instance of the same type that the toStateObject was provided as a parameter
   * @public
   */
  static fromStateObject( stateObject ) {
    return stateObject;
  }

  /**
   * Map the stateObject to arguments that are passed to the `create` function in PhetioGroup.js (or other
   * `PhetioDynamicElementContainer` creation functions). Note that other non-serialized args (not dealt with here) may
   * be supplied as closure variables. This function only needs to be implemented on IO Types that are
   * phetioDynamicElement: true, such as PhetioGroup or PhetioCapsule elements.
   * @param {Object} stateObject - from a corresponding`toStateObject` method
   * @returns {Array.<*>} - the array of arguments to be passed to creation functions like PhetioGroup.create() or PhetioCapsule.getElement().
   * @public
   */
  static stateToArgsForConstructor( stateObject ) {
    return [];
  }

  /**
   * For reference-type serialization. Applies the stateObject value to the object. When setting PhET-iO state, this
   * function will be called on an instrumented instance to set the stateObject's value to it.
   * For more understanding, please read https://github.com/phetsims/phet-io/blob/master/doc/phet-io-instrumentation-guide.md#three-types-of-deserialization
   * @param {PhetioObject} o
   * @param {Object} value - result from toStateObject
   * @public
   */
  static applyState( o, value ) { }

  /**
   * Get the supertype of the passed in IO Type.
   * @example
   * ObjectIO.getSupertype( SliderIO )
   * --> NodeIO
   * ObjectIO.getSupertype( ObjectIO )
   * --> null
   *
   * @param {function(new:ObjectIO)} ioType
   * @returns {function(new:ObjectIO)|null} - null if `ioType` is ObjectIO, because that is the root of the IO hierarchy
   * @public
   */
  static getSupertype( ioType ) {
    assert && assert( ObjectIO.isIOType( ioType ), 'IO Type expected' );

    // getPrototypeOf gets the IO Type's parent type, because the prototype is for the parent.
    const supertype = Object.getPrototypeOf( ioType );
    return supertype === Object.getPrototypeOf( window.Object ) ? null : supertype;
  }

  /**
   * Returns the type hierarchy for the IO Type, from subtypiest to supertypiest
   * @param {function(new:ObjectIO)} ioType
   * @public
   */
  static getTypeHierarchy( ioType ) {
    const array = [];
    while ( ioType !== null ) {
      array.push( ioType );
      ioType = ObjectIO.getSupertype( ioType );
    }
    return array;
  }

  /**
   * Make sure the IO Type has all the required attributes.
   * @param {function(new:ObjectIO)} ioType - class to check
   * @public
   */
  static validateIOType( ioType ) {

    const typeName = ioType.typeName;
    assert && assert( !typeName.includes( '.' ), 'Dots should not appear in type names' );

    // Validate that parametric types look as expected
    if ( typeName.includes( '<' ) ) {
      assert && assert( Array.isArray( ioType.parameterTypes ), 'angle bracket notation is only used for parametric IO Types that have parameter IO Types' );
      ioType.parameterTypes.forEach( parameterType => assert && assert( ObjectIO.isIOType( parameterType ), `parameter type should be an IO Type: ${parameterType}` ) );
    }

    const splitOnParameters = typeName.split( /[<(]/ )[ 0 ];
    assert && assert( splitOnParameters.endsWith( PhetioConstants.IO_TYPE_SUFFIX ), 'IO Type name must end with IO' );
    assert && assert( !ioType.prototype.toStateObject, 'toStateObject should be a static method, not prototype one.' );
    assert && assert( !ioType.prototype.fromStateObject, 'fromStateObject should be a static method, not prototype one.' );
    assert && assert( !ioType.prototype.applyState, 'applyState should be a static method, not prototype one.' );
    assert && assert( !ioType.prototype.stateToArgsForConstructor, 'stateToArgsForConstructor should be a static method, not prototype one.' );

    const supertype = ObjectIO.getSupertype( ioType );

    assert && assert( ioType.hasOwnProperty( 'typeName' ), 'typeName is required' );

    // Prevent inheritance of static attributes, which only pertain to each level of the hierarchy, see https://github.com/phetsims/phet-io/issues/1623
    // Note that parameterTypes do inherit
    if ( !ioType.hasOwnProperty( 'events' ) ) { ioType.events = []; }
    if ( !ioType.hasOwnProperty( 'methods' ) ) { ioType.methods = {}; }
    if ( !ioType.hasOwnProperty( 'documentation' ) ) { ioType.documentation = {}; }
    if ( !ioType.hasOwnProperty( 'methodOrder' ) ) { ioType.methodOrder = []; }

    // assert that each public method adheres to the expected schema
    for ( const method in ioType.methods ) {
      const methodObject = ioType.methods[ method ];
      if ( typeof methodObject === 'object' ) {
        assert && assert( ObjectIO.isIOType( methodObject.returnType ), 'return type must be of type IO: ' + methodObject.returnType );

        assert && assert( Array.isArray( methodObject.parameterTypes ),
          'parameter types must be an array: ' + methodObject.parameterTypes );

        methodObject.parameterTypes.forEach( parameterType => {
          assert && assert( ObjectIO.isIOType( parameterType ), 'parameter type must be of type IO: ' + parameterType );
        } );

        assert && assert( typeof methodObject.implementation === 'function',
          'implementation must be of type function: ' + methodObject.implementation );

        assert && assert( typeof methodObject.documentation === 'string',
          'documentation must be of type string: ' + methodObject.documentation );

        assert && methodObject.invocableForReadOnlyElements && assert( typeof methodObject.invocableForReadOnlyElements === 'boolean',
          'invocableForReadOnlyElements must be of type boolean: ' + methodObject.invocableForReadOnlyElements );
      }
    }

    assert && assert( ioType.validator, 'validator must be provided' );
    assert && assert( ioType.documentation, 'documentation must be provided' );
    assert && ValidatorDef.validateValidator( ioType.validator );

    ioType.hasOwnProperty( 'methodOrder' ) && ioType.methodOrder.forEach( methodName => {
      assert && assert( ioType.methods[ methodName ], 'methodName not in public methods: ' + methodName );
    } );

    if ( ioType.hasOwnProperty( 'api' ) ) {
      assert && assert( ioType.api instanceof Object, 'Object expected for api' );
      assert && assert( Object.getPrototypeOf( ioType.api ) === Object.prototype, 'no extra prototype allowed on API object' );
    }

    // Make sure events are not listed again in the ioType
    const typeHierarchy = ObjectIO.getTypeHierarchy( supertype );
    assert && ioType.events && ioType.events.forEach( event => {
      const has = _.some( typeHierarchy, t => t.events.includes( event ) );
      assert( !has, 'ioType should not declare event that parent also has: ' + event );
    } );
  }
}

/**
 * Checks if type is an IO Type
 * @param {*} type
 * @public
 * @returns {boolean} - true if inherits from ObjectIO or is ObjectIO
 */
ObjectIO.isIOType = type => type === ObjectIO || type.prototype instanceof ObjectIO;

/**
 * @typeDef {Object} MethodObject
 * @property {string} documentation
 * @property {function()} implementation - the function to execute when this method is called
 * @property {function(new:ObjectIO)} returnType - the return IO Type of the method
 * @property {Array.<function(new:ObjectIO)>} parameterTypes - the parameter IO Types for the method
 * @property {boolean} [invocableForReadOnlyElements=true] - by default, all methods are invocable for all elements.
 *    However, for some read-only elements, certain methods should not be invocable. In that case, they are marked as
 *    invocableForReadOnlyElements: false.
 */

/**
 * The public methods available for this IO Type. Each method is not just a function, but a collection of metadata
 * about the method to be able to serialize parameters and return types and provide better documentation.
 * @type {Object.<string, MethodObject>}
 */
ObjectIO.methods = {};

/**
 * The list of events that can be emitted at this level (does not include events from supertypes).
 * @type {string[]}
 * @public
 */
ObjectIO.events = [];

/**
 * Documentation that appears in PhET-iO Studio, supports HTML markup.
 * @public
 */
ObjectIO.documentation = 'The root of the wrapper object hierarchy.';

/**
 * The name that this TypeIO will have in the public PhET-iO API. In general, this should only be word characters,
 * ending in "IO". Parameteric types are a special subset of TypeIOs that include their parameters in their typeName.
 * If a TypeIO's parameters are other IO Type(s), then they should be included within angle brackets, like
 * "PropertyIO<Boolean>". Some other types use a more custom format for displaying their parameter types, in this case
 * the parameter section of the type name (immediately following "IO") should begin with an open paren, "(". Thus the
 * schema for a typeName could be defined (using regex) as `[A-Z]\w*IO([(<].*){0,1}`. In most cases, parameterized
 * types should also include a `parameterTypes` field on the TypeIO.
 * @type {string}
 * @public
 */
ObjectIO.typeName = 'ObjectIO';

/**
 * IO Types can specify the order that methods appear in the documentation by putting their names in this list.
 * This list is only for the methods defined at this level in the type hierarchy.
 *
 * After the methodOrder specified, the methods follow in the order declared in the implementation (which
 * isn't necessarily stable).
 * @type {string[]}
 * @public
 */
ObjectIO.methodOrder = [];

/**
 * For parametric types, they must indicate the types of the parameters here. 0 if nonparametric.
 * @type {function(new:ObjectIO)[]}
 * @public
 */
ObjectIO.parameterTypes = [];

/**
 * A validator object to be used to validate the core types that IOTypes wrap.
 * @type {ValidatorDef}
 * @public
 */
ObjectIO.validator = {
  isValidValue: value => {

    // It could be a number, string, Object, etc.
    return value !== undefined;
  }
};

ObjectIO.validateIOType( ObjectIO );

/**
 * Function that creates an IO Type associated with a core type. Methods are forwarded to the core type.
 * @param {function} coreType, e.g., Bunny
 * @param {string} typeName, e.g., BunnyIO
 * @param {Object} [options]
 * @returns {IOType}
 * @public
 */
ObjectIO.createIOType = ( coreType, typeName, options ) => {
  assert && assert( typeName.endsWith( PhetioConstants.IO_TYPE_SUFFIX ) || typeName.includes( `${PhetioConstants.IO_TYPE_SUFFIX}<` ), 'IO Type name must end with IO' );
  options = merge( {

    // The parent IO Type, which will have standard 'class extends' inheritance, and inherit methods, events, etc.
    // and be shown as a parent type in Studio + API docs
    parentIOType: ObjectIO,

    // {string} e.g., "Animal that has a genotype (genetic blueprint) and a phenotype (appearance)."
    documentation: `IO Type for ${typeName.substring( 0, typeName.length - 2 )}`,

    // {Object} - key/value pairs with methods, see PhetioEngineIO for an example
    methods: {},

    events: [],
    parameterTypes: []
  }, options );

  class IOType extends options.parentIOType {

    /**
     * @param {PhetioObject} phetioObject
     * @returns {Object}
     * @public
     * @override
     */
    static toStateObject( phetioObject ) {
      validate( phetioObject, this.validator );
      return phetioObject.toStateObject();
    }

    // @public
    static fromStateObject( stateObject ) {
      return coreType.fromStateObject( stateObject );
    }

    /**
     * @param {Object} stateObject
     * @returns {Object[]}
     * @public
     * @override
     */
    static stateToArgsForConstructor( stateObject ) {
      return coreType.stateToArgsForConstructor( stateObject );
    }

    /**
     * Restores coreType state after instantiation.
     * @param {PhetioObject} phetioObject
     * @param {Object} stateObject
     * @public
     * @override
     */
    static applyState( phetioObject, stateObject ) {
      validate( phetioObject, this.validator );
      phetioObject.applyState( stateObject );
    }
  }

  IOType.documentation = options.documentation;
  IOType.validator = { valueType: coreType };
  IOType.typeName = typeName;
  IOType.events = options.events;
  IOType.parameterTypes = options.parameterTypes;
  IOType.methods = options.methods;
  ObjectIO.validateIOType( IOType );

  return IOType;
};

/**
 * Fills in the boilerplate for static fields of an IO Type.
 * @param {function} ioType - an IO Type
 * @param ioTypeName - classname of IOType
 * @param coreType - the corresponding Core Type
 * @param {Object} [options]
 */
ObjectIO.setIOTypeFields = ( ioType, ioTypeName, coreType, options ) => {

  options = merge( {
    documentation: null // {string} if not provided, default is defined below
  }, options );

  // Fill in static fields in the IO Type.
  ioType.typeName = ioTypeName;
  ioType.documentation = options.documentation ||
                         `IO Type for ${ioTypeName.substring( 0, ioTypeName.length - PhetioConstants.IO_TYPE_SUFFIX.length )}`;
  ioType.validator = { valueType: coreType };

  // Verify that the IO Type is valid.
  ObjectIO.validateIOType( ioType );
};

tandemNamespace.register( 'ObjectIO', ObjectIO );
export default ObjectIO;<commit_msg>Revert usage of createIOType, see https://github.com/phetsims/tandem/issues/188<commit_after>// Copyright 2018-2020, University of Colorado Boulder

/**
 * ObjectIO is the root of the IO Type hierarchy.  All IO Types extend from ObjectIO.  This type can be subtyped or
 * used directly for types that only need basic serialization static methods.
 *
 * This type purposefully does not have a `parametricTypes` static member. The presence of this field marks that it is
 * a parametric type. ObjectIO is not.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Andrew Adare (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */

import validate from '../../../axon/js/validate.js';
import ValidatorDef from '../../../axon/js/ValidatorDef.js';
import merge from '../../../phet-core/js/merge.js';
import PhetioConstants from '../PhetioConstants.js';
import tandemNamespace from '../tandemNamespace.js';

/**
 * @param {Object} phetioObject
 * @param {string} phetioID
 * @constructor
 * @abstract
 */
class ObjectIO {

  constructor( phetioObject, phetioID ) {
    assert && assert( phetioObject, 'phetioObject should be truthy' );
    assert && assert( phetioID, 'phetioID should be truthy' );

    // @public (read-only)
    this.phetioObject = phetioObject;

    // @public (read-only)
    this.phetioID = phetioID;

    // Use the validator defined on the constructor to make sure the phetioObject is valid
    validate( phetioObject, this.constructor.validator );
  }

  /**
   * Return the serialized form of the wrapped PhetioObject. Most often this looks like an object literal that holds the
   * data about the PhetioObject instance.  This can be overridden by subclasses, or types can use ObjectIO type directly
   * to use this implementation. This implementation should call `validate()` on the parameter with its `validator` field.
   * @param {*} o
   * @returns {*}
   * @public
   */
  static toStateObject( o ) {
    return o;
  }

  /**
   * For data-type serialization. Decodes the object from a state into an instance. This can be overridden by subclasses,
   * or types can use ObjectIO type directly to use this implementation.
   * @param {*} stateObject - whatever was returned from the toStateObject method
   * @returns {*} - the deserialized instance of the same type that the toStateObject was provided as a parameter
   * @public
   */
  static fromStateObject( stateObject ) {
    return stateObject;
  }

  /**
   * Map the stateObject to arguments that are passed to the `create` function in PhetioGroup.js (or other
   * `PhetioDynamicElementContainer` creation functions). Note that other non-serialized args (not dealt with here) may
   * be supplied as closure variables. This function only needs to be implemented on IO Types that are
   * phetioDynamicElement: true, such as PhetioGroup or PhetioCapsule elements.
   * @param {Object} stateObject - from a corresponding`toStateObject` method
   * @returns {Array.<*>} - the array of arguments to be passed to creation functions like PhetioGroup.create() or PhetioCapsule.getElement().
   * @public
   */
  static stateToArgsForConstructor( stateObject ) {
    return [];
  }

  /**
   * For reference-type serialization. Applies the stateObject value to the object. When setting PhET-iO state, this
   * function will be called on an instrumented instance to set the stateObject's value to it.
   * For more understanding, please read https://github.com/phetsims/phet-io/blob/master/doc/phet-io-instrumentation-guide.md#three-types-of-deserialization
   * @param {PhetioObject} o
   * @param {Object} value - result from toStateObject
   * @public
   */
  static applyState( o, value ) { }

  /**
   * Get the supertype of the passed in IO Type.
   * @example
   * ObjectIO.getSupertype( SliderIO )
   * --> NodeIO
   * ObjectIO.getSupertype( ObjectIO )
   * --> null
   *
   * @param {function(new:ObjectIO)} ioType
   * @returns {function(new:ObjectIO)|null} - null if `ioType` is ObjectIO, because that is the root of the IO hierarchy
   * @public
   */
  static getSupertype( ioType ) {
    assert && assert( ObjectIO.isIOType( ioType ), 'IO Type expected' );

    // getPrototypeOf gets the IO Type's parent type, because the prototype is for the parent.
    const supertype = Object.getPrototypeOf( ioType );
    return supertype === Object.getPrototypeOf( window.Object ) ? null : supertype;
  }

  /**
   * Returns the type hierarchy for the IO Type, from subtypiest to supertypiest
   * @param {function(new:ObjectIO)} ioType
   * @public
   */
  static getTypeHierarchy( ioType ) {
    const array = [];
    while ( ioType !== null ) {
      array.push( ioType );
      ioType = ObjectIO.getSupertype( ioType );
    }
    return array;
  }

  /**
   * Make sure the IO Type has all the required attributes.
   * @param {function(new:ObjectIO)} ioType - class to check
   * @public
   */
  static validateIOType( ioType ) {

    const typeName = ioType.typeName;
    assert && assert( !typeName.includes( '.' ), 'Dots should not appear in type names' );

    // Validate that parametric types look as expected
    if ( typeName.includes( '<' ) ) {
      assert && assert( Array.isArray( ioType.parameterTypes ), 'angle bracket notation is only used for parametric IO Types that have parameter IO Types' );
      ioType.parameterTypes.forEach( parameterType => assert && assert( ObjectIO.isIOType( parameterType ), `parameter type should be an IO Type: ${parameterType}` ) );
    }

    const splitOnParameters = typeName.split( /[<(]/ )[ 0 ];
    assert && assert( splitOnParameters.endsWith( PhetioConstants.IO_TYPE_SUFFIX ), 'IO Type name must end with IO' );
    assert && assert( !ioType.prototype.toStateObject, 'toStateObject should be a static method, not prototype one.' );
    assert && assert( !ioType.prototype.fromStateObject, 'fromStateObject should be a static method, not prototype one.' );
    assert && assert( !ioType.prototype.applyState, 'applyState should be a static method, not prototype one.' );
    assert && assert( !ioType.prototype.stateToArgsForConstructor, 'stateToArgsForConstructor should be a static method, not prototype one.' );

    const supertype = ObjectIO.getSupertype( ioType );

    assert && assert( ioType.hasOwnProperty( 'typeName' ), 'typeName is required' );

    // Prevent inheritance of static attributes, which only pertain to each level of the hierarchy, see https://github.com/phetsims/phet-io/issues/1623
    // Note that parameterTypes do inherit
    if ( !ioType.hasOwnProperty( 'events' ) ) { ioType.events = []; }
    if ( !ioType.hasOwnProperty( 'methods' ) ) { ioType.methods = {}; }
    if ( !ioType.hasOwnProperty( 'documentation' ) ) { ioType.documentation = {}; }
    if ( !ioType.hasOwnProperty( 'methodOrder' ) ) { ioType.methodOrder = []; }

    // assert that each public method adheres to the expected schema
    for ( const method in ioType.methods ) {
      const methodObject = ioType.methods[ method ];
      if ( typeof methodObject === 'object' ) {
        assert && assert( ObjectIO.isIOType( methodObject.returnType ), 'return type must be of type IO: ' + methodObject.returnType );

        assert && assert( Array.isArray( methodObject.parameterTypes ),
          'parameter types must be an array: ' + methodObject.parameterTypes );

        methodObject.parameterTypes.forEach( parameterType => {
          assert && assert( ObjectIO.isIOType( parameterType ), 'parameter type must be of type IO: ' + parameterType );
        } );

        assert && assert( typeof methodObject.implementation === 'function',
          'implementation must be of type function: ' + methodObject.implementation );

        assert && assert( typeof methodObject.documentation === 'string',
          'documentation must be of type string: ' + methodObject.documentation );

        assert && methodObject.invocableForReadOnlyElements && assert( typeof methodObject.invocableForReadOnlyElements === 'boolean',
          'invocableForReadOnlyElements must be of type boolean: ' + methodObject.invocableForReadOnlyElements );
      }
    }

    assert && assert( ioType.validator, 'validator must be provided' );
    assert && assert( ioType.documentation, 'documentation must be provided' );
    assert && ValidatorDef.validateValidator( ioType.validator );

    ioType.hasOwnProperty( 'methodOrder' ) && ioType.methodOrder.forEach( methodName => {
      assert && assert( ioType.methods[ methodName ], 'methodName not in public methods: ' + methodName );
    } );

    if ( ioType.hasOwnProperty( 'api' ) ) {
      assert && assert( ioType.api instanceof Object, 'Object expected for api' );
      assert && assert( Object.getPrototypeOf( ioType.api ) === Object.prototype, 'no extra prototype allowed on API object' );
    }

    // Make sure events are not listed again in the ioType
    const typeHierarchy = ObjectIO.getTypeHierarchy( supertype );
    assert && ioType.events && ioType.events.forEach( event => {
      const has = _.some( typeHierarchy, t => t.events.includes( event ) );
      assert( !has, 'ioType should not declare event that parent also has: ' + event );
    } );
  }
}

/**
 * Checks if type is an IO Type
 * @param {*} type
 * @public
 * @returns {boolean} - true if inherits from ObjectIO or is ObjectIO
 */
ObjectIO.isIOType = type => type === ObjectIO || type.prototype instanceof ObjectIO;

/**
 * @typeDef {Object} MethodObject
 * @property {string} documentation
 * @property {function()} implementation - the function to execute when this method is called
 * @property {function(new:ObjectIO)} returnType - the return IO Type of the method
 * @property {Array.<function(new:ObjectIO)>} parameterTypes - the parameter IO Types for the method
 * @property {boolean} [invocableForReadOnlyElements=true] - by default, all methods are invocable for all elements.
 *    However, for some read-only elements, certain methods should not be invocable. In that case, they are marked as
 *    invocableForReadOnlyElements: false.
 */

/**
 * The public methods available for this IO Type. Each method is not just a function, but a collection of metadata
 * about the method to be able to serialize parameters and return types and provide better documentation.
 * @type {Object.<string, MethodObject>}
 */
ObjectIO.methods = {};

/**
 * The list of events that can be emitted at this level (does not include events from supertypes).
 * @type {string[]}
 * @public
 */
ObjectIO.events = [];

/**
 * Documentation that appears in PhET-iO Studio, supports HTML markup.
 * @public
 */
ObjectIO.documentation = 'The root of the wrapper object hierarchy.';

/**
 * The name that this TypeIO will have in the public PhET-iO API. In general, this should only be word characters,
 * ending in "IO". Parameteric types are a special subset of TypeIOs that include their parameters in their typeName.
 * If a TypeIO's parameters are other IO Type(s), then they should be included within angle brackets, like
 * "PropertyIO<Boolean>". Some other types use a more custom format for displaying their parameter types, in this case
 * the parameter section of the type name (immediately following "IO") should begin with an open paren, "(". Thus the
 * schema for a typeName could be defined (using regex) as `[A-Z]\w*IO([(<].*){0,1}`. In most cases, parameterized
 * types should also include a `parameterTypes` field on the TypeIO.
 * @type {string}
 * @public
 */
ObjectIO.typeName = 'ObjectIO';

/**
 * IO Types can specify the order that methods appear in the documentation by putting their names in this list.
 * This list is only for the methods defined at this level in the type hierarchy.
 *
 * After the methodOrder specified, the methods follow in the order declared in the implementation (which
 * isn't necessarily stable).
 * @type {string[]}
 * @public
 */
ObjectIO.methodOrder = [];

/**
 * For parametric types, they must indicate the types of the parameters here. 0 if nonparametric.
 * @type {function(new:ObjectIO)[]}
 * @public
 */
ObjectIO.parameterTypes = [];

/**
 * A validator object to be used to validate the core types that IOTypes wrap.
 * @type {ValidatorDef}
 * @public
 */
ObjectIO.validator = {
  isValidValue: value => {

    // It could be a number, string, Object, etc.
    return value !== undefined;
  }
};

ObjectIO.validateIOType( ObjectIO );

/**
 * Fills in the boilerplate for static fields of an IO Type.
 * @param {function} ioType - an IO Type
 * @param ioTypeName - classname of IOType
 * @param coreType - the corresponding Core Type
 * @param {Object} [options]
 */
ObjectIO.setIOTypeFields = ( ioType, ioTypeName, coreType, options ) => {

  options = merge( {
    documentation: null // {string} if not provided, default is defined below
  }, options );

  // Fill in static fields in the IO Type.
  ioType.typeName = ioTypeName;
  ioType.documentation = options.documentation ||
                         `IO Type for ${ioTypeName.substring( 0, ioTypeName.length - PhetioConstants.IO_TYPE_SUFFIX.length )}`;
  ioType.validator = { valueType: coreType };

  // Verify that the IO Type is valid.
  ObjectIO.validateIOType( ioType );
};

tandemNamespace.register( 'ObjectIO', ObjectIO );
export default ObjectIO;<|endoftext|>


<commit_before>var metaMpgData    = {};
var vehicleRequest = {};
var $vehicleDefer = $.Deferred();
vehicleRequest.index = function() {
            var $carYear  = $('.carYear');
            var $carMake  = $('.carMake');
            var $carModel = $('.carModel');
            var $avgMpg   = $('.avgMpg');
            var $minMpg   = $('.minMpg');
            var $maxMpg   = $('.maxMpg');

            var date = new Date();
            var currentYear = date.getFullYear();
            for(ii=1984;ii<currentYear;ii++){$carYear.append('<option>'+ii+'</option>')};

            $carYear.on('change',function() {
                console.log('carYear event fires');
                var userSelectedYear = ""
                $(".carYear option:selected").each(function() {
                    userSelectedYear += $(this).text();
                });
                var ajaxRequest = $.ajax({
                    type: "GET",
                    url: 'http://www.fueleconomy.gov/ws/rest/vehicle/menu/make?year=' + userSelectedYear,
                    dataType: "xml",
                });
                ajaxRequest.done(function(xml) {
                    $(xml).find("value").each(function() {
                        $carMake.append('<option>' + $(this).text() + '</option>');
                    })
                    // $vehicleDefer.resolve();
                });

            });
            //MODELSd
            $carMake.change(function() {
                console.log('carMake event fires');
                var userSelectedMake = ""
                $(".carMake option:selected").each(function() {
                    userSelectedMake += $(this).text();
                });

                ajaxRequest = $.ajax({
                    type: "GET",
                    url: 'http://www.fueleconomy.gov/ws/rest/vehicle/menu/model?year=2012&make=' + userSelectedMake,
                    dataType: "xml",
                });
                ajaxRequest.done(function(xml) {
                    $(xml).find("value").each(function() {
                        $carModel.append('<option>' + $(this).text() + '</option>');
                    })
                    // $vehicleDefer.resolve();
                    //        console.log("vehicleDefer resolved");

                    userCarId();

                });
            });

            function userCarId() {
                userYear    = $(".carYear option:selected").text();
                userMake    = $(".carMake option:selected").text();
                userModel   = $(".carModel option:selected").text();;
                ajaxRequest = $.ajax({
                    type: "GET",
                    url: '//www.fueleconomy.gov/ws/rest/vehicle/menu/options?year=' + userYear + '&make=' + userMake + '&model=' + userModel,
                    dataType: "xml",
                });
                ajaxRequest.done(function(xml) {
                        var vehicleID = $(xml).find("value").first().text();
                        console.log(vehicleID);
                        ajaxRequest = $.ajax({
                            type: "GET",
                            url: '//www.fueleconomy.gov/ws/rest/ympg/shared/ympgVehicle/' + vehicleID,
                            dataType: "xml",
                        });
                        ajaxRequest.done(function(xml) {
                            $(xml).find("avgMpg").each(function() {
                              metaMpgData.avgmpg = $(this).text();
                              $avgMpg.append($(this).text());
                            });
                            $(xml).find("maxMpg").each(function() {
                              metaMpgData.maxmpg = $(this).text();
                              $maxMpg.append($(this).text());
                            });
                            $(xml).find("minMpg").each(function() {
                              metaMpgData.minmpg = $(this).text();
                              $minMpg.append($(this).text());
                              console.log(metaMpgData);
                            });
                        $vehicleDefer.resolve();
                        console.log("vehicle defer resolved");

                        })
                    });
                  } //userCarId
                }; //vehicleRequest closed
vehicleRequest.index();
<commit_msg>Adding event listener for model change so that user can select the correct model. Tested and ran with 1996 Audi A6 also<commit_after>var metaMpgData    = {};
var vehicleRequest = {};
var $vehicleDefer = $.Deferred();
vehicleRequest.index = function() {
            var $carYear  = $('.carYear');
            var $carMake  = $('.carMake');
            var $carModel = $('.carModel');
            var $avgMpg   = $('.avgMpg');
            var $minMpg   = $('.minMpg');
            var $maxMpg   = $('.maxMpg');

            var date = new Date();
            var currentYear = date.getFullYear();
            for(ii=1984;ii<currentYear;ii++){$carYear.append('<option>'+ii+'</option>')};

            $carYear.on('change',function() {
                console.log('carYear event fires');
                var userSelectedYear = ""
                $(".carYear option:selected").each(function() {
                    userSelectedYear += $(this).text();
                });
                var ajaxRequest = $.ajax({
                    type: "GET",
                    url: 'http://www.fueleconomy.gov/ws/rest/vehicle/menu/make?year=' + userSelectedYear,
                    dataType: "xml",
                });
                ajaxRequest.done(function(xml) {
                    $(xml).find("value").each(function() {
                        $carMake.append('<option>' + $(this).text() + '</option>');
                    })
                    // $vehicleDefer.resolve();
                });
            });
            //MODELSd
            $carMake.change(function() {
                console.log('carMake event fires');
                var userSelectedMake = ""
                $(".carMake option:selected").each(function() {
                    userSelectedMake += $(this).text();
                });

                ajaxRequest = $.ajax({
                    type: "GET",
                    url: 'http://www.fueleconomy.gov/ws/rest/vehicle/menu/model?year=2012&make=' + userSelectedMake,
                    dataType: "xml",
                });
                ajaxRequest.done(function(xml) {
                    $(xml).find("value").each(function() {
                        $carModel.append('<option>' + $(this).text() + '</option>');
                    })
                    // $vehicleDefer.resolve();
                    //        console.log("vehicleDefer resolved");

                });
            });

            $carModel.on('change', function(){
              userCarId();
            });

            function userCarId() {
                userYear    = $(".carYear option:selected").text();
                userMake    = $(".carMake option:selected").text();
                userModel   = $(".carModel option:selected").text();
                ajaxRequest = $.ajax({
                    type: "GET",
                    url: '//www.fueleconomy.gov/ws/rest/vehicle/menu/options?year=' + userYear + '&make=' + userMake + '&model=' + userModel,
                    dataType: "xml",
                });
                ajaxRequest.done(function(xml) {
                        var vehicleID = $(xml).find("value").first().text();
                        console.log(vehicleID);
                        ajaxRequest = $.ajax({
                            type: "GET",
                            url: '//www.fueleconomy.gov/ws/rest/ympg/shared/ympgVehicle/' + vehicleID,
                            dataType: "xml",
                        });
                        ajaxRequest.done(function(xml) {
                            $(xml).find("avgMpg").each(function() {
                              metaMpgData.avgmpg = $(this).text();
                              $avgMpg.append($(this).text());
                            });
                            $(xml).find("maxMpg").each(function() {
                              metaMpgData.maxmpg = $(this).text();
                              $maxMpg.append($(this).text());
                            });
                            $(xml).find("minMpg").each(function() {
                              metaMpgData.minmpg = $(this).text();
                              $minMpg.append($(this).text());
                              console.log(metaMpgData);
                            });
                        $vehicleDefer.resolve();
                        console.log("vehicle defer resolved");

                        })
                    });
                  } //userCarId
                }; //vehicleRequest closed
vehicleRequest.index();
<|endoftext|>


<commit_before>window.FileView = Backbone.View.extend({

    initialize: function () {
        _.bindAll(this);
        this.render();

    },

    events: {
        "click .loadBtn"    : "loadHandler",
        "click .saveBtn"    : "saveHandler"
    },

    render: function () {
        $(this.el).html(this.template());
        return this;
    },

    fileSystemSuccess: function(fileSystem) {
        this.fileSystem = fileSystem;
        fileSystem.root.getDirectory("Music", {create: false, exclusive: false}, getDirSuccess, fail);
    },

    fileSystemError: function() {

    },

    loadHandler: function() {
        var fileName = $('#fileName').val();
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
            function(fileSystem) {
                fileSystem.root.getFile(fileName, {create: false, exclusive: false},
                    function(file) {
                        var reader = new FileReader();
                        reader.onload = function(event) {
                            $('#fileContent').val(event.target.result);
                        };
                        reader.onerror = function(event) {
                            showAlert('Error loading file');
                        };
                        reader.readAsText(file);
                    },
                    function() {
                        navigator.notification.alert(
                            'The file does not exist. Press Save As to create it.',  // message
                            null,
                            'File Open',            // title
                            'OK'                  // button label
                        );
                    });
            },
            function(error) {
                showAlert('FileSystem Error');
            });
        return false;
    },

    saveHandler: function() {
        var fileName = $('#fileName').val();
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
            function(fileSystem) {
                fileSystem.root.getFile(fileName, {create: true, exclusive: false},
                    function(file) {
                        file.createWriter(
                            function(writer) {
                                writer.onwrite = function(event) {
                                    navigator.notification.alert(
                                        'The file was saved successfully',  // message
                                        null,
                                        'File Save',            // title
                                        'OK'                  // button label
                                    );
                                };
                                writer.onerror = function(event) {
                                    navigator.notification.alert(
                                        'An error occurred while saving the file',  // message
                                        null,
                                        'File Save',            // title
                                        'OK'                  // button label
                                    );
                                };
                                writer.write($('#fileContent').val());
                            },
                            function() {
                                showAlert('An error has occurred', 'createWriter');
                            }
                        );

                    },
                    function() {
                        showAlert('An error has occurred', 'getFile')
                    });
            },
            function(error) {
                showAlert('An error has occurred', 'File');
            });
        return false;
    }

});<commit_msg>third commit<commit_after>window.FileView = Backbone.View.extend({

    initialize: function () {
        _.bindAll(this);
        this.render();

    },

    events: {
        "click .loadBtn"    : "loadHandler",
        "click .saveBtn"    : "saveHandler"
    },

    render: function () {
        $(this.el).html(this.template());
        return this;
    },

    fileSystemSuccess: function(fileSystem) {
        this.fileSystem = fileSystem;
        fileSystem.root.getDirectory("Music", {create: false, exclusive: false}, getDirSuccess, fail);
    },

    fileSystemError: function() {

    },

    loadHandler: function() {
        var fileName = $('#fileName').val();
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
            function(fileSystem) {
                fileSystem.root.getFile(fileName, {create: false, exclusive: false},
                    function(file) {
                        var reader = new FileReader();
                        //reader.onload = function(event) {
						reader.onloadend = function(event) {
							console.log("Read as data URL");
							console.log(event.target.result);
                            $('#fileContent').val(event.target.result);
                        };
                        reader.onerror = function(event) {
                            showAlert('Error loading file');
                        };
						reader.readAsText(file);

						reader.onloadend = function(evt) {
							console.log("Read as data URL");
							console.log(event.target.result);
							$('#fileContent').val(event.target.result);
						};
                        reader.readAsDataURL(file);
                    },
                    function() {
                        navigator.notification.alert(
                            'The file does not exist. Press Save As to create it.',  // message
                            null,
                            'File Open',            // title
                            'OK'                  // button label
                        );
                    });
            },
            function(error) {
                showAlert('FileSystem Error');
            });
        return false;
    },

    saveHandler: function() {
        var fileName = $('#fileName').val();
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
            function(fileSystem) {
                fileSystem.root.getFile(fileName, {create: true, exclusive: false},
                    function(file) {
                        file.createWriter(
                            function(writer) {
                                writer.onwrite = function(event) {
                                    navigator.notification.alert(
                                        'The file was saved successfully',  // message
                                        null,
                                        'File Save',            // title
                                        'OK'                  // button label
                                    );
                                };
                                writer.onerror = function(event) {
                                    navigator.notification.alert(
                                        'An error occurred while saving the file',  // message
                                        null,
                                        'File Save',            // title
                                        'OK'                  // button label
                                    );
                                };
                                writer.write($('#fileContent').val());
                            },
                            function() {
                                showAlert('An error has occurred', 'createWriter');
                            }
                        );

                    },
                    function() {
                        showAlert('An error has occurred', 'getFile')
                    });
            },
            function(error) {
                showAlert('An error has occurred', 'File');
            });
        return false;
    }

});
<|endoftext|>


<commit_before>/**
 * @module Promise
 * @private
 * @mixin
 * @description Exports Promise class.
 */

import { isFunction, defineProperties, supportSymbol } from './helpers';

const secret = {};

const iterator = supportSymbol && global.Symbol.iterator ? global.Symbol.iterator : Math.random().toString(36);

/**
 * @typedef {Promise} AbortablePromise
 * @public
 * @description Promise that could be aborted.
 */

/**
 * @member {Function} AbortablePromise#abort
 * @public
 * @returns {AbortablePromise} Returns this.
 * @description Abort method.
 */

export class Promise {
  /**
   * @class Promise
   * @public
   * @param {Function} resolver - Promise resolver.
   * @returns {Promise} Instance of Promise.
   * @description
   * [Promise API]{@link https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise}.
   *
   * @example
   * new Promise((resolve) => {
   *   resolve(1);
   * }).then((value) => {
   *   console.log(value); // 1
   * });
   */
	constructor(resolver) {
    if (!isFunction(resolver)) {
      throw new TypeError(`Promise resolver ${ Object.prototype.toString.call(resolver) } is not a function`);
    }

    let hiddenStatus;
    let hiddenValue;

		const	onResolve = [];
		const onReject = [];
    const realPromise = this;
    const hiddenPromise = {
      handled: false,
      get status() {
        return hiddenStatus;
      },
      set status(value) {
        hiddenStatus = value;
        realPromise.status = value;
      },
      get value() {
        return hiddenValue;
      },
      set value(val) {
        hiddenValue = val;
        realPromise.value = val;
      }
    };

    hiddenPromise.status = 'pending';
    hiddenPromise.value = undefined;

    /**
     * @typedef {Object} hiddenPromise
     * @private
     */

    /**
     * @member {(Boolean|Object)} hiddenPromise#handled
     * @private
     * @description If the promise is handled or not.
     */

    /**
     * @member {('pending'|'resolved'|'rejected')} hiddenPromise#status
     * @private
     * @description Status of the promise.
     */

    /**
     * @member {*} hiddenPromise#value
     * @private
     * @description Value of the promise.
     */

    /**
     * @member {Function} hiddenPromise#handle
     * @private
     * @param {('reject'|'resolve')} event - Type of the event to handle.
     * @param {Function} handler - Handler itself.
     * @param {Function} resolve - Resolve function.
     * @param {Function} reject - Reject function.
     * @param {Object} secret - Secret.
     * @description Private method for handling promises.
     */

    /**
     * @member {hiddenPromise} Promise#$$
     * @protected
     */
		defineProperties(this.$$, {
			'get/set handled': {
				get() {
					return hiddenPromise.handled;
				},
				set(key) {
					if (key === secret) {
						hiddenPromise.handled = true;
					}
				}
			},
			handle(status, f, resolve, reject, key) {
				if (key === secret) {
					const proxy = isFunction(f) ? (value) => {
						try {
							resolve(f(value));
						} catch (err) {
							return reject(err);
						}
					} : null;

					if (status === 'resolve') {
						onResolve.push(proxy || ((value) => resolve(value)));
					} else if (status === 'reject') {
						onReject.push(proxy || ((err) => reject(err)));
					}
				}
			},
			'get status'() {
        return hiddenPromise.status;
      },
			'get value'() {
				return hiddenPromise.value;
			}
		});

		try {
      resolver(resolve, reject);
		} catch (err) {
			reject(err);
		}

		function reject(err) {
			if (hiddenPromise.status === 'pending') {
				hiddenPromise.status = 'rejected';
				hiddenPromise.value = err;

				for (let i = 0, length = onReject.length; i < length; i++) {
					hiddenPromise.handled = true;

					onReject[i](err);
				}

				setTimeout(() => {
					if (!hiddenPromise.handled) {
						console.error('%s %o', 'Uncaught (in promise)', err);
					}
				}, 1);
			}
		}
		function resolve(value) {
			if (hiddenPromise.status === 'pending') {
				if (value && isFunction(value.then)) {
					return value.then((value) => {
						resolve(value);
					}, (err) => {
						reject(err);
					});
				}

				hiddenPromise.status = 'resolved';
				hiddenPromise.value = value;

				for (let i = 0, length = onResolve.length; i < length; i++) {
					hiddenPromise.handled = true;

					onResolve[i](value);
				}
			}
		}
	}

  /**
   * @method Promise.all
   * @public
   * @param {(Array|Arguments|Iterable)} iterable - Iterable object to get promises iterating over it.
   * @returns {Promise} New instance of Promise.
   * @description Returns Promise that is resolved with an array of resolved values of all promises or
   * Promise that is rejected with first error.
   *
   * @example
   * Promise.all([
   *   new Promise((resolve) => resolve(1)),
   *   Promise.resolve(2)
   * ]).then((values) => {
   *   console.log(values); // [1, 2]
   * });
   *
   * Promise.all([
   *   new Promise((resolve) => resolve(1)),
   *   Promise.reject(new Error('error'))
   * ]).catch((error) => {
   *   console.log(error); // Error: error
   * });
   */
	static all(iterable) {
    const array = [];

    let toResolve = 0;

    if (iterable[iterator]) {
      iterable = iterable[iterator]();

      return new Promise((resolve, reject) => {
        let next;
        let i = 0;

        while (!(next = iterable.next()).done) {
          const promise = Promise.resolve(next.value);

          toResolve++;

          ((i) => {
            promise.then((value) => {
              toResolve--;
              array[i] = value;

              setTimeout(() => {
                if (next.done && !toResolve) {
                  resolve(array);
                }
              }, 1);
            }, reject);
          })(i++);
        }

        if (!i) {
          return Promise.resolve([]);
        }
      });
    }

    const length = iterable.length;

    if (!length) {
			return Promise.resolve([]);
		}

    toResolve = length;

		return new Promise((resolve, reject) => {
      for (let i = 0; i < length; i++) {
        const promise = Promise.resolve(array[i]);

        promise.then((value) => {
          toResolve--;
          array[i] = value;

          if (!toResolve) {
            resolve(array);
          }
        }, reject);
      }
		});
	}

  /**
   * @method Promise.race
   * @public
   * @param {(Array|Arguments|Iterable)} iterable - Iterable object to get promises iterating over it.
   * @returns {Promise} New instance of Promise.
   * @description Returns Promise with first resolved value or first rejected error.
   *
   * @example
   * Promise.race([
   *   new Promise((resolve) => setTimeout(() => {
   *     resolve(1);
   *   }, 100)),
   *   new Promise((resolve) => setTimeout(() => {
   *     resolve(2);
   *   }, 50))
   * ]).then((value) => {
   *   console.log(value); // 2
   * });
   *
   * Promise.all([
   *   new Promise((reject) => setTimeout(() => {
   *     resolve(1);
   *   }, 100)),
   *   new Promise((resolve, reject) => setTimeout(() => {
   *     reject(new Error('error'));
   *   }, 50))
   * ]).catch((error) => {
   *   console.log(error); // Error: error
   * });
   */
	static race(iterable) {
    const array = [];

    if (iterable[iterator]) {
      iterable = iterable[iterator]();

      return new Promise((resolve, reject) => {
        let next;

        while (!(next = iterable.next()).done) {
          next.value.then(resolve, reject);
        }
      });
    }

		return new Promise((resolve, reject) => {
			for (let i = 0, length = array.length; i < length; i++) {
				array[i].then(resolve, reject);
			}
		});
	}

  /**
   * @method Promise.reject
   * @public
   * @param {*} [value] - Value to reject.
   * @returns {Promise} New instance of Promise.
   * @description Returns instantly rejected Promise with given value.
   *
   * @example
   * Promise.reject(new Error('error')).catch((err) => {
   *   console.log(error); // Error: error
   * });
   */
	static reject(value) {
		return new Promise((resolve, reject) => {
			reject(value);
		});
	}

  /**
   * @method Promise.resolve
   * @public
   * @param {(*|Promise)} [value] - Value or Promise to resolve.
   * @returns {(Promise|Thenable)} - If the argument is Thenable then returns itself and if not returns Promise.
   * @description Resolves not-Thenable objects and returns Thenable itself if the argument is Thenable.
   *
   * @example
   * Promise.resolve(1).then((value) => {
   *   console.log(value); // 1
   * });
   *
   * Promise.resolve(Promise.resolve(1)).then((value) => {
   *   console.log(value); // 1
   * });
   */
	static resolve(value) {
    if (value && isFunction(value.then)) {
			return value;
		}

		return new Promise((resolve) => {
			resolve(value);
		});
	}

  /**
   * @method Promise#catch
   * @public
   * @param {Function} onReject - Function that is called when the promise is rejected.
   * @returns {Promise} New instance of Promise.
   * @description Method to catch errors inside previous promises.
   *
   * @example
   * Promise.reject(new Error('error')).catch((err) => {
   *   console.log(err); // Error: error
   * });
   */
	catch(onReject) {
		return resolveOrReject(this.$$, null, onReject);
	}

  /**
   * @method Promise#then
   * @public
   * @param {?Function} onResolve - Function that is called when the promise is resolved.
   * @param {?Function} [onReject] - Function that is called when the promise is rejected.
   * @returns {Promise} New instance of Promise.
   * @description Method for chaining promises.
   *
   * @example
   * Promise.resolve(1).then((value) => {
   *   console.log(value); // 1
   * });
   */
	then(onResolve, onReject) {
		return resolveOrReject(this.$$, onResolve, onReject);
	}
}

if (supportSymbol) {
  Promise.prototype[Symbol.toStringTag] = 'Promise';
}

/**
 * @function resolveOrReject
 * @private
 * @param {hiddenPromise} promise
 * @param onResolve
 * @param onReject
 * @returns {Promise} New instance of Promise.
 */
function resolveOrReject(promise, onResolve, onReject) {
	if (promise.status === 'pending') {
		return new Promise((resolve, reject) => {
			promise.handle('reject', onReject, resolve, reject, secret);
			promise.handle('resolve', onResolve, resolve, reject, secret);
		});
	}

	promise.handled = secret;

	const { value } = promise;

	let method;
	let handler;

	if (promise.status === 'resolved') {
		method = 'resolve';
		handler = onResolve;
	} else {
		method = 'reject';
		handler = onReject;
	}

	if (!isFunction(handler)) {
		return Promise[method](value);
	}

	try {
		return Promise.resolve(handler(value));
	} catch (err) {
		return Promise.reject(err);
	}
}

/**
 * @function promise
 * @public
 * @param resolver - Passed to the Promise#constructor.
 * @returns {Promise} New instance of Promise.
 * @description Simple wrap of [new Switcher(...)]{@link Promise}.
 */
export function promise(resolver) {
  return new Promise(resolver);
}

/**
 * @function all
 * @public
 * @description Synonym of {@link Promise.all}.
 */
export const all = Promise.all;

/**
 * @function race
 * @public
 * @description Synonym of {@link Promise.race}.
 */
export const race = Promise.race;

/**
 * @function reject
 * @public
 * @description Synonym of {@link Promise.reject}.
 */
export const reject = Promise.reject;

/**
 * @function resolve
 * @public
 * @description Synonym of {@link Promise.resolve}.
 */
export const resolve = Promise.resolve;

export default Promise;
<commit_msg>Promise: improve jsdoc.<commit_after>/**
 * @module Promise
 * @private
 * @mixin
 * @description Exports Promise class.
 */

import { isFunction, defineProperties, supportSymbol } from './helpers';

const secret = {};

const iterator = supportSymbol && global.Symbol.iterator ? global.Symbol.iterator : Math.random().toString(36);

/**
 * @typedef {Promise} AbortablePromise
 * @public
 * @description Promise that could be aborted.
 */

/**
 * @member {Function} AbortablePromise#abort
 * @public
 * @returns {AbortablePromise} Returns this.
 * @description Abort method.
 */

/**
 * @callback PromiseThen
 * @public
 * @param {*} value - Promise value.
 */

/**
 * @callback PromiseCatch
 * @public
 * @param {(Error|*)} - Promise error.
 */

/**
 * @callback PromiseResolve
 * @public
 * @param {*} - Function for resolving a promise.
 */

/**
 * @callback PromiseReject
 * @public
 * @param {(Error|*)} - Function for rejecting a promise.
 */

/**
 * @callback PromiseResolver
 * @public
 * @param {PromiseResolve} - Function for resolving the promise.
 * @param {PromiseReject} - Function for rejecting the promise.
 */

export class Promise {
  /**
   * @class Promise
   * @public
   * @param {PromiseResolver} resolver - Promise resolver.
   * @returns {Promise} Instance of Promise.
   * @description
   * [Promise API]{@link https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Promise}.
   *
   * @example
   * new Promise((resolve) => {
   *   resolve(1);
   * }).then((value) => {
   *   console.log(value); // 1
   * });
   */
	constructor(resolver) {
    if (!isFunction(resolver)) {
      throw new TypeError(`Promise resolver ${ Object.prototype.toString.call(resolver) } is not a function`);
    }

    let hiddenStatus;
    let hiddenValue;

		const	onResolve = [];
		const onReject = [];
    const realPromise = this;
    const hiddenPromise = {
      handled: false,
      get status() {
        return hiddenStatus;
      },
      set status(value) {
        hiddenStatus = value;
        realPromise.status = value;
      },
      get value() {
        return hiddenValue;
      },
      set value(val) {
        hiddenValue = val;
        realPromise.value = val;
      }
    };

    hiddenPromise.status = 'pending';
    hiddenPromise.value = undefined;

    /**
     * @typedef {Object} hiddenPromise
     * @private
     */

    /**
     * @member {(Boolean|Object)} hiddenPromise#handled
     * @private
     * @description If the promise is handled or not.
     */

    /**
     * @member {('pending'|'resolved'|'rejected')} hiddenPromise#status
     * @private
     * @description Status of the promise.
     */

    /**
     * @member {*} hiddenPromise#value
     * @private
     * @description Value of the promise.
     */

    /**
     * @member {Function} hiddenPromise#handle
     * @private
     * @param {('reject'|'resolve')} event - Type of the event to handle.
     * @param {Function} handler - Handler itself.
     * @param {Function} resolve - Resolve function.
     * @param {Function} reject - Reject function.
     * @param {Object} secret - Secret.
     * @description Private method for handling promises.
     */

    /**
     * @member {hiddenPromise} Promise#$$
     * @protected
     */
    this.$$ = {};
		defineProperties(this.$$, {
			'get/set handled': {
				get() {
					return hiddenPromise.handled;
				},
				set(key) {
					if (key === secret) {
						hiddenPromise.handled = true;
					}
				}
			},
			handle(status, f, resolve, reject, key) {
				if (key === secret) {
					const proxy = isFunction(f) ? (value) => {
						try {
							resolve(f(value));
						} catch (err) {
							return reject(err);
						}
					} : null;

					if (status === 'resolve') {
						onResolve.push(proxy || ((value) => resolve(value)));
					} else if (status === 'reject') {
						onReject.push(proxy || ((err) => reject(err)));
					}
				}
			},
			'get status'() {
        return hiddenPromise.status;
      },
			'get value'() {
				return hiddenPromise.value;
			}
		});

		try {
      resolver(resolve, reject);
		} catch (err) {
			reject(err);
		}

		function reject(err) {
			if (hiddenPromise.status === 'pending') {
				hiddenPromise.status = 'rejected';
				hiddenPromise.value = err;

				for (let i = 0, length = onReject.length; i < length; i++) {
					hiddenPromise.handled = true;

					onReject[i](err);
				}

				setTimeout(() => {
					if (!hiddenPromise.handled) {
						console.error('%s %o', 'Uncaught (in promise)', err);
					}
				}, 1);
			}
		}
		function resolve(value) {
			if (hiddenPromise.status === 'pending') {
				if (value && isFunction(value.then)) {
					return value.then((value) => {
						resolve(value);
					}, (err) => {
						reject(err);
					});
				}

				hiddenPromise.status = 'resolved';
				hiddenPromise.value = value;

				for (let i = 0, length = onResolve.length; i < length; i++) {
					hiddenPromise.handled = true;

					onResolve[i](value);
				}
			}
		}
	}

  /**
   * @method Promise.all
   * @public
   * @param {(Array|Arguments|Iterable)} iterable - Iterable object to get promises iterating over it.
   * @returns {Promise} New instance of Promise.
   * @description Returns Promise that is resolved with an array of resolved values of all promises or
   * Promise that is rejected with first error.
   *
   * @example
   * Promise.all([
   *   new Promise((resolve) => resolve(1)),
   *   Promise.resolve(2)
   * ]).then((values) => {
   *   console.log(values); // [1, 2]
   * });
   *
   * Promise.all([
   *   new Promise((resolve) => resolve(1)),
   *   Promise.reject(new Error('error'))
   * ]).catch((error) => {
   *   console.log(error); // Error: error
   * });
   */
	static all(iterable) {
    const array = [];

    let toResolve = 0;

    if (iterable[iterator]) {
      iterable = iterable[iterator]();

      return new Promise((resolve, reject) => {
        let next;
        let i = 0;

        while (!(next = iterable.next()).done) {
          const promise = Promise.resolve(next.value);

          toResolve++;

          ((i) => {
            promise.then((value) => {
              toResolve--;
              array[i] = value;

              setTimeout(() => {
                if (next.done && !toResolve) {
                  resolve(array);
                }
              }, 1);
            }, reject);
          })(i++);
        }

        if (!i) {
          return Promise.resolve([]);
        }
      });
    }

    const length = iterable.length;

    if (!length) {
			return Promise.resolve([]);
		}

    toResolve = length;

		return new Promise((resolve, reject) => {
      for (let i = 0; i < length; i++) {
        const promise = Promise.resolve(array[i]);

        promise.then((value) => {
          toResolve--;
          array[i] = value;

          if (!toResolve) {
            resolve(array);
          }
        }, reject);
      }
		});
	}

  /**
   * @method Promise.race
   * @public
   * @param {(Array|Arguments|Iterable)} iterable - Iterable object to get promises iterating over it.
   * @returns {Promise} New instance of Promise.
   * @description Returns Promise with first resolved value or first rejected error.
   *
   * @example
   * Promise.race([
   *   new Promise((resolve) => setTimeout(() => {
   *     resolve(1);
   *   }, 100)),
   *   new Promise((resolve) => setTimeout(() => {
   *     resolve(2);
   *   }, 50))
   * ]).then((value) => {
   *   console.log(value); // 2
   * });
   *
   * Promise.all([
   *   new Promise((reject) => setTimeout(() => {
   *     resolve(1);
   *   }, 100)),
   *   new Promise((resolve, reject) => setTimeout(() => {
   *     reject(new Error('error'));
   *   }, 50))
   * ]).catch((error) => {
   *   console.log(error); // Error: error
   * });
   */
	static race(iterable) {
    const array = [];

    if (iterable[iterator]) {
      iterable = iterable[iterator]();

      return new Promise((resolve, reject) => {
        let next;

        while (!(next = iterable.next()).done) {
          next.value.then(resolve, reject);
        }
      });
    }

		return new Promise((resolve, reject) => {
			for (let i = 0, length = array.length; i < length; i++) {
				array[i].then(resolve, reject);
			}
		});
	}

  /**
   * @method Promise.reject
   * @public
   * @param {*} [value] - Value to reject.
   * @returns {Promise} New instance of Promise.
   * @description Returns instantly rejected Promise with given value.
   *
   * @example
   * Promise.reject(new Error('error')).catch((err) => {
   *   console.log(error); // Error: error
   * });
   */
	static reject(value) {
		return new Promise((resolve, reject) => {
			reject(value);
		});
	}

  /**
   * @method Promise.resolve
   * @public
   * @param {(*|Promise)} [value] - Value or Promise to resolve.
   * @returns {(Promise|Thenable)} - If the argument is Thenable then returns itself and if not returns Promise.
   * @description Resolves not-Thenable objects and returns Thenable itself if the argument is Thenable.
   *
   * @example
   * Promise.resolve(1).then((value) => {
   *   console.log(value); // 1
   * });
   *
   * Promise.resolve(Promise.resolve(1)).then((value) => {
   *   console.log(value); // 1
   * });
   */
	static resolve(value) {
    if (value && isFunction(value.then)) {
			return value;
		}

		return new Promise((resolve) => {
			resolve(value);
		});
	}

  /**
   * @method Promise#catch
   * @public
   * @param {PromiseCatch} [onRejected] - Function that is called when the promise is rejected.
   * @returns {Promise} New instance of Promise.
   * @description Method to catch errors inside previous promises.
   *
   * @example
   * Promise.reject(new Error('error')).catch((err) => {
   *   console.log(err); // Error: error
   * });
   */
	catch(onRejected) {
		return resolveOrReject(this.$$, null, onRejected);
	}

  /**
   * @method Promise#then
   * @public
   * @param {?PromiseThen} [onResolved] - Function that is called when the promise is resolved.
   * @param {?PromiseCatch} [onRejected] - Function that is called when the promise is rejected.
   * @returns {Promise} New instance of Promise.
   * @description Method for chaining promises.
   *
   * @example
   * Promise.resolve(1).then((value) => {
   *   console.log(value); // 1
   * });
   */
	then(onResolved, onRejected) {
		return resolveOrReject(this.$$, onResolved, onRejected);
	}
}

if (supportSymbol) {
  Promise.prototype[Symbol.toStringTag] = 'Promise';
}

/**
 * @function resolveOrReject
 * @private
 * @param {hiddenPromise} promise
 * @param {?PromiseThen} onResolved - Resolve function.
 * @param {?PromiseCatch} onRejected - Reject function.
 * @returns {Promise} New instance of Promise.
 */
function resolveOrReject(promise, onResolved, onRejected) {
	if (promise.status === 'pending') {
		return new Promise((resolve, reject) => {
			promise.handle('reject', onRejected, resolve, reject, secret);
			promise.handle('resolve', onResolved, resolve, reject, secret);
		});
	}

	promise.handled = secret;

	const { value } = promise;

	let method;
	let handler;

	if (promise.status === 'resolved') {
		method = 'resolve';
		handler = onResolved;
	} else {
		method = 'reject';
		handler = onRejected;
	}

	if (!isFunction(handler)) {
		return Promise[method](value);
	}

	try {
		return Promise.resolve(handler(value));
	} catch (err) {
		return Promise.reject(err);
	}
}

/**
 * @function promise
 * @public
 * @param {PromiseResolver} resolver - Passed to the Promise#constructor.
 * @returns {Promise} New instance of Promise.
 * @description Simple wrap of [new Switcher(...)]{@link Promise}.
 */
export function promise(resolver) {
  return new Promise(resolver);
}

/**
 * @function all
 * @public
 * @description Synonym of {@link Promise.all}.
 */
export const all = Promise.all;

/**
 * @function race
 * @public
 * @description Synonym of {@link Promise.race}.
 */
export const race = Promise.race;

/**
 * @function reject
 * @public
 * @description Synonym of {@link Promise.reject}.
 */
export const reject = Promise.reject;

/**
 * @function resolve
 * @public
 * @description Synonym of {@link Promise.resolve}.
 */
export const resolve = Promise.resolve;

export default Promise;
<|endoftext|>