<commit_before>//*************************************************************
// and here are the accessors for properties and elements
//*************************************************************

var Accessor = {};

Accessor.generalFields = { //Übersetungstafel der Feldnamen
    color: "color",
    colorhsb: "",
    size: "size",
    alpha: "alpha",
    isshowing: "isshowing",
    visible: "visible",
    name: "name",
    caption: "caption",
    trace: "",
    tracelength: "",
    selected: ""
};

Accessor.getGeoField = function(geoname, field) {
    if (typeof csgeo.csnames[geoname] !== 'undefined') {
        return Accessor.getField(csgeo.csnames[geoname], field);
    }
    return nada;
};

Accessor.setGeoField = function(geoname, field, value) {
    if (typeof csgeo.csnames[geoname] !== 'undefined') {
        return Accessor.setField(csgeo.csnames[geoname], field, value);
    }
    return nada;
};

Accessor.getField = function(geo, field) {
    var erg;
    if (geo.kind === "P") {
        if (field === "xy") {
            erg = List.dehom(geo.homog);
            return General.withUsage(erg, "Point");
        }

        if (field === "homog") {
            return General.withUsage(geo.homog, "Point");
        }

        if (field === "x") {
            return CSNumber.div(geo.homog.value[0], geo.homog.value[2]);
        }

        if (field === "y") {
            return CSNumber.div(geo.homog.value[1], geo.homog.value[2]);
        }
    }
    if (geo.kind === "L" || geo.kind === "S") {
        if (field === "homog") {
            return General.withUsage(geo.homog, "Line");
        }
        if (field === "angle") {
            erg = List.eucangle(List.ey, geo.homog);
            return General.withUsage(erg, "Angle");
        }
        if (field === "slope") {
            return CSNumber.neg(CSNumber.div(
                geo.homog.value[0], geo.homog.value[1]));
        }

    }
    if (geo.kind === "Tr") {
        if (field === "matrix") {
            return geo.matrix;
        }
    }
    if (geo.kind === "C") {
        if (field === "radius") { //Assumes that we have a circle
            var s = geo.matrix;
            var ax = s.value[0].value[0];
            var az = s.value[0].value[2];
            var bz = s.value[1].value[2];
            var cz = s.value[2].value[2];

            var n = CSNumber.mult(ax, ax);
            var aa = CSNumber.div(az, ax);
            var bb = CSNumber.div(bz, ax);
            var cc = CSNumber.div(cz, ax);
            erg = CSNumber.sqrt(CSNumber.sub(CSNumber.add(CSNumber.mult(aa, aa),
                    CSNumber.mult(bb, bb)),
                cc));

            return erg;
        }

        if (field === "size") {
            return geo.size;
        }

        if (field === "matrix") {
            return geo.matrix;
        }

        if (field === "center") {
            var cen = geoOps._helper.CenterOfConic(geo.matrix);
            cen = List.dehom(cen);
            return General.withUsage(cen, "Point");
        }

        if (field === "dualMatrix") {
            return List.normalizeMax(List.adjoint3(geo.matrix));
        }
    }
    if (geo.kind === "Text") {
        if (field === "pressed") {
            if (geo.checkbox) {
                return General.bool(geo.checkbox.checked);
            } else {
                return General.bool(false);
            }
        }
    }
    if (field === "trace") {
        return General.bool(!!geo.drawtrace);
    }

    if (Accessor.generalFields[field]) { //must be defined an an actual string
        erg = geo[Accessor.generalFields[field]];
        if (erg && erg.ctype) {
            return erg;
        } else if (typeof erg !== "object") {
            return General.wrap(erg);
        } else {
            return nada;
        }
    }
    //Accessors for masses
    if (geo.behavior) {
        if (field === "mass" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.mass);
        }
        if (field === "radius" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.radius);
        }
        if (field === "charge" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.charge);
        }
        if (field === "friction" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.friction);
        }
        if (field === "vx" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.vx);
        }
        if (field === "vy" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.vy);
        }
        if (field === "v" && geo.behavior.type === "Mass") {
            return List.realVector([geo.behavior.vx, geo.behavior.vy]);
        }
        if (field === "fx" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.fx);
        }
        if (field === "fy" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.fy);
        }
        if (field === "f" && geo.behavior.type === "Mass") {
            return List.realVector([geo.behavior.fx, geo.behavior.fy]);
        }

    }
    return nada;

};

Accessor.setField = function(geo, field, value) {
    var dir;

    if (field === "color" && CSNumber._helper.isNumberVecN(value, 3)) {
        geo.color = value;
    }
    if (field === "size" && value.ctype === "number") {
        geo.size = value;
    }
    if (field === "alpha" && value.ctype === "number") {
        geo.alpha = value;
    }
    if (field === "visible") {
        if (value.ctype === "boolean") {
            geo.visible = value.value;
        }
    }
    if (field === "pinned") {
        if (value.ctype === "boolean") {
            geo.pinned = value.value;
        }
    }
    if (field === "printlabel") {
        geo.printname = niceprint(value);
    }
    if (field === "trace") {
        if (value.ctype === "boolean") {
            if (value.value && !geo.drawtrace) {
                geo.drawtrace = true;
                setupTraceDrawing(geo);
            } else {
                geo.drawtrace = value.value;
            }
        }
    }

    if (field === "xy" && geo.kind === "P" && geo.movable && List._helper.isNumberVecN(value, 2)) {
        movepointscr(geo, List.turnIntoCSList([value.value[0], value.value[1], CSNumber.real(1)]), "homog");
    }

    if (field === "xy" && geo.kind === "P" && geo.movable && List._helper.isNumberVecN(value, 3)) {
        movepointscr(geo, value, "homog");
    }

    if (field === "x" && geo.kind === "P" && geo.movable && value.ctype === "number") {
        movepointscr(geo, List.turnIntoCSList([CSNumber.mult(value, geo.homog.value[2]), geo.homog.value[1], geo.homog.value[2]]), "homog");
    }

    if (field === "y" && geo.kind === "P" && geo.movable && value.ctype === "number") {
        movepointscr(geo, List.turnIntoCSList([geo.homog.value[0], CSNumber.mult(value, geo.homog.value[2]), geo.homog.value[2]]), "homog");
    }

    if (field === "homog" && geo.kind === "P" && geo.movable && List._helper.isNumberVecN(value, 3)) {
        movepointscr(geo, value, "homog");
    }

    if (field === "homog" && geo.kind === "L" && geo.movable && List._helper.isNumberVecN(value, 3)) {
        movepointscr(geo, value, "homog");
    }

    if (field === "angle" && geo.type === "Through" && value.ctype === "number") {
        var cc = CSNumber.cos(value);
        var ss = CSNumber.sin(value);
        dir = List.turnIntoCSList([cc, ss, CSNumber.real(0)]);
        movepointscr(geo, dir, "dir");
    }
    if (field === "slope" && geo.type === "Through" && value.ctype === "number") {
        dir = List.turnIntoCSList([CSNumber.real(1), value, CSNumber.real(0)]);
        movepointscr(geo, dir, "dir");
    }
    if (geo.kind === "C") {
        if (field === "radius" && geo.type === "CircleMr" && value.ctype === "number") {
            movepointscr(geo, value, "radius");
        }
    }
    if (geo.kind === "Text") {
        if (field === "pressed" && value.ctype === "boolean" && geo.checkbox) {
            geo.checkbox.checked = value.value;
        }
    }
    if (geo.behavior) {
        if (field === "mass" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.mass = value.value.real;
        }
        if (field === "mass" && geo.behavior.type === "Sun" && value.ctype === "number") {
            geo.behavior.mass = value.value.real;
        }
        if (field === "friction" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.friction = value.value.real;
        }
        if (field === "charge" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.charge = value.value.real;
        }
        if (field === "radius" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.radius = value.value.real;
        }
        if (field === "vx" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.vx = value.value.real;
        }
        if (field === "vy" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.vy = value.value.real;
        }
        if (field === "v" && geo.behavior.type === "Mass" && List._helper.isNumberVecN(value, 2)) {
            geo.behavior.vx = value.value[0].value.real;
            geo.behavior.vy = value.value[1].value.real;
        }
    }

};
<commit_msg>Fix color accessor on write<commit_after>//*************************************************************
// and here are the accessors for properties and elements
//*************************************************************

var Accessor = {};

Accessor.generalFields = { //Übersetungstafel der Feldnamen
    color: "color",
    colorhsb: "",
    size: "size",
    alpha: "alpha",
    isshowing: "isshowing",
    visible: "visible",
    name: "name",
    caption: "caption",
    trace: "",
    tracelength: "",
    selected: ""
};

Accessor.getGeoField = function(geoname, field) {
    if (typeof csgeo.csnames[geoname] !== 'undefined') {
        return Accessor.getField(csgeo.csnames[geoname], field);
    }
    return nada;
};

Accessor.setGeoField = function(geoname, field, value) {
    if (typeof csgeo.csnames[geoname] !== 'undefined') {
        return Accessor.setField(csgeo.csnames[geoname], field, value);
    }
    return nada;
};

Accessor.getField = function(geo, field) {
    var erg;
    if (geo.kind === "P") {
        if (field === "xy") {
            erg = List.dehom(geo.homog);
            return General.withUsage(erg, "Point");
        }

        if (field === "homog") {
            return General.withUsage(geo.homog, "Point");
        }

        if (field === "x") {
            return CSNumber.div(geo.homog.value[0], geo.homog.value[2]);
        }

        if (field === "y") {
            return CSNumber.div(geo.homog.value[1], geo.homog.value[2]);
        }
    }
    if (geo.kind === "L" || geo.kind === "S") {
        if (field === "homog") {
            return General.withUsage(geo.homog, "Line");
        }
        if (field === "angle") {
            erg = List.eucangle(List.ey, geo.homog);
            return General.withUsage(erg, "Angle");
        }
        if (field === "slope") {
            return CSNumber.neg(CSNumber.div(
                geo.homog.value[0], geo.homog.value[1]));
        }

    }
    if (geo.kind === "Tr") {
        if (field === "matrix") {
            return geo.matrix;
        }
    }
    if (geo.kind === "C") {
        if (field === "radius") { //Assumes that we have a circle
            var s = geo.matrix;
            var ax = s.value[0].value[0];
            var az = s.value[0].value[2];
            var bz = s.value[1].value[2];
            var cz = s.value[2].value[2];

            var n = CSNumber.mult(ax, ax);
            var aa = CSNumber.div(az, ax);
            var bb = CSNumber.div(bz, ax);
            var cc = CSNumber.div(cz, ax);
            erg = CSNumber.sqrt(CSNumber.sub(CSNumber.add(CSNumber.mult(aa, aa),
                    CSNumber.mult(bb, bb)),
                cc));

            return erg;
        }

        if (field === "size") {
            return geo.size;
        }

        if (field === "matrix") {
            return geo.matrix;
        }

        if (field === "center") {
            var cen = geoOps._helper.CenterOfConic(geo.matrix);
            cen = List.dehom(cen);
            return General.withUsage(cen, "Point");
        }

        if (field === "dualMatrix") {
            return List.normalizeMax(List.adjoint3(geo.matrix));
        }
    }
    if (geo.kind === "Text") {
        if (field === "pressed") {
            if (geo.checkbox) {
                return General.bool(geo.checkbox.checked);
            } else {
                return General.bool(false);
            }
        }
    }
    if (field === "trace") {
        return General.bool(!!geo.drawtrace);
    }

    if (Accessor.generalFields[field]) { //must be defined an an actual string
        erg = geo[Accessor.generalFields[field]];
        if (erg && erg.ctype) {
            return erg;
        } else if (typeof erg !== "object") {
            return General.wrap(erg);
        } else {
            return nada;
        }
    }
    //Accessors for masses
    if (geo.behavior) {
        if (field === "mass" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.mass);
        }
        if (field === "radius" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.radius);
        }
        if (field === "charge" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.charge);
        }
        if (field === "friction" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.friction);
        }
        if (field === "vx" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.vx);
        }
        if (field === "vy" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.vy);
        }
        if (field === "v" && geo.behavior.type === "Mass") {
            return List.realVector([geo.behavior.vx, geo.behavior.vy]);
        }
        if (field === "fx" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.fx);
        }
        if (field === "fy" && geo.behavior.type === "Mass") {
            return CSNumber.real(geo.behavior.fy);
        }
        if (field === "f" && geo.behavior.type === "Mass") {
            return List.realVector([geo.behavior.fx, geo.behavior.fy]);
        }

    }
    return nada;

};

Accessor.setField = function(geo, field, value) {
    var dir;

    if (field === "color" && List._helper.isNumberVecN(value, 3)) {
        geo.color = value;
    }
    if (field === "size" && value.ctype === "number") {
        geo.size = value;
    }
    if (field === "alpha" && value.ctype === "number") {
        geo.alpha = value;
    }
    if (field === "visible") {
        if (value.ctype === "boolean") {
            geo.visible = value.value;
        }
    }
    if (field === "pinned") {
        if (value.ctype === "boolean") {
            geo.pinned = value.value;
        }
    }
    if (field === "printlabel") {
        geo.printname = niceprint(value);
    }
    if (field === "trace") {
        if (value.ctype === "boolean") {
            if (value.value && !geo.drawtrace) {
                geo.drawtrace = true;
                setupTraceDrawing(geo);
            } else {
                geo.drawtrace = value.value;
            }
        }
    }

    if (field === "xy" && geo.kind === "P" && geo.movable && List._helper.isNumberVecN(value, 2)) {
        movepointscr(geo, List.turnIntoCSList([value.value[0], value.value[1], CSNumber.real(1)]), "homog");
    }

    if (field === "xy" && geo.kind === "P" && geo.movable && List._helper.isNumberVecN(value, 3)) {
        movepointscr(geo, value, "homog");
    }

    if (field === "x" && geo.kind === "P" && geo.movable && value.ctype === "number") {
        movepointscr(geo, List.turnIntoCSList([CSNumber.mult(value, geo.homog.value[2]), geo.homog.value[1], geo.homog.value[2]]), "homog");
    }

    if (field === "y" && geo.kind === "P" && geo.movable && value.ctype === "number") {
        movepointscr(geo, List.turnIntoCSList([geo.homog.value[0], CSNumber.mult(value, geo.homog.value[2]), geo.homog.value[2]]), "homog");
    }

    if (field === "homog" && geo.kind === "P" && geo.movable && List._helper.isNumberVecN(value, 3)) {
        movepointscr(geo, value, "homog");
    }

    if (field === "homog" && geo.kind === "L" && geo.movable && List._helper.isNumberVecN(value, 3)) {
        movepointscr(geo, value, "homog");
    }

    if (field === "angle" && geo.type === "Through" && value.ctype === "number") {
        var cc = CSNumber.cos(value);
        var ss = CSNumber.sin(value);
        dir = List.turnIntoCSList([cc, ss, CSNumber.real(0)]);
        movepointscr(geo, dir, "dir");
    }
    if (field === "slope" && geo.type === "Through" && value.ctype === "number") {
        dir = List.turnIntoCSList([CSNumber.real(1), value, CSNumber.real(0)]);
        movepointscr(geo, dir, "dir");
    }
    if (geo.kind === "C") {
        if (field === "radius" && geo.type === "CircleMr" && value.ctype === "number") {
            movepointscr(geo, value, "radius");
        }
    }
    if (geo.kind === "Text") {
        if (field === "pressed" && value.ctype === "boolean" && geo.checkbox) {
            geo.checkbox.checked = value.value;
        }
    }
    if (geo.behavior) {
        if (field === "mass" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.mass = value.value.real;
        }
        if (field === "mass" && geo.behavior.type === "Sun" && value.ctype === "number") {
            geo.behavior.mass = value.value.real;
        }
        if (field === "friction" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.friction = value.value.real;
        }
        if (field === "charge" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.charge = value.value.real;
        }
        if (field === "radius" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.radius = value.value.real;
        }
        if (field === "vx" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.vx = value.value.real;
        }
        if (field === "vy" && geo.behavior.type === "Mass" && value.ctype === "number") {
            geo.behavior.vy = value.value.real;
        }
        if (field === "v" && geo.behavior.type === "Mass" && List._helper.isNumberVecN(value, 2)) {
            geo.behavior.vx = value.value[0].value.real;
            geo.behavior.vy = value.value[1].value.real;
        }
    }

};
<|endoftext|>


<commit_before>define('app/controllers/base_array', ['ember'],
    //
    //  Base Array Controller
    //
    //  @returns Class
    //
    function () {

        'use scrict';

        return Ember.ArrayController.extend(Ember.Evented, {

            //
            //
            //  Properties
            //
            //

            model: null,
            content: null,
            passOnProperties: [],

            //
            //
            //  Computed Properties
            //
            //

            selectedObjects: function () {
                return this.get('content').filterBy('selected', true);
            }.property('content.@each.selected'),

            //
            //
            //  Initialization
            //
            //

            load: function () {
                this.setProperties({
                    loading: true,
                    content: new Array(),
                });
            }.on('init'),

            //
            //
            //  Public Methods
            //
            //

            setContent: function (content) {
                this.get('passOnProperties').forEach(function (property) {
                    content.setEach(property, this.get(property));
                }, this);
                this._updateContent(content);
                this.set('loading', false);
            },

            getObject: function (id) {
                return this.get('content').findBy('id', id);
            },

            objectExists: function (id) {
                return !!this.getObject(id);
            },

            //
            //
            //  Private Methods
            //
            //

            _updateContent: function (content) {

                // Remove deleted objects
                this.get('content').forEach(function (object) {
                    if (!content.findBy('id', object.id))
                        this._deleteObject(object);
                }, this);

                // Update existing objects or add new ones
                content.forEach(function (object) {
                    if (this.objectExists(object.id)) {
                        this._updateObject(object);
                    } else {
                        this._addObject(object);
                    }
                }, this);

                this.trigger('onChange');
            },

            _addObject: function (object) {
                Ember.run(this, function () {
                    this.addObject(this.get('model').create(object));
                    this.trigger('onAdd', {
                        object: object
                    });
                });
            },

            _deleteObject: function (object) {
                Ember.run(this, function () {
                    this.removeObject(object);
                    this.trigger('onDelete', {
                        object: object
                    });
                });
            },

            _updateObject: function (object) {
                Ember.run(this, function () {
                    this.getObject(object.id).update(object);
                    this.trigger('onUpdate', {
                        object: object
                    });
                });
            },
        })
    }
);
<commit_msg>Add content update into an ember run loop<commit_after>define('app/controllers/base_array', ['ember'],
    //
    //  Base Array Controller
    //
    //  @returns Class
    //
    function () {

        'use scrict';

        return Ember.ArrayController.extend(Ember.Evented, {

            //
            //
            //  Properties
            //
            //

            model: null,
            content: null,
            passOnProperties: [],

            //
            //
            //  Computed Properties
            //
            //

            selectedObjects: function () {
                return this.get('content').filterBy('selected', true);
            }.property('content.@each.selected'),

            //
            //
            //  Initialization
            //
            //

            load: function () {
                this.setProperties({
                    loading: true,
                    content: new Array(),
                });
            }.on('init'),

            //
            //
            //  Public Methods
            //
            //

            setContent: function (content) {
                this.get('passOnProperties').forEach(function (property) {
                    content.setEach(property, this.get(property));
                }, this);
                this._updateContent(content);
                this.set('loading', false);
            },

            getObject: function (id) {
                return this.get('content').findBy('id', id);
            },

            objectExists: function (id) {
                return !!this.getObject(id);
            },

            //
            //
            //  Private Methods
            //
            //

            _updateContent: function (content) {

                Ember.run(this, function () {
                    // Remove deleted objects
                    this.get('content').forEach(function (object) {
                        if (!content.findBy('id', object.id))
                            this._deleteObject(object);
                    }, this);

                    // Update existing objects or add new ones
                    content.forEach(function (object) {
                        if (this.objectExists(object.id)) {
                            this._updateObject(object);
                        } else {
                            this._addObject(object);
                        }
                    }, this);

                    this.trigger('onChange');
                });
            },

            _addObject: function (object) {
                Ember.run(this, function () {
                    this.addObject(this.get('model').create(object));
                    this.trigger('onAdd', {
                        object: object
                    });
                });
            },

            _deleteObject: function (object) {
                Ember.run(this, function () {
                    this.removeObject(object);
                    this.trigger('onDelete', {
                        object: object
                    });
                });
            },

            _updateObject: function (object) {
                Ember.run(this, function () {
                    this.getObject(object.id).update(object);
                    this.trigger('onUpdate', {
                        object: object
                    });
                });
            },
        })
    }
);
<|endoftext|>


<commit_before>importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.0.0/workbox-sw.js')

const staticCache = 'react-base-static-v1'
const dynamicCache = 'react-base-dynamic-v1'
const timeCache = 30 * 24 * 60 * 60

self.workbox.skipWaiting()
self.workbox.clientsClaim()

workbox.core.setCacheNameDetails({
  precache: staticCache,
  runtime:  dynamicCache
})

workbox.routing.registerRoute(
  /\.(?:js|css)$/,
  workbox.strategies.cacheFirst({
    cacheName: staticCache
  }),
)

workbox.routing.registerRoute(
  new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
  workbox.strategies.cacheFirst({
    cacheName: staticCache,
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 10,
      }),
    ],
  }),
)

workbox.routing.registerRoute(
  new RegExp('/content/(.*)'),
  workbox.strategies.staleWhileRevalidate({
    cacheName: dynamicCache,
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 60,
        maxAgeSeconds: timeCache
      }),
    ],
  }),
)
<commit_msg>Add time cache to static cache<commit_after>importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.0.0/workbox-sw.js')

const staticCache = 'react-base-static-v1'
const dynamicCache = 'react-base-dynamic-v1'
const timeCache = 30 * 24 * 60 * 60

self.workbox.skipWaiting()
self.workbox.clientsClaim()

workbox.core.setCacheNameDetails({
  precache: staticCache,
  runtime:  dynamicCache
})

workbox.routing.registerRoute(
  /\.(?:js|css)$/,
  workbox.strategies.cacheFirst({
    cacheName: staticCache,
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 20,
        maxAgeSeconds: timeCache
      }),
    ],
  }),
)

workbox.routing.registerRoute(
  new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
  workbox.strategies.cacheFirst({
    cacheName: staticCache,
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 10,
      }),
    ],
  }),
)

workbox.routing.registerRoute(
  new RegExp('/content/(.*)'),
  workbox.strategies.staleWhileRevalidate({
    cacheName: dynamicCache,
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 60,
        maxAgeSeconds: timeCache
      }),
    ],
  }),
)
<|endoftext|>


<commit_before>define('app/controllers/metric_add', ['app/models/metric', 'ember'],
    //
    //  Metric Add Controller
    //
    //  @returns Class
    //
    function (Metric) {

        'use strict';

        return Ember.Object.extend({

            //
            //
            //  Properties
            //
            //

            view: null,
            metrics: [],
            machine: null,
            callback: null,
            formReady: null,
            newMetric: null,
            metricsTree: [],

            //
            //
            //  Methods
            //
            //

            open: function (machine, callback) {
                this.clear();
                this.set('machine', machine)
                    .set('callback', callback);

                this.view.open();
                this.loadMetrics();
            },

            close: function () {
                this.clear();
                this.view.close();
            },

            add: function () {
                var that = this;
                Mist.metricsController.addMetric(
                    this.machine,
                    this.newMetric,
                    function (success, metric) {
                        if (success) {
                            if (that.callback)
                                that.callback(metric);
                            that.close();
                        }
                });
            },

            loadMetrics: function () {

                var url = '/backends/' + this.machine.backend.id +
                          '/machines/' + this.machine.id + '/metrics';

                var that = this;
                this.set('loadingMetrics', true);
                Mist.ajax.GET(url, {
                }).success(function(metrics) {
                    that._setMetrics(metrics);
                }).error(function(message) {
                    Mist.notificationController.notify(
                        'Failed to load metrics: ' + message);
                }).complete(function() {
                    that.set('loadingMetrics', false);
                });
            },

            clear: function () {
                this.view.clear();
                this.set('metrics', [])
                    .set('metricsTree', [])
                    .set('machine', null)
                    .set('callback', null)
                    .set('newMetric', {
                        'target': null,
                        'newName': null
                    });
            },

            getMetric: function (id) {
                return this.metrics.findBy('id', id);
            },

            //
            //
            //  Pseudo-Private Methods
            //
            //

            _setMetrics: function (metrics) {
                Ember.run(this, function () {
                    var newMetrics = [];
                    for (var metricId in metrics) {
                        var metric = metrics[metricId];
                        metric.id = metricId;
                        newMetrics.push(Metric.create(metric));
                    }
                    this.set('metrics', newMetrics);
                    this._setMetricsTree();
                });
            },

            _setMetricsTree: function () {
                var metricsObject = this._metricsToObject();
                var metricsTree = new Node(
                    metricsObject, 'collectd', 0, '', '');
                this.set('metricsTree', metricsTree);
            },

            _metricsToObject: function () {

                var ret = new Object();
                this.metrics.forEach(function (metric) {
                    var prevParent = ret;
                    var list = metric.id.split('.');
                    var lastIndex = list.length - 1;
                    list.forEach(function (subTarget, index) {

                        // Check if this property exists in the object already
                        if (!prevParent[subTarget]) {

                            // if this is the last node, append the id
                            if (index == lastIndex)
                                prevParent[subTarget] = metric.id;
                            else
                                prevParent[subTarget] = new Object();
                        }
                        prevParent = prevParent[subTarget];
                    });
                });
                return ret;
            },

            //
            //
            //  Observers
            //
            //

            newMetricObserver: function () {
                if (this.newMetric.target && this.newMetric.newName)
                    this.set('formReady', true);
                else
                    this.set('formReady', false);
            }.observes('newMetric.target', 'newMetric.newName', 'newMetric'),

        });

        function Node (dict, text, nestIndex, target, parentTarget) {

            this.text = text;
            this.nestIndex = nestIndex;
            this.isRootNode = !nestIndex;
            this.target = (parentTarget ? parentTarget + '.' : '') + target;

            var subTargets = new Array();

            for (var key in dict) {

                // if dict[key] is an object, there are more nested targets to
                // parse. Therfore we create another Node() using dict[key].
                // Else, we pass an empty object to signify an ending node.

                var childObject = dict[key] instanceof Object ? dict[key] : {};

                subTargets.push(
                    new Node(childObject, key, nestIndex + 1, key, this.target)
                );
            }

            this.subTargets = subTargets;
            this.isEndNode = !subTargets.length;
        };
    }
);
<commit_msg>Sort metric nodes<commit_after>define('app/controllers/metric_add', ['app/models/metric', 'ember'],
    //
    //  Metric Add Controller
    //
    //  @returns Class
    //
    function (Metric) {

        'use strict';

        return Ember.Object.extend({

            //
            //
            //  Properties
            //
            //

            view: null,
            metrics: [],
            machine: null,
            callback: null,
            formReady: null,
            newMetric: null,
            metricsTree: [],

            //
            //
            //  Methods
            //
            //

            open: function (machine, callback) {
                this.clear();
                this.set('machine', machine)
                    .set('callback', callback);

                this.view.open();
                this.loadMetrics();
            },

            close: function () {
                this.clear();
                this.view.close();
            },

            add: function () {
                var that = this;
                Mist.metricsController.addMetric(
                    this.machine,
                    this.newMetric,
                    function (success, metric) {
                        if (success) {
                            if (that.callback)
                                that.callback(metric);
                            that.close();
                        }
                });
            },

            loadMetrics: function () {

                var url = '/backends/' + this.machine.backend.id +
                          '/machines/' + this.machine.id + '/metrics';

                var that = this;
                this.set('loadingMetrics', true);
                Mist.ajax.GET(url, {
                }).success(function(metrics) {
                    that._setMetrics(metrics);
                }).error(function(message) {
                    Mist.notificationController.notify(
                        'Failed to load metrics: ' + message);
                }).complete(function() {
                    that.set('loadingMetrics', false);
                });
            },

            clear: function () {
                this.view.clear();
                this.set('metrics', [])
                    .set('metricsTree', [])
                    .set('machine', null)
                    .set('callback', null)
                    .set('newMetric', {
                        'target': null,
                        'newName': null
                    });
            },

            getMetric: function (id) {
                return this.metrics.findBy('id', id);
            },

            //
            //
            //  Pseudo-Private Methods
            //
            //

            _setMetrics: function (metrics) {
                Ember.run(this, function () {
                    var newMetrics = [];
                    for (var metricId in metrics) {
                        var metric = metrics[metricId];
                        metric.id = metricId;
                        newMetrics.push(Metric.create(metric));
                    }
                    this.set('metrics', newMetrics);
                    this._setMetricsTree();
                });
            },

            _setMetricsTree: function () {
                var metricsObject = this._metricsToObject();
                var metricsTree = new Node(
                    metricsObject, 'collectd', 0, '', '');
                this.set('metricsTree', metricsTree);
            },

            _metricsToObject: function () {

                var ret = new Object();
                this.metrics.forEach(function (metric) {
                    var prevParent = ret;
                    var list = metric.id.split('.');
                    var lastIndex = list.length - 1;
                    list.forEach(function (subTarget, index) {

                        // Check if this property exists in the object already
                        if (!prevParent[subTarget]) {

                            // if this is the last node, append the id
                            if (index == lastIndex)
                                prevParent[subTarget] = metric.id;
                            else
                                prevParent[subTarget] = new Object();
                        }
                        prevParent = prevParent[subTarget];
                    });
                });
                return ret;
            },

            //
            //
            //  Observers
            //
            //

            newMetricObserver: function () {
                if (this.newMetric.target && this.newMetric.newName)
                    this.set('formReady', true);
                else
                    this.set('formReady', false);
            }.observes('newMetric.target', 'newMetric.newName', 'newMetric'),

        });

        function Node (dict, text, nestIndex, target, parentTarget) {

            this.text = text;
            this.nestIndex = nestIndex;
            this.isRootNode = !nestIndex;
            this.target = (parentTarget ? parentTarget + '.' : '') + target;

            var subTargets = new Array();

            for (var key in dict) {

                // if dict[key] is an object, there are more nested targets to
                // parse. Therfore we create another Node() using dict[key].
                // Else, we pass an empty object to signify an ending node.

                var childObject = dict[key] instanceof Object ? dict[key] : {};

                subTargets.push(
                    new Node(childObject, key, nestIndex + 1, key, this.target)
                );
            }

            this.subTargets = sortNodes(subTargets);
            this.isEndNode = !subTargets.length;
        };

        function sortNodes (nodes) {

            // Bubble short
            var changed = true;
            while (changed) {
                changed = false;
                for (var i = 0; i < nodes.length - 1; i++) {
                    if (nodes[i].text > nodes[i+1].text) {
                        var tmp = nodes[i];
                        nodes[i] = nodes[i+1];
                        nodes[i+1] = tmp;
                        changed = true;
                    }
                }
            }
            return nodes;
        }
    }
);
<|endoftext|>


<commit_before>(function( Popcorn, window, document ) {

  var

  EMPTY_STRING = "",

  jwReady = false,
  jwLoaded = false,
  jwCallbacks = [];

  function onJWPlayerAPIReady() {
    jwReady = true;
    var i = jwCallbacks.length;
    while( i-- ) {
      jwCallbacks[ i ]();
      delete jwCallbacks[ i ];
    }
  };

  function jwplayerReadyCheck() {
    if ( window.jwplayer ) {
      onJWPlayerAPIReady();
    } else {
      setTimeout( jwplayerReadyCheck, 100 );
    }
  }

  function isJWPlayerReady() {
    // If the jwplayer API isn't injected, do it now.
    if ( !jwLoaded ) {
      if ( !window.jwplayer ) {
        var tag = document.createElement( "script" );

        tag.src = "https://jwpsrv.com/library/zaIF4JI9EeK2FSIACpYGxA.js";
        var firstScriptTag = document.getElementsByTagName( "script" )[ 0 ];
        firstScriptTag.parentNode.insertBefore( tag, firstScriptTag );
      }
      jwLoaded = true;
      jwplayerReadyCheck();
    }
    return jwReady;
  }

  function addJWPlayerCallback( callback ) {
    jwCallbacks.unshift( callback );
  }

  function HTMLJWPlayerVideoElement( id ) {

    if ( !window.postMessage ) {
      throw "ERROR: HTMLJWPlayerVideoElement requires window.postMessage";
    }

    var self = new Popcorn._MediaElementProto(),
      parent = typeof id === "string" ? document.querySelector( id ) : id,
      impl = {
        src: EMPTY_STRING,
        networkState: self.NETWORK_EMPTY,
        readyState: self.HAVE_NOTHING,
        seeking: false,
        autoplay: EMPTY_STRING,
        preload: EMPTY_STRING,
        controls: false,
        loop: false,
        poster: EMPTY_STRING,
        volume: 1,
        muted: false,
        currentTime: 0,
        duration: NaN,
        ended: false,
        paused: true,
        error: null
      },
      playerReady = false,
      catchRoguePauseEvent = false,
      catchRoguePlayEvent = false,
      mediaReady = false,
      loopedPlay = false,
      player,
      playerPaused = true,
      mediaReadyCallbacks = [],
      playerState = -1,
      lastLoadedFraction = 0,
      firstPlay = true,
      firstPause = false;

    // Namespace all events we'll produce
    self._eventNamespace = Popcorn.guid( "HTMLJWPlayerVideoElement::" );

    self.parentNode = parent;

    // Mark this as JWPlayer
    self._util.type = "JWPlayer";

    function addMediaReadyCallback( callback ) {
      mediaReadyCallbacks.unshift( callback );
    }

    function onReady() {
      // JWPlayer needs a play/pause to force ready state.
      // However, the ready state does not happen until after the play/pause callbacks.
      // So we put this inside a setTimeout to ensure we do this afterwards,
      // thus, actually being ready.
      setTimeout( function() {
        impl.duration = player.getDuration();
        self.dispatchEvent( "durationchange" );
        impl.readyState = self.HAVE_METADATA;
        self.dispatchEvent( "loadedmetadata" );
        self.dispatchEvent( "loadeddata" );

        impl.readyState = self.HAVE_FUTURE_DATA;
        self.dispatchEvent( "canplay" );

        mediaReady = true;

        var i = 0;
        while( mediaReadyCallbacks.length ) {
          mediaReadyCallbacks[ i ]();
          mediaReadyCallbacks.shift();
        }
        // We can't easily determine canplaythrough, but will send anyway.
        impl.readyState = self.HAVE_ENOUGH_DATA;
        self.dispatchEvent( "canplaythrough" );
      }, 0 );
    }

    // TODO: (maybe)
    // JWPlayer events cannot be removed, so we use functions inside the event.
    // This way we can change these functions to "remove" events.
    function onPauseEvent() {
      if ( catchRoguePauseEvent ) {
        catchRoguePauseEvent = false;
      } else if ( firstPause ) {
        firstPause = false;
        onReady();
      } else {
        onPause();
      }
    }
    function onPlayEvent() {
      if ( firstPlay ) {
        // fake ready event
        firstPlay = false;

        // Set initial paused state
        if ( impl.autoplay || !impl.paused ) {
          impl.paused = false;
          addMediaReadyCallback( onPlay );
          onReady();
        } else {
          firstPause = true;
          catchRoguePlayEvent = true;
          player.pause( true );
        }
      } else if ( catchRoguePlayEvent ) {
        catchRoguePlayEvent = false;
        catchRoguePauseEvent = true;
        // Repause without triggering any events.
        player.pause( true );
      } else {
        onPlay();
      }
    }

    function onSeekEvent() {
      if ( impl.seeking ) {
        onSeeked();
      }
    }

    function onPlayerReady() {
      player.onPause( onPauseEvent );
      player.onTime(function() {
        if ( !impl.ended && !impl.seeking ) {
          impl.currentTime = player.getPosition();
          self.dispatchEvent( "timeupdate" );
        }
      });
      player.onSeek( onSeekEvent );
      player.onPlay(function() {
        if ( !impl.ended ) {
          onPlayEvent();
        }
      });
      player.onBufferChange( onProgress );
      player.onComplete( onEnded );
      player.play( true );
    }

    function getDuration() {
      return player.getDuration();
    }

    function onPlayerError( e ) {
      var err = { name: "MediaError" };
      err.message = e.message;
      err.code = e.code || 5;

      impl.error = err;
      self.dispatchEvent( "error" );
    }

    function destroyPlayer() {
      if ( !( playerReady && player ) ) {
        return;
      }

      player.destroy();
    }

    function changeSrc( aSrc ) {
      if ( !self._canPlaySrc( aSrc ) ) {
        impl.error = {
          name: "MediaError",
          message: "Media Source Not Supported",
          code: MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED
        };
        self.dispatchEvent( "error" );
        return;
      }

      // Use any player vars passed on the URL
      var playerVars = self._util.parseUri( aSrc ).queryKey;

      // Show/hide controls. Sync with impl.controls and prefer URL value.
      impl.controls = playerVars.controls = playerVars.controls || impl.controls;

      impl.src = aSrc;

      // Make sure JWPlayer is ready, and if not, register a callback
      if ( !isJWPlayerReady() ) {
        addJWPlayerCallback( function() { changeSrc( aSrc ); } );
        return;
      }

      if ( playerReady ) {
        destroyPlayer();
      }

      jwplayer( parent.id ).setup({
        file: aSrc,
        width: "100%",
        height: "100%",
        controls: impl.controls
      });

      player = jwplayer( parent.id );
      player.onReady( onPlayerReady );
      player.onError( onPlayerError );
      jwplayer.utils.log = function( msg, obj ) {
        if ( typeof console !== "undefined" && typeof console.log !== "undefined" ) {
          if ( obj ) {
            console.log( msg, obj );
          } else {
            console.log( msg );
          }
        }

        if ( msg === "No suitable players found and fallback enabled" ) {
          onPlayerError({
            message: msg,
            code: 4
          });
        }
      };

      impl.networkState = self.NETWORK_LOADING;
      self.dispatchEvent( "loadstart" );
      self.dispatchEvent( "progress" );
    }

    function getCurrentTime() {
      return impl.currentTime;
    }

    function changeCurrentTime( aTime ) {
      impl.currentTime = aTime;
      if ( !mediaReady ) {
        addMediaReadyCallback( function() {
          onSeeking();
          player.seek( aTime );
        });
        return;
      }

      onSeeking();
      player.seek( aTime );
    }

    function onSeeking() {
      impl.seeking = true;
      // jwplayer plays right after a seek, we do not want this.
      if ( impl.paused ) {
        catchRoguePlayEvent = true;
      }
      self.dispatchEvent( "seeking" );
    }

    function onSeeked() {
      impl.ended = false;
      impl.seeking = false;
      self.dispatchEvent( "timeupdate" );
      self.dispatchEvent( "seeked" );
      self.dispatchEvent( "canplay" );
      self.dispatchEvent( "canplaythrough" );
    }

    function onPlay() {
      impl.paused = false;

      if ( playerPaused ) {
        playerPaused = false;

        // Only 1 play when video.loop=true
        if ( ( impl.loop && !loopedPlay ) || !impl.loop ) {
          loopedPlay = true;
          self.dispatchEvent( "play" );
        }
        self.dispatchEvent( "playing" );
      }
    }

    function onProgress() {
      self.dispatchEvent( "progress" );
    }

    self.play = function() {
      self.dispatchEvent( "play" );
      impl.paused = false;
      if ( !mediaReady ) {
        addMediaReadyCallback( function() { self.play(); } );
        return;
      }
      if ( impl.ended ) {
        changeCurrentTime( 0 );
        impl.ended = false;
      }
      player.play( true );
    };

    function onPause() {
      impl.paused = true;
      if ( !playerPaused ) {
        playerPaused = true;
        self.dispatchEvent( "pause" );
      }
    }

    self.pause = function() {
      impl.paused = true;
      if ( !mediaReady ) {
        addMediaReadyCallback( function() { self.pause(); } );
        return;
      }
      player.pause( true );
    };

    function onEnded() {
      if ( impl.loop ) {
        changeCurrentTime( 0 );
      } else {
        impl.ended = true;
        onPause();
        self.dispatchEvent( "timeupdate" );
        self.dispatchEvent( "ended" );
      }
    }

    function setVolume( aValue ) {
      impl.volume = aValue;
      if ( !mediaReady ) {
        addMediaReadyCallback( function() {
          setVolume( impl.volume );
        });
        return;
      }
      player.setVolume( impl.volume * 100 );
      self.dispatchEvent( "volumechange" );
    }

    function setMuted( aValue ) {
      impl.muted = aValue;
      if ( !mediaReady ) {
        addMediaReadyCallback( function() { setMuted( impl.muted ); } );
        return;
      }
      player.setMute( aValue );
      self.dispatchEvent( "volumechange" );
    }

    function getMuted() {
      return impl.muted;
    }

    Object.defineProperties( self, {

      src: {
        get: function() {
          return impl.src;
        },
        set: function( aSrc ) {
          if ( aSrc && aSrc !== impl.src ) {
            changeSrc( aSrc );
          }
        }
      },

      autoplay: {
        get: function() {
          return impl.autoplay;
        },
        set: function( aValue ) {
          impl.autoplay = self._util.isAttributeSet( aValue );
        }
      },

      loop: {
        get: function() {
          return impl.loop;
        },
        set: function( aValue ) {
          impl.loop = self._util.isAttributeSet( aValue );
        }
      },

      width: {
        get: function() {
          return self.parentNode.offsetWidth;
        }
      },

      height: {
        get: function() {
          return self.parentNode.offsetHeight;
        }
      },

      currentTime: {
        get: function() {
          return getCurrentTime();
        },
        set: function( aValue ) {
          changeCurrentTime( aValue );
        }
      },

      duration: {
        get: function() {
          return getDuration();
        }
      },

      ended: {
        get: function() {
          return impl.ended;
        }
      },

      paused: {
        get: function() {
          return impl.paused;
        }
      },

      seeking: {
        get: function() {
          return impl.seeking;
        }
      },

      readyState: {
        get: function() {
          return impl.readyState;
        }
      },

      networkState: {
        get: function() {
          return impl.networkState;
        }
      },

      volume: {
        get: function() {
          return impl.volume;
        },
        set: function( aValue ) {
          if ( aValue < 0 || aValue > 1 ) {
            throw "Volume value must be between 0.0 and 1.0";
          }

          setVolume( aValue );
        }
      },

      muted: {
        get: function() {
          return impl.muted;
        },
        set: function( aValue ) {
          setMuted( self._util.isAttributeSet( aValue ) );
        }
      },

      error: {
        get: function() {
          return impl.error;
        }
      },

      buffered: {
        get: function () {
          var timeRanges = {
            start: function( index ) {
              if ( index === 0 ) {
                return 0;
              }

              //throw fake DOMException/INDEX_SIZE_ERR
              throw "INDEX_SIZE_ERR: DOM Exception 1";
            },
            end: function( index ) {
              var duration;
              if ( index === 0 ) {
                duration = getDuration();
                if ( !duration ) {
                  return 0;
                }

                return duration * ( player.getBuffer() / 100 );
              }

              //throw fake DOMException/INDEX_SIZE_ERR
              throw "INDEX_SIZE_ERR: DOM Exception 1";
            },
            length: 1
          };

          return timeRanges;
        }
      }
    });

    self._canPlaySrc = Popcorn.HTMLJWPlayerVideoElement._canPlaySrc;
    self.canPlayType = Popcorn.HTMLJWPlayerVideoElement.canPlayType;

    return self;
  }

  Popcorn.HTMLJWPlayerVideoElement = function( id ) {
    return new HTMLJWPlayerVideoElement( id );
  };

  // Helper for identifying URLs we know how to play.
  Popcorn.HTMLJWPlayerVideoElement._canPlaySrc = function( url ) {
    // Because of the nature of JWPlayer playing all media types,
    // it can potentially play all url formats.
    return "probably";
  };

  // This could potentially support everything. It is a bit of a catch all player.
  Popcorn.HTMLJWPlayerVideoElement.canPlayType = function( type ) {
    return "probably";
  };

}( Popcorn, window, document ));
<commit_msg>Allow multiple sources for JW player<commit_after>(function( Popcorn, window, document ) {

  var

  EMPTY_STRING = "",

  jwReady = false,
  jwLoaded = false,
  jwCallbacks = [];

  function onJWPlayerAPIReady() {
    jwReady = true;
    var i = jwCallbacks.length;
    while( i-- ) {
      jwCallbacks[ i ]();
      delete jwCallbacks[ i ];
    }
  };

  function jwplayerReadyCheck() {
    if ( window.jwplayer ) {
      onJWPlayerAPIReady();
    } else {
      setTimeout( jwplayerReadyCheck, 100 );
    }
  }

  function isJWPlayerReady() {
    // If the jwplayer API isn't injected, do it now.
    if ( !jwLoaded ) {
      if ( !window.jwplayer ) {
        var tag = document.createElement( "script" );

        tag.src = "https://jwpsrv.com/library/zaIF4JI9EeK2FSIACpYGxA.js";
        var firstScriptTag = document.getElementsByTagName( "script" )[ 0 ];
        firstScriptTag.parentNode.insertBefore( tag, firstScriptTag );
      }
      jwLoaded = true;
      jwplayerReadyCheck();
    }
    return jwReady;
  }

  function addJWPlayerCallback( callback ) {
    jwCallbacks.unshift( callback );
  }

  function HTMLJWPlayerVideoElement( id ) {

    if ( !window.postMessage ) {
      throw "ERROR: HTMLJWPlayerVideoElement requires window.postMessage";
    }

    var self = new Popcorn._MediaElementProto(),
      parent = typeof id === "string" ? document.querySelector( id ) : id,
      impl = {
        src: EMPTY_STRING,
        networkState: self.NETWORK_EMPTY,
        readyState: self.HAVE_NOTHING,
        seeking: false,
        autoplay: EMPTY_STRING,
        preload: EMPTY_STRING,
        controls: false,
        loop: false,
        poster: EMPTY_STRING,
        volume: 1,
        muted: false,
        currentTime: 0,
        duration: NaN,
        ended: false,
        paused: true,
        error: null
      },
      playerReady = false,
      catchRoguePauseEvent = false,
      catchRoguePlayEvent = false,
      mediaReady = false,
      loopedPlay = false,
      player,
      playerPaused = true,
      mediaReadyCallbacks = [],
      playerState = -1,
      lastLoadedFraction = 0,
      firstPlay = true,
      firstPause = false;

    // Namespace all events we'll produce
    self._eventNamespace = Popcorn.guid( "HTMLJWPlayerVideoElement::" );

    self.parentNode = parent;

    // Mark this as JWPlayer
    self._util.type = "JWPlayer";

    function addMediaReadyCallback( callback ) {
      mediaReadyCallbacks.unshift( callback );
    }

    function onReady() {
      // JWPlayer needs a play/pause to force ready state.
      // However, the ready state does not happen until after the play/pause callbacks.
      // So we put this inside a setTimeout to ensure we do this afterwards,
      // thus, actually being ready.
      setTimeout( function() {
        impl.duration = player.getDuration();
        self.dispatchEvent( "durationchange" );
        impl.readyState = self.HAVE_METADATA;
        self.dispatchEvent( "loadedmetadata" );
        self.dispatchEvent( "loadeddata" );

        impl.readyState = self.HAVE_FUTURE_DATA;
        self.dispatchEvent( "canplay" );

        mediaReady = true;

        var i = 0;
        while( mediaReadyCallbacks.length ) {
          mediaReadyCallbacks[ i ]();
          mediaReadyCallbacks.shift();
        }
        // We can't easily determine canplaythrough, but will send anyway.
        impl.readyState = self.HAVE_ENOUGH_DATA;
        self.dispatchEvent( "canplaythrough" );
      }, 0 );
    }

    // TODO: (maybe)
    // JWPlayer events cannot be removed, so we use functions inside the event.
    // This way we can change these functions to "remove" events.
    function onPauseEvent() {
      if ( catchRoguePauseEvent ) {
        catchRoguePauseEvent = false;
      } else if ( firstPause ) {
        firstPause = false;
        onReady();
      } else {
        onPause();
      }
    }
    function onPlayEvent() {
      if ( firstPlay ) {
        // fake ready event
        firstPlay = false;

        // Set initial paused state
        if ( impl.autoplay || !impl.paused ) {
          impl.paused = false;
          addMediaReadyCallback( onPlay );
          onReady();
        } else {
          firstPause = true;
          catchRoguePlayEvent = true;
          player.pause( true );
        }
      } else if ( catchRoguePlayEvent ) {
        catchRoguePlayEvent = false;
        catchRoguePauseEvent = true;
        // Repause without triggering any events.
        player.pause( true );
      } else {
        onPlay();
      }
    }

    function onSeekEvent() {
      if ( impl.seeking ) {
        onSeeked();
      }
    }

    function onPlayerReady() {
      player.onPause( onPauseEvent );
      player.onTime(function() {
        if ( !impl.ended && !impl.seeking ) {
          impl.currentTime = player.getPosition();
          self.dispatchEvent( "timeupdate" );
        }
      });
      player.onSeek( onSeekEvent );
      player.onPlay(function() {
        if ( !impl.ended ) {
          onPlayEvent();
        }
      });
      player.onBufferChange( onProgress );
      player.onComplete( onEnded );
      player.play( true );
    }

    function getDuration() {
      return player.getDuration();
    }

    function onPlayerError( e ) {
      var err = { name: "MediaError" };
      err.message = e.message;
      err.code = e.code || 5;

      impl.error = err;
      self.dispatchEvent( "error" );
    }

    function destroyPlayer() {
      if ( !( playerReady && player ) ) {
        return;
      }

      player.destroy();
    }

    function changeSrc( aSrc ) {
      if ( !self._canPlaySrc( aSrc ) ) {
        impl.error = {
          name: "MediaError",
          message: "Media Source Not Supported",
          code: MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED
        };
        self.dispatchEvent( "error" );
        return;
      }

      // Use any player vars passed on the URL
      var playerVars = self._util.parseUri( aSrc ).queryKey;

      // Show/hide controls. Sync with impl.controls and prefer URL value.
      impl.controls = playerVars.controls = playerVars.controls || impl.controls;

      impl.src = aSrc;

      // Make sure JWPlayer is ready, and if not, register a callback
      if ( !isJWPlayerReady() ) {
        addJWPlayerCallback( function() { changeSrc( aSrc ); } );
        return;
      }

      if ( playerReady ) {
        destroyPlayer();
      }

      //ExecOnline: allow source to be a hash
      var params = {
        width: "100%",
        height: "100%",
        controls: impl.controls
      };

      if(typeof aSrc == "string"){
        params["file"] = aSrc;
      } else {
        params["sources"] = aSrc;
      }

      jwplayer( parent.id ).setup(params);

      player = jwplayer( parent.id );
      player.onReady( onPlayerReady );
      player.onError( onPlayerError );
      jwplayer.utils.log = function( msg, obj ) {
        if ( typeof console !== "undefined" && typeof console.log !== "undefined" ) {
          if ( obj ) {
            console.log( msg, obj );
          } else {
            console.log( msg );
          }
        }

        if ( msg === "No suitable players found and fallback enabled" ) {
          onPlayerError({
            message: msg,
            code: 4
          });
        }
      };

      impl.networkState = self.NETWORK_LOADING;
      self.dispatchEvent( "loadstart" );
      self.dispatchEvent( "progress" );
    }

    function getCurrentTime() {
      return impl.currentTime;
    }

    function changeCurrentTime( aTime ) {
      impl.currentTime = aTime;
      if ( !mediaReady ) {
        addMediaReadyCallback( function() {
          onSeeking();
          player.seek( aTime );
        });
        return;
      }

      onSeeking();
      player.seek( aTime );
    }

    function onSeeking() {
      impl.seeking = true;
      // jwplayer plays right after a seek, we do not want this.
      if ( impl.paused ) {
        catchRoguePlayEvent = true;
      }
      self.dispatchEvent( "seeking" );
    }

    function onSeeked() {
      impl.ended = false;
      impl.seeking = false;
      self.dispatchEvent( "timeupdate" );
      self.dispatchEvent( "seeked" );
      self.dispatchEvent( "canplay" );
      self.dispatchEvent( "canplaythrough" );
    }

    function onPlay() {
      impl.paused = false;

      if ( playerPaused ) {
        playerPaused = false;

        // Only 1 play when video.loop=true
        if ( ( impl.loop && !loopedPlay ) || !impl.loop ) {
          loopedPlay = true;
          self.dispatchEvent( "play" );
        }
        self.dispatchEvent( "playing" );
      }
    }

    function onProgress() {
      self.dispatchEvent( "progress" );
    }

    self.play = function() {
      self.dispatchEvent( "play" );
      impl.paused = false;
      if ( !mediaReady ) {
        addMediaReadyCallback( function() { self.play(); } );
        return;
      }
      if ( impl.ended ) {
        changeCurrentTime( 0 );
        impl.ended = false;
      }
      player.play( true );
    };

    function onPause() {
      impl.paused = true;
      if ( !playerPaused ) {
        playerPaused = true;
        self.dispatchEvent( "pause" );
      }
    }

    self.pause = function() {
      impl.paused = true;
      if ( !mediaReady ) {
        addMediaReadyCallback( function() { self.pause(); } );
        return;
      }
      player.pause( true );
    };

    function onEnded() {
      if ( impl.loop ) {
        changeCurrentTime( 0 );
      } else {
        impl.ended = true;
        onPause();
        self.dispatchEvent( "timeupdate" );
        self.dispatchEvent( "ended" );
      }
    }

    function setVolume( aValue ) {
      impl.volume = aValue;
      if ( !mediaReady ) {
        addMediaReadyCallback( function() {
          setVolume( impl.volume );
        });
        return;
      }
      player.setVolume( impl.volume * 100 );
      self.dispatchEvent( "volumechange" );
    }

    function setMuted( aValue ) {
      impl.muted = aValue;
      if ( !mediaReady ) {
        addMediaReadyCallback( function() { setMuted( impl.muted ); } );
        return;
      }
      player.setMute( aValue );
      self.dispatchEvent( "volumechange" );
    }

    function getMuted() {
      return impl.muted;
    }

    Object.defineProperties( self, {

      src: {
        get: function() {
          return impl.src;
        },
        set: function( aSrc ) {
          if ( aSrc && aSrc !== impl.src ) {
            changeSrc( aSrc );
          }
        }
      },

      autoplay: {
        get: function() {
          return impl.autoplay;
        },
        set: function( aValue ) {
          impl.autoplay = self._util.isAttributeSet( aValue );
        }
      },

      loop: {
        get: function() {
          return impl.loop;
        },
        set: function( aValue ) {
          impl.loop = self._util.isAttributeSet( aValue );
        }
      },

      width: {
        get: function() {
          return self.parentNode.offsetWidth;
        }
      },

      height: {
        get: function() {
          return self.parentNode.offsetHeight;
        }
      },

      currentTime: {
        get: function() {
          return getCurrentTime();
        },
        set: function( aValue ) {
          changeCurrentTime( aValue );
        }
      },

      duration: {
        get: function() {
          return getDuration();
        }
      },

      ended: {
        get: function() {
          return impl.ended;
        }
      },

      paused: {
        get: function() {
          return impl.paused;
        }
      },

      seeking: {
        get: function() {
          return impl.seeking;
        }
      },

      readyState: {
        get: function() {
          return impl.readyState;
        }
      },

      networkState: {
        get: function() {
          return impl.networkState;
        }
      },

      volume: {
        get: function() {
          return impl.volume;
        },
        set: function( aValue ) {
          if ( aValue < 0 || aValue > 1 ) {
            throw "Volume value must be between 0.0 and 1.0";
          }

          setVolume( aValue );
        }
      },

      muted: {
        get: function() {
          return impl.muted;
        },
        set: function( aValue ) {
          setMuted( self._util.isAttributeSet( aValue ) );
        }
      },

      error: {
        get: function() {
          return impl.error;
        }
      },

      buffered: {
        get: function () {
          var timeRanges = {
            start: function( index ) {
              if ( index === 0 ) {
                return 0;
              }

              //throw fake DOMException/INDEX_SIZE_ERR
              throw "INDEX_SIZE_ERR: DOM Exception 1";
            },
            end: function( index ) {
              var duration;
              if ( index === 0 ) {
                duration = getDuration();
                if ( !duration ) {
                  return 0;
                }

                return duration * ( player.getBuffer() / 100 );
              }

              //throw fake DOMException/INDEX_SIZE_ERR
              throw "INDEX_SIZE_ERR: DOM Exception 1";
            },
            length: 1
          };

          return timeRanges;
        }
      }
    });

    self._canPlaySrc = Popcorn.HTMLJWPlayerVideoElement._canPlaySrc;
    self.canPlayType = Popcorn.HTMLJWPlayerVideoElement.canPlayType;

    return self;
  }

  Popcorn.HTMLJWPlayerVideoElement = function( id ) {
    return new HTMLJWPlayerVideoElement( id );
  };

  // Helper for identifying URLs we know how to play.
  Popcorn.HTMLJWPlayerVideoElement._canPlaySrc = function( url ) {
    // Because of the nature of JWPlayer playing all media types,
    // it can potentially play all url formats.
    return "probably";
  };

  // This could potentially support everything. It is a bit of a catch all player.
  Popcorn.HTMLJWPlayerVideoElement.canPlayType = function( type ) {
    return "probably";
  };

}( Popcorn, window, document ));
<|endoftext|>


<commit_before>import React, {
    createElement,
    useMemo,
    useRef,
    useState,
    useCallback,
    useImperativeHandle,
    useLayoutEffect
} from 'react';
import { eachDayOfInterval, format } from 'proton-shared/lib/date-fns-utc';
import PropTypes from 'prop-types';
import { useWindowSize, classnames } from 'react-components';
import { noop } from 'proton-shared/lib/helpers/function';
import { isDateYYMMDDEqual } from 'proton-shared/lib/date/date';

import { sortWithTemporaryEvent } from './layout';
import useTimeGridMouseHandler from './useTimeGridMouseHandler';
import useTimeGridEventLayout from './useTimeGridEventLayout';
import useDayGridEventLayout from './useDayGridEventLayout';
import { getKey, toUTCMinutes } from './splitTimeGridEventsPerDay';
import useDayGridMouseHandler from './useDayGridMouseHandler';
import usePopoverEvent from './usePopoverEvent';
import useMore from './useMore';
import { getEvent, isMoreSelected } from './DayGrid';
import { toPercent } from './mouseHelpers/mathHelpers';

const hours = Array.from({ length: 24 }, (a, i) => {
    return new Date(Date.UTC(2000, 0, 1, i));
});

const totalMinutes = 24 * 60;

const defaultFormat = (utcDate) => format(utcDate, 'p');

const TimeGrid = React.forwardRef(
    (
        {
            now,
            tzid,
            date,
            dateRange: [start, end],
            displaySecondaryTimezone,
            primaryTimezone,
            secondaryTimezone,
            secondaryTimezoneOffset,
            events = [],
            components: { FullDayEvent, PartDayEvent, PopoverEvent, MorePopoverEvent, MoreFullDayEvent },
            formatTime = defaultFormat,
            onClickDate = noop,
            onEditEvent = noop,
            defaultEventDuration = 30,
            defaultEventData,
            isInteractionEnabled = false,
            weekdaysLong = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
        },
        ref
    ) => {
        const timeGridRef = useRef();
        const mainRef = useRef();
        const nowRef = useRef();
        const titleRef = useRef();
        const scrollRef = useRef();
        const [windowWidth, windowHeight] = useWindowSize();

        const [scrollTop, setScrollTop] = useState();

        const days = useMemo(() => {
            return eachDayOfInterval(start, end);
        }, [+start, +end]);

        const [temporaryEvent, setTemporaryEvent] = useState();
        const [selectedEventID, setSelectedEventID] = useState();
        const [moreDateIdx, setMoreDateIdx] = useState();

        const sortedEvents = useMemo(() => {
            return sortWithTemporaryEvent(events, temporaryEvent);
        }, [temporaryEvent, events]);

        const formattedHours = useMemo(() => {
            return hours.map(formatTime);
        }, [formatTime]);

        const formattedSecondaryHours = useMemo(() => {
            return hours.map((hourDate) => formatTime(new Date(hourDate.getTime() - secondaryTimezoneOffset)));
        }, [secondaryTimezoneOffset, formatTime]);

        const timeEvents = useMemo(() => sortedEvents.filter((e) => !e.isAllDay), [sortedEvents]);
        const dayEvents = useMemo(() => sortedEvents.filter((e) => !!e.isAllDay), [sortedEvents]);
        const daysRows = useMemo(() => [days], [days]);

        const dayEventHeight = 24;
        const numberOfRows = 3;

        const eventsPerRows = useDayGridEventLayout(daysRows, dayEvents, numberOfRows, dayEventHeight);

        const [{ eventsInRow, eventsInRowStyles, maxRows, eventsInRowSummary }] = eventsPerRows;
        const actualRows = Math.max(Math.min(maxRows, numberOfRows + 1), 1);

        const [eventsPerDay, eventsLaidOut] = useTimeGridEventLayout(timeEvents, days, totalMinutes);

        const onDayGridMouseDown = useDayGridMouseHandler({
            isInteractionEnabled,
            setTemporaryEvent,
            setSelectedEventID,
            setMoreDateIdx,
            defaultEventData,
            events: dayEvents,
            eventsPerRows,
            rows: daysRows
        });

        const onTimeGridMouseDown = useTimeGridMouseHandler({
            totalDays: days.length,
            totalMinutes,
            interval: 30,
            defaultEventDuration,
            defaultEventData,
            setTemporaryEvent,
            setSelectedEventID,
            events: timeEvents,
            eventsPerDay,
            days
        });

        const nowTop = toUTCMinutes(now) / totalMinutes;
        const nowTopPercentage = toPercent(nowTop);

        const selectedEventRef = useRef();
        const selectedEvent = useMemo(() => {
            return sortedEvents.find(({ id }) => id === selectedEventID);
        }, [selectedEventID, sortedEvents]);

        const [popoverStyle, popoverLayout] = usePopoverEvent(
            selectedEvent,
            selectedEventRef,
            mainRef,
            days,
            windowWidth,
            windowHeight
        );
        let isFirstSelection = true;

        const selectedMoreRef = useRef();
        const [selectedMoreData, moreRow, moreIdx] = useMore(moreDateIdx, eventsPerRows, dayEvents);
        const selectedMoreDate =
            typeof moreRow !== 'undefined' && typeof moreIdx !== 'undefined' ? days[moreIdx] : undefined;
        const [morePopoverStyle, morePopoverLayout] = usePopoverEvent(
            selectedMoreData,
            selectedMoreRef,
            mainRef,
            days,
            windowWidth,
            windowHeight
        );

        const handleScroll = useCallback(({ target }) => {
            setScrollTop(target.scrollTop);
        }, []);

        useImperativeHandle(
            ref,
            () => ({
                scrollToNow: () => {
                    if (!scrollRef.current || !timeGridRef.current) {
                        return;
                    }
                    //const nowTop = nowRef.current.offsetTop;
                    const timeRect = timeGridRef.current.getBoundingClientRect();
                    const nowTopOffset = timeRect.height * nowTop;
                    const titleRect = titleRef.current.getBoundingClientRect();
                    const scrollRect = scrollRef.current.getBoundingClientRect();
                    scrollRef.current.scrollTop = nowTopOffset - scrollRect.height / 2 + titleRect.height / 2;
                }
            }),
            [ref, nowTop]
        );

        useLayoutEffect(() => {
            ref.current.scrollToNow();
        }, []);

        return (

                            {displaySecondaryTimezone ? (

                                        {secondaryTimezone}

                            ) : null}

{primaryTimezone}

                            {days.map((day) => {
                                return (
                                    <button
                                        className="flex-item-fluid aligncenter calendar-grid-heading p0-5"
                                        type="button"
                                        key={day.getUTCDate()}
                                        aria-current={isDateYYMMDDEqual(day, now) ? 'date' : undefined}
                                        aria-pressed={isDateYYMMDDEqual(day, date) ? true : undefined}
                                        onClick={() => onClickDate(day)}
                                    >

                                            {day.getUTCDate()}

                                            {weekdaysLong[day.getUTCDay()]}

                                    </button>
                                );
                            })}

                            {displaySecondaryTimezone ?

 : null}

                                    {days.map((day) => {
                                        return (

                                        );
                                    })}

                                    {eventsInRowStyles.map(({ idx, type, style }) => {
                                        if (type === 'more') {
                                            const isSelected = isMoreSelected(idx, moreIdx, 0, moreRow);
                                            const eventRef = isSelected ? selectedMoreRef : undefined;
                                            return createElement(MoreFullDayEvent, {
                                                key: `more${idx}`,
                                                style,
                                                more: eventsInRowSummary[idx].more,
                                                eventRef,
                                                isSelected
                                            });
                                        }

                                        const event = getEvent(idx, eventsInRow, dayEvents);
                                        const isSelected = event.id === selectedEventID;
                                        const isBeforeNow = now > event.end && !isDateYYMMDDEqual(now, event.end);
                                        const eventRef = isSelected && isFirstSelection ? selectedEventRef : undefined;
                                        if (eventRef) {
                                            isFirstSelection = false;
                                        }
                                        return createElement(FullDayEvent, {
                                            event,
                                            style,
                                            key: event.id,
                                            eventRef,
                                            formatTime,
                                            isSelected,
                                            isBeforeNow
                                        });
                                    })}

                        {displaySecondaryTimezone ? (

                                {formattedSecondaryHours.map((text, i) => {
                                    return (

                                            {i === 0 ? null : (

                                                    {text}

                                            )}

                                    );
                                })}

                        ) : null}

                            {formattedHours.map((text, i) => {
                                return (

                                        {i === 0 ? null : (

                                                {text}

                                        )}

                                );
                            })}

                                {hours.map((hour) => {
                                    return
;
                                })}

                            {days.map((day) => {
                                const key = getKey(day);
                                return (

                                        {Array.isArray(eventsPerDay[key]) &&
                                            eventsPerDay[key].map((eventTimeDay, i) => {
                                                const { idx } = eventTimeDay;
                                                const event = timeEvents[idx];
                                                const style = eventsLaidOut[key][i];
                                                const isSelected = event.id === selectedEventID;
                                                const isBeforeNow = now > event.end;
                                                const eventRef =
                                                    isSelected && isFirstSelection ? selectedEventRef : undefined;
                                                if (eventRef) {
                                                    isFirstSelection = false;
                                                }
                                                return createElement(PartDayEvent, {
                                                    event,
                                                    style,
                                                    key: event.id,
                                                    formatTime,
                                                    eventRef,
                                                    isSelected,
                                                    isBeforeNow
                                                });
                                            })}
                                        {isDateYYMMDDEqual(day, now) ? (

                                        ) : null}

                                );
                            })}

                    {morePopoverStyle &&
                        selectedMoreData &&
                        createElement(MorePopoverEvent, {
                            events: selectedMoreData,
                            style: morePopoverStyle,
                            layout: morePopoverLayout,
                            eventRef: selectedEventRef,
                            selectedEventID,
                            setSelectedEventID: (id) => {
                                setSelectedEventID(id);
                                setTemporaryEvent();
                            },
                            selectedMoreDate,
                            formatTime,
                            onClose: () => setMoreDateIdx()
                        })}
                    {popoverStyle &&
                        selectedEvent &&
                        createElement(PopoverEvent, {
                            event: selectedEvent,
                            style: popoverStyle,
                            layout: popoverLayout,
                            tzid,
                            formatTime,
                            onClose: () => {
                                setSelectedEventID();
                                setTemporaryEvent();
                            },
                            onEditEvent
                        })}

        );
    }
);

TimeGrid.propTypes = {
    children: PropTypes.func,
    onCreateEvent: PropTypes.func,
    onEditEvent: PropTypes.func,
    onClickDate: PropTypes.func,
    isInteractionEnabled: PropTypes.bool,
    defaultEventDuration: PropTypes.number,
    events: PropTypes.array,
    dateRange: PropTypes.array,
    now: PropTypes.instanceOf(Date),
    scrollRef: PropTypes.object
};

export default TimeGrid;
<commit_msg>Fix - calendar headings remain sticky on mobile<commit_after>import React, {
    createElement,
    useMemo,
    useRef,
    useState,
    useCallback,
    useImperativeHandle,
    useLayoutEffect
} from 'react';
import { eachDayOfInterval, format } from 'proton-shared/lib/date-fns-utc';
import PropTypes from 'prop-types';
import { useWindowSize, classnames } from 'react-components';
import { noop } from 'proton-shared/lib/helpers/function';
import { isDateYYMMDDEqual } from 'proton-shared/lib/date/date';

import { sortWithTemporaryEvent } from './layout';
import useTimeGridMouseHandler from './useTimeGridMouseHandler';
import useTimeGridEventLayout from './useTimeGridEventLayout';
import useDayGridEventLayout from './useDayGridEventLayout';
import { getKey, toUTCMinutes } from './splitTimeGridEventsPerDay';
import useDayGridMouseHandler from './useDayGridMouseHandler';
import usePopoverEvent from './usePopoverEvent';
import useMore from './useMore';
import { getEvent, isMoreSelected } from './DayGrid';
import { toPercent } from './mouseHelpers/mathHelpers';

const hours = Array.from({ length: 24 }, (a, i) => {
    return new Date(Date.UTC(2000, 0, 1, i));
});

const totalMinutes = 24 * 60;

const defaultFormat = (utcDate) => format(utcDate, 'p');

const TimeGrid = React.forwardRef(
    (
        {
            now,
            tzid,
            date,
            dateRange: [start, end],
            displaySecondaryTimezone,
            primaryTimezone,
            secondaryTimezone,
            secondaryTimezoneOffset,
            events = [],
            components: { FullDayEvent, PartDayEvent, PopoverEvent, MorePopoverEvent, MoreFullDayEvent },
            formatTime = defaultFormat,
            onClickDate = noop,
            onEditEvent = noop,
            defaultEventDuration = 30,
            defaultEventData,
            isInteractionEnabled = false,
            weekdaysLong = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
        },
        ref
    ) => {
        const timeGridRef = useRef();
        const mainRef = useRef();
        const nowRef = useRef();
        const titleRef = useRef();
        const scrollRef = useRef();
        const [windowWidth, windowHeight] = useWindowSize();

        const [scrollTop, setScrollTop] = useState();

        const days = useMemo(() => {
            return eachDayOfInterval(start, end);
        }, [+start, +end]);

        const [temporaryEvent, setTemporaryEvent] = useState();
        const [selectedEventID, setSelectedEventID] = useState();
        const [moreDateIdx, setMoreDateIdx] = useState();

        const sortedEvents = useMemo(() => {
            return sortWithTemporaryEvent(events, temporaryEvent);
        }, [temporaryEvent, events]);

        const formattedHours = useMemo(() => {
            return hours.map(formatTime);
        }, [formatTime]);

        const formattedSecondaryHours = useMemo(() => {
            return hours.map((hourDate) => formatTime(new Date(hourDate.getTime() - secondaryTimezoneOffset)));
        }, [secondaryTimezoneOffset, formatTime]);

        const timeEvents = useMemo(() => sortedEvents.filter((e) => !e.isAllDay), [sortedEvents]);
        const dayEvents = useMemo(() => sortedEvents.filter((e) => !!e.isAllDay), [sortedEvents]);
        const daysRows = useMemo(() => [days], [days]);

        const dayEventHeight = 24;
        const numberOfRows = 3;

        const eventsPerRows = useDayGridEventLayout(daysRows, dayEvents, numberOfRows, dayEventHeight);

        const [{ eventsInRow, eventsInRowStyles, maxRows, eventsInRowSummary }] = eventsPerRows;
        const actualRows = Math.max(Math.min(maxRows, numberOfRows + 1), 1);

        const [eventsPerDay, eventsLaidOut] = useTimeGridEventLayout(timeEvents, days, totalMinutes);

        const onDayGridMouseDown = useDayGridMouseHandler({
            isInteractionEnabled,
            setTemporaryEvent,
            setSelectedEventID,
            setMoreDateIdx,
            defaultEventData,
            events: dayEvents,
            eventsPerRows,
            rows: daysRows
        });

        const onTimeGridMouseDown = useTimeGridMouseHandler({
            totalDays: days.length,
            totalMinutes,
            interval: 30,
            defaultEventDuration,
            defaultEventData,
            setTemporaryEvent,
            setSelectedEventID,
            events: timeEvents,
            eventsPerDay,
            days
        });

        const nowTop = toUTCMinutes(now) / totalMinutes;
        const nowTopPercentage = toPercent(nowTop);

        const selectedEventRef = useRef();
        const selectedEvent = useMemo(() => {
            return sortedEvents.find(({ id }) => id === selectedEventID);
        }, [selectedEventID, sortedEvents]);

        const [popoverStyle, popoverLayout] = usePopoverEvent(
            selectedEvent,
            selectedEventRef,
            mainRef,
            days,
            windowWidth,
            windowHeight
        );
        let isFirstSelection = true;

        const selectedMoreRef = useRef();
        const [selectedMoreData, moreRow, moreIdx] = useMore(moreDateIdx, eventsPerRows, dayEvents);
        const selectedMoreDate =
            typeof moreRow !== 'undefined' && typeof moreIdx !== 'undefined' ? days[moreIdx] : undefined;
        const [morePopoverStyle, morePopoverLayout] = usePopoverEvent(
            selectedMoreData,
            selectedMoreRef,
            mainRef,
            days,
            windowWidth,
            windowHeight
        );

        const handleScroll = useCallback(({ target }) => {
            setScrollTop(target.scrollTop);
        }, []);

        useImperativeHandle(
            ref,
            () => ({
                scrollToNow: () => {
                    if (!scrollRef.current || !timeGridRef.current) {
                        return;
                    }
                    //const nowTop = nowRef.current.offsetTop;
                    const timeRect = timeGridRef.current.getBoundingClientRect();
                    const nowTopOffset = timeRect.height * nowTop;
                    const titleRect = titleRef.current.getBoundingClientRect();
                    const scrollRect = scrollRef.current.getBoundingClientRect();
                    scrollRef.current.scrollTop = nowTopOffset - scrollRect.height / 2 + titleRect.height / 2;
                }
            }),
            [ref, nowTop]
        );

        useLayoutEffect(() => {
            ref.current.scrollToNow();
        }, []);

        return (

                            {displaySecondaryTimezone ? (

                                        {secondaryTimezone}

                            ) : null}

{primaryTimezone}

                            {days.map((day) => {
                                return (
                                    <button
                                        className="flex-item-fluid aligncenter calendar-grid-heading p0-5"
                                        type="button"
                                        key={day.getUTCDate()}
                                        aria-current={isDateYYMMDDEqual(day, now) ? 'date' : undefined}
                                        aria-pressed={isDateYYMMDDEqual(day, date) ? true : undefined}
                                        onClick={() => onClickDate(day)}
                                    >

                                            {day.getUTCDate()}

                                            {weekdaysLong[day.getUTCDay()]}

                                    </button>
                                );
                            })}

                            {displaySecondaryTimezone ?

 : null}

                                    {days.map((day) => {
                                        return (

                                        );
                                    })}

                                    {eventsInRowStyles.map(({ idx, type, style }) => {
                                        if (type === 'more') {
                                            const isSelected = isMoreSelected(idx, moreIdx, 0, moreRow);
                                            const eventRef = isSelected ? selectedMoreRef : undefined;
                                            return createElement(MoreFullDayEvent, {
                                                key: `more${idx}`,
                                                style,
                                                more: eventsInRowSummary[idx].more,
                                                eventRef,
                                                isSelected
                                            });
                                        }

                                        const event = getEvent(idx, eventsInRow, dayEvents);
                                        const isSelected = event.id === selectedEventID;
                                        const isBeforeNow = now > event.end && !isDateYYMMDDEqual(now, event.end);
                                        const eventRef = isSelected && isFirstSelection ? selectedEventRef : undefined;
                                        if (eventRef) {
                                            isFirstSelection = false;
                                        }
                                        return createElement(FullDayEvent, {
                                            event,
                                            style,
                                            key: event.id,
                                            eventRef,
                                            formatTime,
                                            isSelected,
                                            isBeforeNow
                                        });
                                    })}

                        {displaySecondaryTimezone ? (

                                {formattedSecondaryHours.map((text, i) => {
                                    return (

                                            {i === 0 ? null : (

                                                    {text}

                                            )}

                                    );
                                })}

                        ) : null}

                            {formattedHours.map((text, i) => {
                                return (

                                        {i === 0 ? null : (

                                                {text}

                                        )}

                                );
                            })}

                                {hours.map((hour) => {
                                    return
;
                                })}

                            {days.map((day) => {
                                const key = getKey(day);
                                return (

                                        {Array.isArray(eventsPerDay[key]) &&
                                            eventsPerDay[key].map((eventTimeDay, i) => {
                                                const { idx } = eventTimeDay;
                                                const event = timeEvents[idx];
                                                const style = eventsLaidOut[key][i];
                                                const isSelected = event.id === selectedEventID;
                                                const isBeforeNow = now > event.end;
                                                const eventRef =
                                                    isSelected && isFirstSelection ? selectedEventRef : undefined;
                                                if (eventRef) {
                                                    isFirstSelection = false;
                                                }
                                                return createElement(PartDayEvent, {
                                                    event,
                                                    style,
                                                    key: event.id,
                                                    formatTime,
                                                    eventRef,
                                                    isSelected,
                                                    isBeforeNow
                                                });
                                            })}
                                        {isDateYYMMDDEqual(day, now) ? (

                                        ) : null}

                                );
                            })}

                    {morePopoverStyle &&
                        selectedMoreData &&
                        createElement(MorePopoverEvent, {
                            events: selectedMoreData,
                            style: morePopoverStyle,
                            layout: morePopoverLayout,
                            eventRef: selectedEventRef,
                            selectedEventID,
                            setSelectedEventID: (id) => {
                                setSelectedEventID(id);
                                setTemporaryEvent();
                            },
                            selectedMoreDate,
                            formatTime,
                            onClose: () => setMoreDateIdx()
                        })}
                    {popoverStyle &&
                        selectedEvent &&
                        createElement(PopoverEvent, {
                            event: selectedEvent,
                            style: popoverStyle,
                            layout: popoverLayout,
                            tzid,
                            formatTime,
                            onClose: () => {
                                setSelectedEventID();
                                setTemporaryEvent();
                            },
                            onEditEvent
                        })}

        );
    }
);

TimeGrid.propTypes = {
    children: PropTypes.func,
    onCreateEvent: PropTypes.func,
    onEditEvent: PropTypes.func,
    onClickDate: PropTypes.func,
    isInteractionEnabled: PropTypes.bool,
    defaultEventDuration: PropTypes.number,
    events: PropTypes.array,
    dateRange: PropTypes.array,
    now: PropTypes.instanceOf(Date),
    scrollRef: PropTypes.object
};

export default TimeGrid;
<|endoftext|>