<commit_before>function switchPage({data}) {
    return {
        page: data.page,
        lastPage: data.lastPage || null
    };
}

function togglePopup({state}) {
    return {
        showPopup: !state.showPopup
    };
}

function openPopup({data}) {
    return {
        showPopup: true,
        page: data.page
    };
}

function back({state}) {
    if (state.lastPage) {
        return {
            page: state.lastPage
        };
    } else {
        closePopup({state});
    }
}

function closePopup({state}) {
    return {
        showPopup: false,
        page: state.page === 'magiclink' ? '' : state.page
    };
}

async function signout({api}) {
    await api.member.signout();
    return {
        action: 'signout:success'
    };
}

async function signin({data, api}) {
    await api.member.sendMagicLink(data);
    return {
        page: 'magiclink'
    };
}

async function signup({data, api}) {
    const {plan, email, name} = data;
    if (plan.toLowerCase() === 'free') {
        await api.member.sendMagicLink(data);
    } else {
        await api.member.checkoutPlan({plan, email, name});
    }
    return {
        page: 'magiclink'
    };
}

async function updateEmail({data, api}) {
    await api.member.sendMagicLink(data);
    return {
        action: 'updateEmail:success'
    };
}

async function checkoutPlan({data, api}) {
    const {plan} = data;
    await api.member.checkoutPlan({
        plan
    });
}

async function updateSubscription({data, api}) {
    const {plan, subscriptionId, cancelAtPeriodEnd} = data;
    await api.member.updateSubscription({
        planName: plan, subscriptionId, cancelAtPeriodEnd
    });
    const member = await api.member.sessionData();
    return {
        action: 'updateSubscription:success',
        page: 'accountHome',
        member: member
    };
}

async function editBilling({data, updateState, state, api}) {
    await api.member.editBilling();
}

async function updateMember({data, updateState, state, api}) {
    const {name, subscribed} = data;
    const member = await api.member.update({name, subscribed});
    if (!member) {
        return {
            action: 'updateMember:failed'
        };
    } else {
        return {
            action: 'updateMember:success',
            member: member
        };
    }
}

async function updateProfile({data, updateState, state, api}) {
    const {name, subscribed} = data;
    const member = await api.member.update({name, subscribed});
    if (!member) {
        return {
            action: 'updateProfile:failed'
        };
    } else {
        return {
            action: 'updateProfile:success',
            member: member,
            page: 'accountHome'
        };
    }
}

const Actions = {
    togglePopup,
    openPopup,
    closePopup,
    switchPage,
    back,
    signout,
    signin,
    signup,
    updateEmail,
    updateSubscription,
    updateMember,
    updateProfile,
    editBilling,
    checkoutPlan
};

/** Handle actions in the App, returns updated state */
export default async function ActionHandler({action, data, updateState, state, api}) {
    const handler = Actions[action];
    if (handler) {
        return await handler({data, updateState, state, api}) || {};
    }
    return {};
}<commit_msg>Added subscription cancel action handler<commit_after>function switchPage({data}) {
    return {
        page: data.page,
        lastPage: data.lastPage || null
    };
}

function togglePopup({state}) {
    return {
        showPopup: !state.showPopup
    };
}

function openPopup({data}) {
    return {
        showPopup: true,
        page: data.page
    };
}

function back({state}) {
    if (state.lastPage) {
        return {
            page: state.lastPage
        };
    } else {
        closePopup({state});
    }
}

function closePopup({state}) {
    return {
        showPopup: false,
        page: state.page === 'magiclink' ? '' : state.page
    };
}

async function signout({api}) {
    await api.member.signout();
    return {
        action: 'signout:success'
    };
}

async function signin({data, api}) {
    await api.member.sendMagicLink(data);
    return {
        page: 'magiclink'
    };
}

async function signup({data, api}) {
    const {plan, email, name} = data;
    if (plan.toLowerCase() === 'free') {
        await api.member.sendMagicLink(data);
    } else {
        await api.member.checkoutPlan({plan, email, name});
    }
    return {
        page: 'magiclink'
    };
}

async function updateEmail({data, api}) {
    await api.member.sendMagicLink(data);
    return {
        action: 'updateEmail:success'
    };
}

async function checkoutPlan({data, api}) {
    const {plan} = data;
    await api.member.checkoutPlan({
        plan
    });
}

async function updateSubscription({data, api}) {
    const {plan, subscriptionId, cancelAtPeriodEnd} = data;
    await api.member.updateSubscription({
        planName: plan, subscriptionId, cancelAtPeriodEnd
    });
    const member = await api.member.sessionData();
    return {
        action: 'updateSubscription:success',
        page: 'accountHome',
        member: member
    };
}

async function cancelSubscription({data, api}) {
    const {subscriptionId, cancelAtPeriodEnd} = data;
    await api.member.updateSubscription({
        subscriptionId, cancelAtPeriodEnd
    });
    const member = await api.member.sessionData();
    return {
        action: 'cancelSubscription:success',
        page: 'accountHome',
        member: member
    };
}

async function editBilling({data, updateState, state, api}) {
    await api.member.editBilling();
}

async function updateMember({data, updateState, state, api}) {
    const {name, subscribed} = data;
    const member = await api.member.update({name, subscribed});
    if (!member) {
        return {
            action: 'updateMember:failed'
        };
    } else {
        return {
            action: 'updateMember:success',
            member: member
        };
    }
}

async function updateProfile({data, updateState, state, api}) {
    const {name, subscribed} = data;
    const member = await api.member.update({name, subscribed});
    if (!member) {
        return {
            action: 'updateProfile:failed'
        };
    } else {
        return {
            action: 'updateProfile:success',
            member: member,
            page: 'accountHome'
        };
    }
}

const Actions = {
    togglePopup,
    openPopup,
    closePopup,
    switchPage,
    back,
    signout,
    signin,
    signup,
    updateEmail,
    updateSubscription,
    cancelSubscription,
    updateMember,
    updateProfile,
    editBilling,
    checkoutPlan
};

/** Handle actions in the App, returns updated state */
export default async function ActionHandler({action, data, updateState, state, api}) {
    const handler = Actions[action];
    if (handler) {
        return await handler({data, updateState, state, api}) || {};
    }
    return {};
}<|endoftext|>


<commit_before>'use strict';

define(['angular', 'ol', 'olus', 'zones', 'pois', 'sidebar', 'toolbar', 'layermanager', 'SparqlJson', 'map', 'query', 'search', 'print', 'permalink', 'measure', 'legend', 'geolocation', 'core', 'api', 'angular-gettext', 'bootstrap', 'translations', 'compositions', 'status_creator', 'ows', 'cesium', 'cesiumjs'],

    function (angular, ol, olus, zones, pois, sidebar, toolbar, layermanager, SparqlJson) {
        var module = angular.module('hs', [
            'hs.toolbar',
            'hs.layermanager',
            'hs.map',
            'hs.query',
            'hs.search', 'hs.permalink', 'hs.measure',
            'hs.geolocation', 'hs.core',
            'hs.api',
            'gettext',
            'hs.sidebar',
            'hs.cesium'
        ]);

        module.directive('hs', ['hs.map.service', 'Core', '$compile', '$timeout', function (OlMap, Core, $compile, $timeout) {
            return {
                templateUrl: hsl_path + 'hslayers.html',
                link: function (scope, element) {
                    angular.element('.page-content', element).append($compile('

')(scope));
                    $timeout(function () {
                        Core.fullScreenMap(element)
                    }, 0);
                }
            };
        }])

            .directive('hs.foodiezones.infoDirective', function () {
                return {
                    templateUrl: './info.html?bust=' + gitsha,
                    link: function (scope, element, attrs) {
                        $('#zone-info-dialog').modal('show');
                    }
                };
            }).directive('description', ['$compile', 'hs.utils.service', function ($compile, utils) {
                return {
                    templateUrl: './description.html?bust=' + gitsha,
                    scope: {
                        object: '=',
                        url: '@'
                    },
                    link: function (scope, element, attrs) {
                        scope.describe = function (e, attribute) {
                            if (angular.element(e.target).parent().find('table').length > 0) {
                                angular.element(e.target).parent().find('table').remove();
                            } else {
                                var table = angular.element('

');
                                angular.element(e.target).parent().append(table);
                                $compile(table)(scope.$parent);
                            }
                        }
                        if (angular.isUndefined(scope.object) && angular.isDefined(attrs.url) && typeof attrs.url == 'string') {
                            scope.object = { attributes: [] };
                            var q = 'https://www.foodie-cloud.org/sparql?default-graph-uri=&query=' + encodeURIComponent('describe <' + attrs.url + '>') + '&should-sponge=&format=application%2Fsparql-results%2Bjson&timeout=0&debug=on';
                            $.ajax({
                                url: utils.proxify(q)
                            })
                                .done(function (response) {
                                    if (angular.isUndefined(response.results)) return;
                                    for (var i = 0; i < response.results.bindings.length; i++) {
                                        var b = response.results.bindings[i];
                                        var short_name = b.p.value;
                                        if (short_name.indexOf('#') > -1)
                                            short_name = short_name.split('#')[1];
                                        scope.object.attributes.push({ short_name: short_name, value: b.o.value });
                                        if (!scope.$$phase) scope.$apply();
                                    }
                                })
                        }
                    }
                };
            }]);

        module.value('config', {
            default_layers: [new ol.layer.Tile({
                source: new ol.source.OSM({
                    wrapX: false
                }),
                title: "Base layer",
                base: true
            })],
            //project_name: 'hslayers',
            default_view: new ol.View({
                center: [-8.796119, 41.942791],
                zoom: 16,
                units: "m",
                projection: 'EPSG:4326'
            })
        });

        module.controller('Main', ['$scope', 'Core', 'hs.query.baseService', 'hs.compositions.service_parser', '$timeout', 'hs.map.service', '$http', 'config', '$rootScope', 'hs.utils.service', '$compile', 'hs.query.wmsService', '$sce',
            function ($scope, Core, QueryService, composition_parser, $timeout, hsMap, $http, config, $rootScope, utils, $compile, WmsService, $sce) {
                if (console) console.log("Main called");
                $scope.hsl_path = hsl_path; //Get this from hslayers.js file
                $scope.Core = Core;
                Core.sidebarExpanded = false;
                var map;
                var zones_source = new ol.source.Vector();
                var olus_source = new ol.source.Vector();
                var spoi_source = new ol.source.Vector();

                $rootScope.$on('map.loaded', function () {
                    map = hsMap.map;
                    olus.init($scope, $compile, map, utils);
                    zones.init($scope, $compile, map, utils);
                    pois.init($scope, $compile);
                    zones.get();
                    map.on('moveend', extentChanged);
                });

                function extentChanged() {
                    olus.get();
                }

                $rootScope.$on('map.sync_center', function (e, center, bounds) {
                    pois.getPois(map, utils, bounds);
                })

                config.default_layers.push(olus.createOluLayer());
                config.default_layers.push(zones.createLayer());
                config.default_layers.push(pois.createPoiLayer());

                $scope.showInfo = function (zone) {
                    var id, obj_type;
                    if (zone.properties['management zone']) { id = zone.properties['management zone'].getValue(); obj_type = 'Management Zone' }
                    if (zone.properties.poi) { id = zone.properties.poi.getValue(); obj_type = 'Point of interest' }
                    if (zone.properties.parcel) { id = zone.properties.parcel.getValue(); obj_type = 'Land use parcel' }
                    $scope.zone = {
                        id: $sce.trustAsHtml(),
                        attributes: [],
                        links: [],
                        obj_type: obj_type
                    };
                    describe(id, function () {
                        if (!$scope.$$phase) $scope.$apply();
                    });
                }

                function describe(id, callback) {
                    var q = 'https://www.foodie-cloud.org/sparql?default-graph-uri=&query=' + encodeURIComponent('describe <' + id + '>') + '&should-sponge=&format=application%2Fsparql-results%2Bjson&timeout=0&debug=on';
                    $.ajax({
                        url: utils.proxify(q)
                    })
                        .done(function (response) {
                            if (angular.isUndefined(response.results)) return;
                            for (var i = 0; i < response.results.bindings.length; i++) {
                                var b = response.results.bindings[i];
                                var short_name = b.p.value;
                                if (short_name.indexOf('#') > -1)
                                    short_name = short_name.split('#')[1];
                                $scope.zone.attributes.push({ short_name: short_name, value: b.o.value });
                            }
                            $scope.getLinksTo(id, callback);
                        })
                }

                $scope.getLinksTo = function (id, callback) {
                    var q = 'https://www.foodie-cloud.org/sparql?default-graph-uri=&query=' + encodeURIComponent('PREFIX geo: <http://www.opengis.net/ont/geosparql#> PREFIX geof: <http://www.opengis.net/def/function/geosparql/> PREFIX virtrdf: <http://www.openlinksw.com/schemas/virtrdf#> PREFIX poi: <http://www.openvoc.eu/poi#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT * WHERE {?obj <http://www.opengis.net/ont/geosparql#hasGeometry> ?obj_geom. ?obj_geom geo:asWKT ?Coordinates . FILTER(bif:st_intersects (?Coordinates, ?wkt)). { SELECT ?wkt WHERE { <' + id + '> geo:hasGeometry ?geometry. ?geometry geo:asWKT ?wkt.} } }') + '&should-sponge=&format=application%2Fsparql-results%2Bjson&timeout=0&debug=on';
                    $.ajax({
                        url: utils.proxify(q)
                    })
                        .done(function (response) {
                            for (var i = 0; i < response.results.bindings.length; i++) {
                                var b = response.results.bindings[i];
                                $scope.zone.links.push({ url: b.obj.value });
                            }
                            callback();
                        })
                }

                $scope.$on('infopanel.feature_selected', function (event, feature) {
                    $scope.showInfo(feature);
                })

                $scope.$on('popupOpened', function (e, source) {
                    if (angular.isDefined(source) && source != "inside" && angular.isDefined(popup)) popup.hide();
                })

                $scope.$on('query.dataUpdated', function (event) {
                    if (console) console.log('Attributes', QueryService.data.attributes, 'Groups', QueryService.data.groups);
                });

                Core.setMainPanel('info');
                Core.panelEnabled('compositions', false);
                Core.panelEnabled('status_creator', false);
            }
        ]);

        return module;
    });
<commit_msg>Dont display osm by default in produce3d example<commit_after>'use strict';

define(['angular', 'ol', 'olus', 'zones', 'pois', 'sidebar', 'toolbar', 'layermanager', 'SparqlJson', 'map', 'query', 'search', 'print', 'permalink', 'measure', 'legend', 'geolocation', 'core', 'api', 'angular-gettext', 'bootstrap', 'translations', 'compositions', 'status_creator', 'ows', 'cesium', 'cesiumjs'],

    function (angular, ol, olus, zones, pois, sidebar, toolbar, layermanager, SparqlJson) {
        var module = angular.module('hs', [
            'hs.toolbar',
            'hs.layermanager',
            'hs.map',
            'hs.query',
            'hs.search', 'hs.permalink', 'hs.measure',
            'hs.geolocation', 'hs.core',
            'hs.api',
            'gettext',
            'hs.sidebar',
            'hs.cesium'
        ]);

        module.directive('hs', ['hs.map.service', 'Core', '$compile', '$timeout', function (OlMap, Core, $compile, $timeout) {
            return {
                templateUrl: hsl_path + 'hslayers.html',
                link: function (scope, element) {
                    angular.element('.page-content', element).append($compile('

')(scope));
                    $timeout(function () {
                        Core.fullScreenMap(element)
                    }, 0);
                }
            };
        }])

            .directive('hs.foodiezones.infoDirective', function () {
                return {
                    templateUrl: './info.html?bust=' + gitsha,
                    link: function (scope, element, attrs) {
                        $('#zone-info-dialog').modal('show');
                    }
                };
            }).directive('description', ['$compile', 'hs.utils.service', function ($compile, utils) {
                return {
                    templateUrl: './description.html?bust=' + gitsha,
                    scope: {
                        object: '=',
                        url: '@'
                    },
                    link: function (scope, element, attrs) {
                        scope.describe = function (e, attribute) {
                            if (angular.element(e.target).parent().find('table').length > 0) {
                                angular.element(e.target).parent().find('table').remove();
                            } else {
                                var table = angular.element('

');
                                angular.element(e.target).parent().append(table);
                                $compile(table)(scope.$parent);
                            }
                        }
                        if (angular.isUndefined(scope.object) && angular.isDefined(attrs.url) && typeof attrs.url == 'string') {
                            scope.object = { attributes: [] };
                            var q = 'https://www.foodie-cloud.org/sparql?default-graph-uri=&query=' + encodeURIComponent('describe <' + attrs.url + '>') + '&should-sponge=&format=application%2Fsparql-results%2Bjson&timeout=0&debug=on';
                            $.ajax({
                                url: utils.proxify(q)
                            })
                                .done(function (response) {
                                    if (angular.isUndefined(response.results)) return;
                                    for (var i = 0; i < response.results.bindings.length; i++) {
                                        var b = response.results.bindings[i];
                                        var short_name = b.p.value;
                                        if (short_name.indexOf('#') > -1)
                                            short_name = short_name.split('#')[1];
                                        scope.object.attributes.push({ short_name: short_name, value: b.o.value });
                                        if (!scope.$$phase) scope.$apply();
                                    }
                                })
                        }
                    }
                };
            }]);

        module.value('config', {
            default_layers: [new ol.layer.Tile({
                source: new ol.source.OSM({
                    wrapX: false
                }),
                title: "Base layer",
                visible: false,
                base: true
            })],
            //project_name: 'hslayers',
            default_view: new ol.View({
                center: [-8.796119, 41.942791],
                zoom: 16,
                units: "m",
                projection: 'EPSG:4326'
            })
        });

        module.controller('Main', ['$scope', 'Core', 'hs.query.baseService', 'hs.compositions.service_parser', '$timeout', 'hs.map.service', '$http', 'config', '$rootScope', 'hs.utils.service', '$compile', 'hs.query.wmsService', '$sce',
            function ($scope, Core, QueryService, composition_parser, $timeout, hsMap, $http, config, $rootScope, utils, $compile, WmsService, $sce) {
                if (console) console.log("Main called");
                $scope.hsl_path = hsl_path; //Get this from hslayers.js file
                $scope.Core = Core;
                Core.sidebarExpanded = false;
                var map;
                var zones_source = new ol.source.Vector();
                var olus_source = new ol.source.Vector();
                var spoi_source = new ol.source.Vector();

                $rootScope.$on('map.loaded', function () {
                    map = hsMap.map;
                    olus.init($scope, $compile, map, utils);
                    zones.init($scope, $compile, map, utils);
                    pois.init($scope, $compile);
                    zones.get();
                    map.on('moveend', extentChanged);
                });

                function extentChanged() {
                    olus.get();
                }

                $rootScope.$on('map.sync_center', function (e, center, bounds) {
                    pois.getPois(map, utils, bounds);
                })

                config.default_layers.push(olus.createOluLayer());
                config.default_layers.push(zones.createLayer());
                config.default_layers.push(pois.createPoiLayer());

                $scope.showInfo = function (zone) {
                    var id, obj_type;
                    if (zone.properties['management zone']) { id = zone.properties['management zone'].getValue(); obj_type = 'Management Zone' }
                    if (zone.properties.poi) { id = zone.properties.poi.getValue(); obj_type = 'Point of interest' }
                    if (zone.properties.parcel) { id = zone.properties.parcel.getValue(); obj_type = 'Land use parcel' }
                    $scope.zone = {
                        id: $sce.trustAsHtml(),
                        attributes: [],
                        links: [],
                        obj_type: obj_type
                    };
                    describe(id, function () {
                        if (!$scope.$$phase) $scope.$apply();
                    });
                }

                function describe(id, callback) {
                    var q = 'https://www.foodie-cloud.org/sparql?default-graph-uri=&query=' + encodeURIComponent('describe <' + id + '>') + '&should-sponge=&format=application%2Fsparql-results%2Bjson&timeout=0&debug=on';
                    $.ajax({
                        url: utils.proxify(q)
                    })
                        .done(function (response) {
                            if (angular.isUndefined(response.results)) return;
                            for (var i = 0; i < response.results.bindings.length; i++) {
                                var b = response.results.bindings[i];
                                var short_name = b.p.value;
                                if (short_name.indexOf('#') > -1)
                                    short_name = short_name.split('#')[1];
                                $scope.zone.attributes.push({ short_name: short_name, value: b.o.value });
                            }
                            $scope.getLinksTo(id, callback);
                        })
                }

                $scope.getLinksTo = function (id, callback) {
                    var q = 'https://www.foodie-cloud.org/sparql?default-graph-uri=&query=' + encodeURIComponent('PREFIX geo: <http://www.opengis.net/ont/geosparql#> PREFIX geof: <http://www.opengis.net/def/function/geosparql/> PREFIX virtrdf: <http://www.openlinksw.com/schemas/virtrdf#> PREFIX poi: <http://www.openvoc.eu/poi#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> SELECT * WHERE {?obj <http://www.opengis.net/ont/geosparql#hasGeometry> ?obj_geom. ?obj_geom geo:asWKT ?Coordinates . FILTER(bif:st_intersects (?Coordinates, ?wkt)). { SELECT ?wkt WHERE { <' + id + '> geo:hasGeometry ?geometry. ?geometry geo:asWKT ?wkt.} } }') + '&should-sponge=&format=application%2Fsparql-results%2Bjson&timeout=0&debug=on';
                    $.ajax({
                        url: utils.proxify(q)
                    })
                        .done(function (response) {
                            for (var i = 0; i < response.results.bindings.length; i++) {
                                var b = response.results.bindings[i];
                                $scope.zone.links.push({ url: b.obj.value });
                            }
                            callback();
                        })
                }

                $scope.$on('infopanel.feature_selected', function (event, feature) {
                    $scope.showInfo(feature);
                })

                $scope.$on('popupOpened', function (e, source) {
                    if (angular.isDefined(source) && source != "inside" && angular.isDefined(popup)) popup.hide();
                })

                $scope.$on('query.dataUpdated', function (event) {
                    if (console) console.log('Attributes', QueryService.data.attributes, 'Groups', QueryService.data.groups);
                });

                Core.setMainPanel('info');
                Core.panelEnabled('compositions', false);
                Core.panelEnabled('status_creator', false);
            }
        ]);

        return module;
    });
<|endoftext|>


<commit_before><commit_msg>Prevent the reading of paths outside the blog folder<commit_after><|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Initializes phet globals that are used by all simulations, including assertions, arch and query-parameters.
 * See https://github.com/phetsims/phetcommon/issues/23
 *
 * PhET Simulations can be launched with query parameters which enable certain features.  To use a query parameter,
 * provide the full URL of the simulation and append a question mark (?) then the query parameter (and optionally its
 * value assignment).  For instance:
 * http://www.colorado.edu/physics/phet/dev/html/reactants-products-and-leftovers/1.0.0-dev.13/reactants-products-and-leftovers_en.html?dev
 *
 * Here is an example of a value assignment:
 * http://www.colorado.edu/physics/phet/dev/html/reactants-products-and-leftovers/1.0.0-dev.13/reactants-products-and-leftovers_en.html?webgl=false
 *
 * To use multiple query parameters, specify the question mark before the first query parameter, then ampersands (&)
 * between other query parameters.  Here is an example of multiple query parameters:
 * http://www.colorado.edu/physics/phet/dev/html/reactants-products-and-leftovers/1.0.0-dev.13/reactants-products-and-leftovers_en.html?dev&showPointerAreas&webgl=false
 *
 * For more on query parameters, please see http://en.wikipedia.org/wiki/Query_string
 *
 * Query parameters most useful for QA Testing:
 *
 * dev - enable developer-only features, such as showing the layout bounds
 * ea - enable assertions, internal code error checks
 * fuzzMouse - randomly sends mouse events to sim
 * profiler - shows profiling information for the sim
 * showPointerAreas - touch areas in red, mouse areas in blue, both dotted outlines
 * webgl - can be set to false with ?webgl=false to turn off WebGL rendering, see https://github.com/phetsims/scenery/issues/289
 *
 * Other query parameters:
 *
 * accessibility - enable accessibility features, such as keyboard navigation (mileage may vary!)
 * eall - enable all assertions, as above but with more time consuming checks
 * joistRenderer - specify a renderer for Joist to use, such as 'svg', 'webgl' or 'canvas'
 * locale - test with a specific locale
 * playbackInputEventLog - plays event logging back from the server, provide an optional name for the session
 * recordInputEventLog - enables input event logging, provide an optional name for the session, log is available via PhET menu
 * sceneryLog - list of one or more logs to enable in scenery 0.2+, delimited with .
 *                          - For example: ?sceneryLog=Display.Drawable.WebGLBlock
 * sceneryStringLog - Scenery logs will be output to a string instead of the window
 * screens - select one or more screens (with a 1-based index) to run in the sim, with a dot instead of a comma delimiter.
 *                          - For example ?screens=3.1 will launch with screen 1 and 3 with 3 first and 1 second.
 *                          - ?screens=2 would launch with just screen 2.
 *                          - Note that launching with a subset of screens can speed up the startup time significantly
 *                          - because only the selected screens are initialized
 * showHomeScreen - if false, go immediate to screenIndex, defaults to screenIndex=0
 * strings - override strings, value is JSON that is identical to string.json files
 * webglContextLossTimeout - if enabled, will create WebGL contexts that can simulate context loss
 *                         - if a value is specified, it will also simulate a context loss after the specified number
 *                         - of milliseconds has elapsed.
 *                         - The value can be omitted to manually simulate the context loss with simScene.simulateWebGLContextLoss()
 * webglContextLossIncremental - if this option is present, it will put the WebGLLayer into a testing mode which
 *                             - simulates context loss between successively increasing gl calls (starting at 1)
 *                             - this option should be used in conjunction with webglContextLossTimeout since
 *                             - it only triggers upon the first context lass.
 *
 * This file reads query parameters from browser window's URL.
 * This file must be loaded before requirejs is started up, and this file cannot be loaded as an AMD module.
 * The easiest way to do this is via a <script> tag in your HTML file.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Chris Malley (PixelZoom, Inc.)
 */
(function() {
  'use strict';

  //If arch has already been preloaded, then this script does nothing.
  //If arch has not been preloaded, then this will assign window.arch = null
  //This will enable us to use a pattern like `arch && arch.method`
  window.arch = window.arch || null;

// Initialize query parameters, see docs above
  (function() {
    'use strict';

    // Create the attachment point for all PhET globals
    window.phet = window.phet || {};
    window.phet.chipper = window.phet.chipper || {};

    //Pre-populate the query parameters map so that multiple subsequent look-ups are fast
    var queryParamsMap = {};

    if ( typeof window !== 'undefined' && window.location.search ) {
      var params = window.location.search.slice( 1 ).split( '&' );
      for ( var i = 0; i < params.length; i++ ) {
        var nameValuePair = params[ i ].split( '=' );
        queryParamsMap[ nameValuePair[ 0 ] ] = decodeURIComponent( nameValuePair[ 1 ] );
      }
    }

    /**
     * Retrieves the first occurrence of a query parameter based on its key.
     * Returns undefined if the query parameter is not found.
     * @param {string} key
     * @return {string}
     */
    window.phet.chipper.getQueryParameter = function( key ) {
      return queryParamsMap[ key ];
    };

    /**
     * Gets the cache buster args based on the provided query parameters.  Dy default it is:
     * ?bust=<number>
     * But this can be omitted if ?cacheBuster=false is provided
     * See https://github.com/phetsims/joist/issues/196
     * @returns {string}
     */
    window.phet.chipper.getCacheBusterArgs = function() {
      return (phet.chipper.getQueryParameter( 'cacheBuster' ) !== 'false') ? ('bust=' + Date.now()) : '';
    };
  }());
  /**
   * Enables or disables assertions in common libraries using query parameters.
   * There are two types of assertions: basic and slow. Enabling slow assertions will adversely impact performance.
   * 'ea' enables basic assertions, 'eall' enables basic and slow assertions.
   * Must be run before RequireJS, and assumes that assert.js and query-parameters.js has been run.
   */
  (function() {
    'use strict';

    // TODO: separate this logic out into a more common area?
    var isProduction = $( 'meta[name=phet-sim-level]' ).attr( 'content' ) === 'production';

    var enableAllAssertions = !isProduction && !!phet.chipper.getQueryParameter( 'eall' ); // enables all assertions (basic and slow)
    var enableBasicAssertions = enableAllAssertions || ( !isProduction && !!phet.chipper.getQueryParameter( 'ea' ) );  // enables basic assertions

    if ( enableBasicAssertions ) {
      window.assertions.enableAssert();
    }
    if ( enableAllAssertions ) {
      window.assertions.enableAssertSlow();
    }

    // Communicate sim errors to joist/tests/test-sims.html
    if ( phet.chipper.getQueryParameter( 'postMessageOnError' ) ) {
      window.addEventListener( 'error', function( a, b, c, d, e ) {
        var message = '';
        var stack = '';
        if ( a && a.message ) {
          message = a.message;
        }
        if ( a && a.error && a.error.stack ) {
          stack = a.error.stack;
        }
        window.parent && window.parent.postMessage( JSON.stringify( {
          type: 'error',
          url: window.location.href,
          message: message,
          stack: stack
        } ), '*' );
      } );
    }
  }());
}());
<commit_msg>Removed extra 'use strict' statements.<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Initializes phet globals that are used by all simulations, including assertions, arch and query-parameters.
 * See https://github.com/phetsims/phetcommon/issues/23
 *
 * PhET Simulations can be launched with query parameters which enable certain features.  To use a query parameter,
 * provide the full URL of the simulation and append a question mark (?) then the query parameter (and optionally its
 * value assignment).  For instance:
 * http://www.colorado.edu/physics/phet/dev/html/reactants-products-and-leftovers/1.0.0-dev.13/reactants-products-and-leftovers_en.html?dev
 *
 * Here is an example of a value assignment:
 * http://www.colorado.edu/physics/phet/dev/html/reactants-products-and-leftovers/1.0.0-dev.13/reactants-products-and-leftovers_en.html?webgl=false
 *
 * To use multiple query parameters, specify the question mark before the first query parameter, then ampersands (&)
 * between other query parameters.  Here is an example of multiple query parameters:
 * http://www.colorado.edu/physics/phet/dev/html/reactants-products-and-leftovers/1.0.0-dev.13/reactants-products-and-leftovers_en.html?dev&showPointerAreas&webgl=false
 *
 * For more on query parameters, please see http://en.wikipedia.org/wiki/Query_string
 *
 * Query parameters most useful for QA Testing:
 *
 * dev - enable developer-only features, such as showing the layout bounds
 * ea - enable assertions, internal code error checks
 * fuzzMouse - randomly sends mouse events to sim
 * profiler - shows profiling information for the sim
 * showPointerAreas - touch areas in red, mouse areas in blue, both dotted outlines
 * webgl - can be set to false with ?webgl=false to turn off WebGL rendering, see https://github.com/phetsims/scenery/issues/289
 *
 * Other query parameters:
 *
 * accessibility - enable accessibility features, such as keyboard navigation (mileage may vary!)
 * eall - enable all assertions, as above but with more time consuming checks
 * joistRenderer - specify a renderer for Joist to use, such as 'svg', 'webgl' or 'canvas'
 * locale - test with a specific locale
 * playbackInputEventLog - plays event logging back from the server, provide an optional name for the session
 * recordInputEventLog - enables input event logging, provide an optional name for the session, log is available via PhET menu
 * sceneryLog - list of one or more logs to enable in scenery 0.2+, delimited with .
 *                          - For example: ?sceneryLog=Display.Drawable.WebGLBlock
 * sceneryStringLog - Scenery logs will be output to a string instead of the window
 * screens - select one or more screens (with a 1-based index) to run in the sim, with a dot instead of a comma delimiter.
 *                          - For example ?screens=3.1 will launch with screen 1 and 3 with 3 first and 1 second.
 *                          - ?screens=2 would launch with just screen 2.
 *                          - Note that launching with a subset of screens can speed up the startup time significantly
 *                          - because only the selected screens are initialized
 * showHomeScreen - if false, go immediate to screenIndex, defaults to screenIndex=0
 * strings - override strings, value is JSON that is identical to string.json files
 * webglContextLossTimeout - if enabled, will create WebGL contexts that can simulate context loss
 *                         - if a value is specified, it will also simulate a context loss after the specified number
 *                         - of milliseconds has elapsed.
 *                         - The value can be omitted to manually simulate the context loss with simScene.simulateWebGLContextLoss()
 * webglContextLossIncremental - if this option is present, it will put the WebGLLayer into a testing mode which
 *                             - simulates context loss between successively increasing gl calls (starting at 1)
 *                             - this option should be used in conjunction with webglContextLossTimeout since
 *                             - it only triggers upon the first context lass.
 *
 * This file reads query parameters from browser window's URL.
 * This file must be loaded before requirejs is started up, and this file cannot be loaded as an AMD module.
 * The easiest way to do this is via a <script> tag in your HTML file.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 * @author Chris Malley (PixelZoom, Inc.)
 */
(function() {
  'use strict';

  //If arch has already been preloaded, then this script does nothing.
  //If arch has not been preloaded, then this will assign window.arch = null
  //This will enable us to use a pattern like `arch && arch.method`
  window.arch = window.arch || null;

// Initialize query parameters, see docs above
  (function() {

    // Create the attachment point for all PhET globals
    window.phet = window.phet || {};
    window.phet.chipper = window.phet.chipper || {};

    //Pre-populate the query parameters map so that multiple subsequent look-ups are fast
    var queryParamsMap = {};

    if ( typeof window !== 'undefined' && window.location.search ) {
      var params = window.location.search.slice( 1 ).split( '&' );
      for ( var i = 0; i < params.length; i++ ) {
        var nameValuePair = params[ i ].split( '=' );
        queryParamsMap[ nameValuePair[ 0 ] ] = decodeURIComponent( nameValuePair[ 1 ] );
      }
    }

    /**
     * Retrieves the first occurrence of a query parameter based on its key.
     * Returns undefined if the query parameter is not found.
     * @param {string} key
     * @return {string}
     */
    window.phet.chipper.getQueryParameter = function( key ) {
      return queryParamsMap[ key ];
    };

    /**
     * Gets the cache buster args based on the provided query parameters.  Dy default it is:
     * ?bust=<number>
     * But this can be omitted if ?cacheBuster=false is provided
     * See https://github.com/phetsims/joist/issues/196
     * @returns {string}
     */
    window.phet.chipper.getCacheBusterArgs = function() {
      return (phet.chipper.getQueryParameter( 'cacheBuster' ) !== 'false') ? ('bust=' + Date.now()) : '';
    };
  }());
  /**
   * Enables or disables assertions in common libraries using query parameters.
   * There are two types of assertions: basic and slow. Enabling slow assertions will adversely impact performance.
   * 'ea' enables basic assertions, 'eall' enables basic and slow assertions.
   * Must be run before RequireJS, and assumes that assert.js and query-parameters.js has been run.
   */
  (function() {

    // TODO: separate this logic out into a more common area?
    var isProduction = $( 'meta[name=phet-sim-level]' ).attr( 'content' ) === 'production';

    var enableAllAssertions = !isProduction && !!phet.chipper.getQueryParameter( 'eall' ); // enables all assertions (basic and slow)
    var enableBasicAssertions = enableAllAssertions || ( !isProduction && !!phet.chipper.getQueryParameter( 'ea' ) );  // enables basic assertions

    if ( enableBasicAssertions ) {
      window.assertions.enableAssert();
    }
    if ( enableAllAssertions ) {
      window.assertions.enableAssertSlow();
    }

    // Communicate sim errors to joist/tests/test-sims.html
    if ( phet.chipper.getQueryParameter( 'postMessageOnError' ) ) {
      window.addEventListener( 'error', function( a, b, c, d, e ) {
        var message = '';
        var stack = '';
        if ( a && a.message ) {
          message = a.message;
        }
        if ( a && a.error && a.error.stack ) {
          stack = a.error.stack;
        }
        window.parent && window.parent.postMessage( JSON.stringify( {
          type: 'error',
          url: window.location.href,
          message: message,
          stack: stack
        } ), '*' );
      } );
    }
  }());
}());
<|endoftext|>


<commit_before>import peerflix from 'peerflix';
import path from 'path';
import remote from 'remote';
import parseTorrent from 'parse-torrent';
import readTorrent from 'read-torrent';
import Promise from 'bluebird';
import getPort from 'get-port';
import torrentActions from '../../actions/torrentActions';

let temp = path.join(remote.require('app').getPath('temp'), 'Powder-Player');

module.exports = {
    init(torrent) {
        return new Promise((resolve, reject) => {
            Promise.all([this.read(torrent), getPort()])
                .spread((torrentInfo, port) => {
                    resolve(peerflix(torrentInfo, {
                        tracker: true,
                        port: port,
                        tmp: temp,
                        buffer: (1.5 * 1024 * 1024).toString()
                    }));
                })
                .catch(reject);
        });
    },
    destroy(infoHash) {
        if (this.streams[infoHash]) {
            if (this.streams[infoHash].server._handle) {
                this.streams[infoHash].server.close();
            }
            this.streams[infoHash].destroy();
        }
    },
    read(torrent) {
        return new Promise((resolve, reject) => {
            readTorrent(torrent, (err, parsedTorrent) => {
                return (err || !parsedTorrent) ? reject(err) : resolve(parsedTorrent);
            });
        });
    }
};<commit_msg>lalala<commit_after>import peerflix from 'peerflix';
import path from 'path';
import remote from 'remote';
import readTorrent from 'read-torrent';
import Promise from 'bluebird';
import getPort from 'get-port';
import torrentActions from '../../actions/torrentActions';

let temp = path.join(remote.require('app').getPath('temp'), 'Powder-Player');

module.exports = {
    init(torrent) {
        return new Promise((resolve, reject) => {
            Promise.all([this.read(torrent), getPort()])
                .spread((torrentInfo, port) => {
                    resolve(peerflix(torrentInfo, {
                        tracker: true,
                        port: port,
                        tmp: temp,
                        buffer: (1.5 * 1024 * 1024).toString()
                    }));
                })
                .catch(reject);
        });
    },
    destroy(infoHash) {
        if (this.streams[infoHash]) {
            if (this.streams[infoHash].server._handle) {
                this.streams[infoHash].server.close();
            }
            this.streams[infoHash].destroy();
        }
    },
    read(torrent) {
        return new Promise((resolve, reject) => {
            readTorrent(torrent, (err, parsedTorrent) => {
                return (err || !parsedTorrent) ? reject(err) : resolve(parsedTorrent);
            });
        });
    }
};<|endoftext|>


<commit_before>'use strict';

import * as _ from 'lodash'
import {courseCache} from './db'
import buildQueryFromString from './queryStuff'

function getCourse(clbid) {
	let course = courseCache[clbid]

	if (!course) {
		console.warn('course retrieval failed for: ' + clbid)
	}

	return _.cloneDeep(course)
}

function getCourses(clbids) {
	// Takes a list of clbids, and returns a list of the course objects for
	// those clbids.
	return _.map(clbids, getCourse)
}

function deptNumToCrsid(deptNumString) {
	let result = _.find(courseCache.courses, {deptnum: deptNumString})
	if (result) {
		return result.crsid
	} else {
		console.warn('Course ' + deptNumString + ' was not found')
	}
}

function checkCoursesForDeptNum(courses, deptNumString) {
	var crsidsToCheckAgainst = _.chain(courses).pluck('crsid').uniq().value()

	let crsid = deptNumToCrsid(deptNumString)
	return _.contains(crsidsToCheckAgainst, crsid)
}

function checkCoursesFor(courses, filter) {
	return _.any(courses, filter)
}

////
// from queryCourses

// 'dept: Computer Science  dept: Asian Studies  name: Parallel  level: 300  year: $OR year:2013 year: 2014'
// { depts: [ '$AND', 'CSCI', 'ASIAN' ],
//   title: [ 'Parallel' ],
//   level: [ 300 ],
//   year: [ '$OR', 2013, 2014 ] }

// 'dept: ASIAN  Dept: Religion  title: "Japan*"  LEVEL: 200  year: 2014  semester: $OR  semester: 3  semester: 1'
// { depts: [ '$AND', 'ASIAN', 'REL' ],
//   title: [ '"Japan*"' ],
//   level: [ 200 ],
//   year: [ 2014 ],
//   semester: [ '$OR', 3, 1 ] }

// 'department: American Conversations  name: Independence  year: 2014  time: Tuesdays after 12'
// { depts: [ 'AMCON' ],
//   title: [ 'Independence' ],
//   year: [ 2014 ],
//   time: [ 'Tuesdays after 12' ] }

// 'ges: $AND  geneds: history of western culture gened: HBS  semester: Spring  year: 2014'
// { geneds: [ '$AND', 'HWC', 'HBS' ],
//   semester: [ 3 ],
//   year: [ 2014 ] }

function queryCourses(queryString) {
	let query = buildQueryFromString(queryString)
	var results = _(courseCache)
		.filter(course => {
			let matches = _.map(queryString, (val, key) => {
				let matches = [];

				let AND = false;
				let OR = false;
				let substring = false;

				// val is either:
				// - a 1-long array
				// - an $AND query
				// - an $OR query
				// - one of the above, but substring

				if (val.length === 1) {
					matches.push(course[key] === val[0])
				} else if (val[0] === '$OR') {
					OR = true
				} else if (val[0] === '$AND') {
					AND = true
				}

				if (_.contains(['title', 'name', 'description', 'notes'], key)) {
					substring = true;
				}

				let values = _.tail(val);

				matches = matches.concat(_.map(values, (v) => {
					// dept, gereqs, etc.
					if (_.isArray(course[key])) {
						return _.contains(course[key], v)
					}
					return course[key] === v;
				}))

				if (OR)  return _.some(matches)
				return _.all(matches)
			})
			return _.all(matches)
		})
		.map(_.cloneDeep)
		.sortBy('title')
		.groupBy('term')
		.value()
	return results
}

export {
	getCourse,
	getCourses,
	queryCourses,

	deptNumToCrsid,
	checkCoursesForDeptNum,
	checkCoursesFor
}

window.courses = {
	getCourse, getCourses,
	queryCourses,

	deptNumToCrsid,
	checkCoursesForDeptNum,
	checkCoursesFor,
}
<commit_msg>Fix up some stuff in course->query<commit_after>'use strict';

import * as _ from 'lodash'
import {courseCache} from './db'
import buildQueryFromString from './queryStuff'

function getCourse(clbid) {
	let course = courseCache[clbid]

	if (!course) {
		console.warn('course retrieval failed for: ' + clbid)
	}

	return _.cloneDeep(course)
}

function getCourses(clbids) {
	// Takes a list of clbids, and returns a list of the course objects for
	// those clbids.
	return _.map(clbids, getCourse)
}

function deptNumToCrsid(deptNumString) {
	let result = _.find(courseCache.courses, {deptnum: deptNumString})
	if (result) {
		return result.crsid
	} else {
		console.warn('Course ' + deptNumString + ' was not found')
	}
}

function checkCoursesForDeptNum(courses, deptNumString) {
	var crsidsToCheckAgainst = _.chain(courses).pluck('crsid').uniq().value()

	let crsid = deptNumToCrsid(deptNumString)
	return _.contains(crsidsToCheckAgainst, crsid)
}

function checkCoursesFor(courses, filter) {
	return _.any(courses, filter)
}

////
// from queryCourses

// 'dept: Computer Science  dept: Asian Studies  name: Parallel  level: 300  year: $OR year:2013 year: 2014'
// { depts: [ '$AND', 'CSCI', 'ASIAN' ],
//   title: [ 'Parallel' ],
//   level: [ 300 ],
//   year: [ '$OR', 2013, 2014 ] }

// 'dept: ASIAN  Dept: Religion  title: "Japan*"  LEVEL: 200  year: 2014  semester: $OR  semester: 3  semester: 1'
// { depts: [ '$AND', 'ASIAN', 'REL' ],
//   title: [ '"Japan*"' ],
//   level: [ 200 ],
//   year: [ 2014 ],
//   semester: [ '$OR', 3, 1 ] }

// 'department: American Conversations  name: Independence  year: 2014  time: Tuesdays after 12'
// { depts: [ 'AMCON' ],
//   title: [ 'Independence' ],
//   year: [ 2014 ],
//   time: [ 'Tuesdays after 12' ] }

// 'ges: $AND  geneds: history of western culture gened: HBS  semester: Spring  year: 2014'
// { geneds: [ '$AND', 'HWC', 'HBS' ],
//   semester: [ 3 ],
//   year: [ 2014 ] }

function queryCourses(queryString) {
	let query = buildQueryFromString(queryString)
	console.log('query:', query)
	var results = _(courseCache)
		.filter(course => {
			let matches = _.map(query, (values, key) => {
				let AND = false;
				let OR = false;
				let substring = false;

				// values is either:
				// - a 1-long array
				// - an $AND query
				// - an $OR query
				// - one of the above, but substring

				if (values[0] === '$OR') {
					OR = true
				} else if (values[0] === '$AND') {
					AND = true
				}

				if (AND || OR) {
					values = _.tail(values);
				}

				if (_.contains(['title', 'name', 'description', 'notes'], key)) {
					substring = true;
				}

				let internalMatches = _.map(values, (v) => {
					// dept, gereqs, etc.
					if (_.isArray(course[key]) || substring) {
						return _.contains(course[key], v)
					}
					return course[key] === v;
				})

				return OR ? _.some(internalMatches) : _.all(internalMatches)
			})
			return _.all(matches)
		})
		.map(_.cloneDeep)
		.sortBy('title')
		.groupBy('term')
		.value()
	return results
}

export {
	getCourse,
	getCourses,
	queryCourses,

	deptNumToCrsid,
	checkCoursesForDeptNum,
	checkCoursesFor
}

window.courses = {
	getCourse, getCourses,
	queryCourses,

	deptNumToCrsid,
	checkCoursesForDeptNum,
	checkCoursesFor,
}
<|endoftext|>


<commit_before>// Convert an element into a 3D laptop, moving its contents into the screen.

class Laptop {
  constructor(elem, index, dummy = false) {
    this.elem = elem;
    this.index = index;
    this.dummy = dummy;

    this._translateX = this._translateY = this._translateZ = 0;
    this._rotateX = this._rotateY = this._rotateZ = 0;

    this.elem.classList.add('laptop3d');
    if (this.dummy) this.elem.classList.add('dummy');
    if (typeof this.index !== 'undefined') this.elem.setAttribute('data-laptop3d-index', this.index);

    const children = [...this.elem.childNodes];
    this.elem.innerHTML = `

    `;
    // Record some common elements as properties
    ['wrapper', 'base', 'lid', 'screen'].forEach((name) => {
      this[name] = this.elem.getElementsByClassName(name)[0];
    });
    // Add all former children to the new screen element
    children.forEach(child => this.screen.appendChild(child));

    // Transform
    this.elem.style.transform = `translateX(50vw) translateX(-50%) rotateX(80deg) rotateZ(45deg) translateX(${30 * this.index}vw)`;

    // Lift up a bit on hover
    if (!this.dummy) {
      this.elem.addEventListener('mouseenter', () => { this.rotateZ = -90; });
      this.elem.addEventListener('mouseleave', () => { this.rotateZ = 0; });
    }

    this._applyTransform();
  }

  // Allow getting and setting transformation properties individually
  _applyTransform() {
    this.wrapper.style.transform = [
      `rotateX(${this._rotateX})`,
      `rotateY(${this._rotateY})`,
      `rotateZ(${this._rotateZ})`,
      `translateX(${this._translateX})`,
      `translateY(${this._translateY})`,
      `translateZ(${this._translateZ})`,
    ].join(' ');
  }

  get translateX() { return this._translateX; }
  get translateY() { return this._translateY; }
  get translateZ() { return this._translateZ; }
  get rotateX() { return this._rotateX; }
  get rotateY() { return this._rotateY; }
  get rotateZ() { return this._rotateZ; }

  set translateX(val) { this._translateX = val; this._applyTransform(); }
  set translateY(val) { this._translateY = val; this._applyTransform(); }
  set translateZ(val) { this._translateZ = val; this._applyTransform(); }

  _setRotate(axis, val) {
    if (!['X', 'Y', 'Z'].includes(axis)) throw new Error('Axis must be one of X Y or Z'); // val must be XYZ
    const prop = `_rotate${axis}`; // Property that we're setting
    if (typeof val === 'string' && val.endsWith('deg')) this[prop] = val; // Don't change if the value is already a string ending in 'deg'
    else this[prop] = `${val}deg`; // Otherwise add the 'deg' suffix
    this._applyTransform();
  }
  set rotateX(val) { this._setRotate('X', val); }
  set rotateY(val) { this._setRotate('Y', val); }
  set rotateZ(val) { this._setRotate('Z', val); }
}

class LaptopCarousel {
  constructor(container, laptops) {
    this.container = container;
    this.laptops = laptops;
    // Sort by index
    this.laptops.sort((a, b) => a.index - b.index);
    // Check that there are no overlapping indices
    const indices = this.laptops.map(l => l.index);
    if (indices.length !== new Set(indices).size) throw new Error('LaptopCarousel indices must be unique');
    // Record this as the carousel for each laptop
    laptops.forEach((l) => { l.carousel = this; });
    // Build map of laptops by index
    this.laptopsByIndex = {};
    laptops.forEach((l) => { this.laptopsByIndex[l.index] = l; });
  }

  // All indices represented
  get indices() { return this.laptops.map(l => l.index); }
  // Minimum index represented
  get minIndex() { return Math.min(...this.indices); }
  // Maximum index represented
  get maxIndex() { return Math.max(...this.indices); }

  }

  // Maximum index represented
  get maxIndex() {
    return Math.max(...this.laptops.map(l => l.index));
  }

  scroll(progress) {
    this.progress = progress;
  }
}

// Set up laptop scene
document.addEventListener('DOMContentLoaded', () => {
  // Convert all elements with the class 'laptop3d' into laptops
  const laptopsContainer = document.getElementsByClassName('laptops-container')[0];
  const laptopElems = [...laptopsContainer.getElementsByClassName('laptop3d')];
  const laptops = laptopElems.map((l, i) => new Laptop(l, i));
  // Add 10 dummy laptops to the beginning
  const dummyLaptops = new Array(10)
    .fill(0)
    .map(() => document.createElement('div'))
    .map(l => laptopsContainer.appendChild(l))
    .map((l, i) => new Laptop(l, -(i + 1), true));

  window.carousel = new LaptopCarousel(laptopsContainer, laptops.concat(dummyLaptops));
});
<commit_msg>Allow easily adjusting laptop lid angle<commit_after>// Convert an element into a 3D laptop, moving its contents into the screen.

class Laptop {
  constructor(elem, index, dummy = false) {
    this.elem = elem;
    this.index = index;
    this.dummy = dummy;

    this._translateX = this._translateY = this._translateZ = 0;
    this._rotateX = this._rotateY = this._rotateZ = 0;
    this._lidAngle = 90;

    this.elem.classList.add('laptop3d');
    if (this.dummy) this.elem.classList.add('dummy');
    if (typeof this.index !== 'undefined') this.elem.setAttribute('data-laptop3d-index', this.index);

    const children = [...this.elem.childNodes];
    this.elem.innerHTML = `

    `;
    // Record some common elements as properties
    ['wrapper', 'base', 'lid', 'screen'].forEach((name) => {
      this[name] = this.elem.getElementsByClassName(name)[0];
    });
    // Add all former children to the new screen element
    children.forEach(child => this.screen.appendChild(child));

    // Transform
    this.elem.style.transform = `translateX(50vw) translateX(-50%) rotateX(80deg) rotateZ(45deg) translateX(${30 * this.index}vw)`;

    // Lift up a bit on hover
    if (!this.dummy) {
      this.elem.addEventListener('mouseenter', () => { this.rotateZ = -90; });
      this.elem.addEventListener('mouseleave', () => { this.rotateZ = 0; });
    }

    this._applyTransform();
  }

  _applyTransform() {
    this.wrapper.style.transform = [
      `rotateX(${this._rotateX})`,
      `rotateY(${this._rotateY})`,
      `rotateZ(${this._rotateZ})`,
      `translateX(${this._translateX})`,
      `translateY(${this._translateY})`,
      `translateZ(${this._translateZ})`,
    ].join(' ');
    this.lid.style.transform = `translateY(-33.333333vw) rotateX(${-180 + this._lidAngle}deg)`;
  }

  // Translation

  get translateX() { return this._translateX; }
  get translateY() { return this._translateY; }
  get translateZ() { return this._translateZ; }

  set translateX(val) { this._translateX = val; this._applyTransform(); }
  set translateY(val) { this._translateY = val; this._applyTransform(); }
  set translateZ(val) { this._translateZ = val; this._applyTransform(); }

  // Rotation
  get rotateX() { return this._rotateX; }
  get rotateY() { return this._rotateY; }
  get rotateZ() { return this._rotateZ; }

  _setRotate(axis, val) {
    if (!['X', 'Y', 'Z'].includes(axis)) throw new Error('Axis must be one of X Y or Z'); // val must be XYZ
    const prop = `_rotate${axis}`; // Property that we're setting
    if (typeof val === 'string' && val.endsWith('deg')) this[prop] = val; // Don't change if the value is already a string ending in 'deg'
    else this[prop] = `${val}deg`; // Otherwise add the 'deg' suffix
    this._applyTransform();
  }
  set rotateX(val) { this._setRotate('X', val); }
  set rotateY(val) { this._setRotate('Y', val); }
  set rotateZ(val) { this._setRotate('Z', val); }

  // Lid
  get lidAngle() { return this._lidAngle; }
  set lidAngle(val) { this._lidAngle = val; this._applyTransform(); }

}

class LaptopCarousel {
  constructor(container, laptops) {
    this.container = container;
    this.laptops = laptops;
    // Sort by index
    this.laptops.sort((a, b) => a.index - b.index);
    // Check that there are no overlapping indices
    const indices = this.laptops.map(l => l.index);
    if (indices.length !== new Set(indices).size) throw new Error('LaptopCarousel indices must be unique');
    // Record this as the carousel for each laptop
    laptops.forEach((l) => { l.carousel = this; });
    // Build map of laptops by index
    this.laptopsByIndex = {};
    laptops.forEach((l) => { this.laptopsByIndex[l.index] = l; });
  }

  // All indices represented
  get indices() { return this.laptops.map(l => l.index); }
  // Minimum index represented
  get minIndex() { return Math.min(...this.indices); }
  // Maximum index represented
  get maxIndex() { return Math.max(...this.indices); }

  }

  // Maximum index represented
  get maxIndex() {
    return Math.max(...this.laptops.map(l => l.index));
  }

  scroll(progress) {
    this.progress = progress;
  }
}

// Set up laptop scene
document.addEventListener('DOMContentLoaded', () => {
  // Convert all elements with the class 'laptop3d' into laptops
  const laptopsContainer = document.getElementsByClassName('laptops-container')[0];
  const laptopElems = [...laptopsContainer.getElementsByClassName('laptop3d')];
  const laptops = laptopElems.map((l, i) => new Laptop(l, i));
  // Add 10 dummy laptops to the beginning
  const dummyLaptops = new Array(10)
    .fill(0)
    .map(() => document.createElement('div'))
    .map(l => laptopsContainer.appendChild(l))
    .map((l, i) => new Laptop(l, -(i + 1), true));

  window.carousel = new LaptopCarousel(laptopsContainer, laptops.concat(dummyLaptops));
});
<|endoftext|>


<commit_before>/* eslint-env browser */
/* global CheckedEmitter */

'use strict';

// TODO: handle invalid grammar in textbox
// TODO: fix needed eamples bug (need force update to get rid of ident)

var ExampleWorker = require('worker!./exampleWorker-shim');
var ohmEditor = require('./ohmEditor');

var DEBUG = false;
var exampleWorkerManager = new CheckedEmitter();

exampleWorkerManager.registerEvents({
  'received:examples': ['ruleName', 'examples'],
  'received:neededExamples': ['neededExamples']
});

var eventsToEmit = ['received:examples', 'received:neededExamples'];

var exampleWorker = new ExampleWorker();

// TODO: may want to reset current worker instead

function resetWorker(grammar) {
  if (exampleWorker) {
    exampleWorker.terminate();
  }
  exampleWorker = new ExampleWorker();
  exampleWorker.onmessage = onWorkerMessage;
  exampleWorker.postMessage({
    name: 'initialize', recipe: grammar.toRecipe()
  });

  var examples = ohmEditor.examples.getExamples();
  Object.keys(examples).forEach(function(id) {
    var example = examples[id];
    var match;
    if (example.startRule in grammar.rules) {
      match = grammar.match(example.text, example.startRule);
    } else {
      match = grammar.match(example.text);
    }

    if (match.succeeded()) {
      exampleWorkerManager.addUserExample(example.startRule || grammar.defaultStartRule,
                                          example.text);
    }
  });
}

ohmEditor.addListener('parse:grammar', function(_, g, err) {
  if (!err) {
    resetWorker(g);
  }
});

ohmEditor.examples.addListener('set:example', function(_, oldValue, newValue) {
  var grammar;
  if (newValue.text.trim() === '') {
    return;
  } else if (oldValue && oldValue.text.trim() === '' ||
             !oldValue) {
    grammar = ohmEditor.grammar;
    if (grammar.match(newValue.text, newValue.startRule).succeeded()) {
      exampleWorkerManager.addUserExample(newValue.startRule || grammar.defaultStartRule,
                                          newValue.text);
    }
  } else {
    resetWorker(ohmEditor.grammar);
  }
});

ohmEditor.examples.addListener('remove:example', function(_) {
  resetWorker(ohmEditor.grammar);
});

function onWorkerMessage(event) {
  if (eventsToEmit.includes(event.data.name)) {
    exampleWorkerManager.emit.apply(exampleWorkerManager,
                                    [event.data.name].concat(event.data.args));
  } else if (DEBUG) {
    console.debug('WORKER:', event.data);  // eslint-disable-line no-console
  }
}

exampleWorkerManager.requestExamples = function(ruleName) {
  relayEvent('request:examples', [ruleName]);
};

exampleWorkerManager.updateNeededExamples = function() {
  relayEvent('update:neededExamples', []);
};

exampleWorkerManager.addUserExample = function(ruleName, example) {
  relayEvent('add:userExample', [ruleName, example]);
};

function relayEvent(eventName, args) {
  exampleWorker.postMessage({
    name: eventName,
    args: args
  });
}

exampleWorkerManager.neededExamples = null;
exampleWorkerManager.addListener('received:neededExamples', function(neededExamples) {
  exampleWorkerManager.neededExamples = neededExamples;

});

// Exports
// -------

module.exports = exampleWorkerManager;
<commit_msg>Example worker: prevent error when grammar has no rules.<commit_after>/* eslint-env browser */
/* global CheckedEmitter */

'use strict';

// TODO: handle invalid grammar in textbox
// TODO: fix needed eamples bug (need force update to get rid of ident)

var ExampleWorker = require('worker!./exampleWorker-shim');
var ohmEditor = require('./ohmEditor');

var DEBUG = false;
var exampleWorkerManager = new CheckedEmitter();

exampleWorkerManager.registerEvents({
  'received:examples': ['ruleName', 'examples'],
  'received:neededExamples': ['neededExamples']
});

var eventsToEmit = ['received:examples', 'received:neededExamples'];

var exampleWorker = new ExampleWorker();

// TODO: may want to reset current worker instead

function resetWorker(grammar) {
  if (exampleWorker) {
    exampleWorker.terminate();
  }
  exampleWorker = new ExampleWorker();
  exampleWorker.onmessage = onWorkerMessage;
  exampleWorker.postMessage({
    name: 'initialize', recipe: grammar.toRecipe()
  });

  var examples = ohmEditor.examples.getExamples();
  Object.keys(examples).forEach(function(id) {
    var example = examples[id];
    var startRule = example.startRule in grammar.rules ?
        example.startRule : grammar.defaultStartRule;
    if (startRule && grammar.match(example.text, startRule).succeeded()) {
      exampleWorkerManager.addUserExample(startRule, example.text);
    }
  });
}

ohmEditor.addListener('parse:grammar', function(_, g, err) {
  if (!err) {
    resetWorker(g);
  }
});

ohmEditor.examples.addListener('set:example', function(_, oldValue, newValue) {
  var grammar;
  if (newValue.text.trim() === '') {
    return;
  } else if (oldValue && oldValue.text.trim() === '' ||
             !oldValue) {
    grammar = ohmEditor.grammar;
    if (grammar.match(newValue.text, newValue.startRule).succeeded()) {
      exampleWorkerManager.addUserExample(newValue.startRule || grammar.defaultStartRule,
                                          newValue.text);
    }
  } else {
    resetWorker(ohmEditor.grammar);
  }
});

ohmEditor.examples.addListener('remove:example', function(_) {
  resetWorker(ohmEditor.grammar);
});

function onWorkerMessage(event) {
  if (eventsToEmit.includes(event.data.name)) {
    exampleWorkerManager.emit.apply(exampleWorkerManager,
                                    [event.data.name].concat(event.data.args));
  } else if (DEBUG) {
    console.debug('WORKER:', event.data);  // eslint-disable-line no-console
  }
}

exampleWorkerManager.requestExamples = function(ruleName) {
  relayEvent('request:examples', [ruleName]);
};

exampleWorkerManager.updateNeededExamples = function() {
  relayEvent('update:neededExamples', []);
};

exampleWorkerManager.addUserExample = function(ruleName, example) {
  relayEvent('add:userExample', [ruleName, example]);
};

function relayEvent(eventName, args) {
  exampleWorker.postMessage({
    name: eventName,
    args: args
  });
}

exampleWorkerManager.neededExamples = null;
exampleWorkerManager.addListener('received:neededExamples', function(neededExamples) {
  exampleWorkerManager.neededExamples = neededExamples;
});

// Exports
// -------

module.exports = exampleWorkerManager;
<|endoftext|>