<commit_before>define(function(require) {
    'use strict';

    var AttributeGroupComponent;
    var $ = require('jquery');
    var _ = require('underscore');
    var mediator = require('oroui/js/mediator');
    var BaseComponent = require('oroui/js/app/components/base/component');
    var Modal = require('oroui/js/modal');

    AttributeGroupComponent = BaseComponent.extend({

        /**
         * @property {Object}
         */
        options: {
            delimiter: ';'
        },

        /**
         * @property {String}
         */
        groupSelector: '[data-attribute-group]',

        /**
         * @property {String}
         */
        removeBtn: '.removeRow.btn-link',

        /**
         * @inheritDoc
         */
        initialize: function(options) {
            AttributeGroupComponent.__super__.initialize.call(this, options);

            this.options = _.defaults(options || {}, this.options);
            mediator.on('attribute-select:find-selected-attributes', this.onGetSelectedAttributes, this);

            var groups = this.getAllGroups();
            if (groups.length === 1) {
                this.hideRemoveButton(groups);
            } else {
                this.showRemoveButton(groups);
            }
            var self = this;
            $(this.options._sourceElement).parent().find(this.removeBtn).on('click', function (event) {
                var systemAttributesSelected = self.getAttributeSelect().find('option[locked="locked"]:selected');
                if (systemAttributesSelected.length) {
                    self.showConfirmModal(this);
                    return false;
                }
                return true;
            });
            //temporary width fix
            $(this.options._sourceElement).parents().find('.oro-item-collection .row-oro').width(960);
        },

        showConfirmModal: function (removeBtn) {
            var confirmDialog = new Modal({
                title:  _.__('oro.attribute.remove_confirmation_title'),
                content: _.__('oro.attribute.remove_confirmation_text'),
                className: 'modal oro-modal-danger'
            });

            confirmDialog.on('ok', function() {
                var item;
                var closest = '*[data-content]';
                if ($(removeBtn).data('closest')) {
                    closest = $(removeBtn).data('closest');
                }

                item = $(removeBtn).closest(closest);
                item.trigger('content:remove').remove();
                confirmDialog.close();
            });

            confirmDialog.open();
        },

        getAllGroups: function() {
            return $(this.groupSelector);
        },

        showRemoveButton: function(groups) {
            groups.parent().find(this.removeBtn).show();
        },

        hideRemoveButton: function(groups) {
            groups.parent().find(this.removeBtn).hide();
        },

        getAttributeSelect: function() {
            return $(this.options._sourceElement).find('[data-name="field__attribute-relations"]');
        },

        onGetSelectedAttributes: function(eventData) {
            var groupLabel = $(this.options._sourceElement).find('[data-attribute-select-group]').val();

            eventData.attributeSelects.push({groupLabel: groupLabel, attributesSelect: this.getAttributeSelect()});
        },

        dispose: function() {
            var groups = this.getAllGroups();
            if (groups.length === 2) { // Disable remove button for the last group
                this.hideRemoveButton(groups);
            }

            mediator.off('attribute-select:find-selected-attributes', this.onGetSelectedAttributes, this);
            mediator.trigger('attribute-group:remove', {
                attributeSelectFtid: this.getAttributeSelect().data('ftid'),
                firstGroup: groups.not(this.options._sourceElement).first()
            });

            AttributeGroupComponent.__super__.dispose.call(this);
        }
    });

    return AttributeGroupComponent;
});
<commit_msg>BB-6195: Create notification on removing attrribute group if it has system attributes - fix js styles<commit_after>define(function(require) {
    'use strict';

    var AttributeGroupComponent;
    var $ = require('jquery');
    var _ = require('underscore');
    var mediator = require('oroui/js/mediator');
    var BaseComponent = require('oroui/js/app/components/base/component');
    var Modal = require('oroui/js/modal');

    AttributeGroupComponent = BaseComponent.extend({

        /**
         * @property {Object}
         */
        options: {
            delimiter: ';'
        },

        /**
         * @property {String}
         */
        groupSelector: '[data-attribute-group]',

        /**
         * @property {String}
         */
        removeBtn: '.removeRow.btn-link',

        /**
         * @inheritDoc
         */
        initialize: function(options) {
            AttributeGroupComponent.__super__.initialize.call(this, options);

            this.options = _.defaults(options || {}, this.options);
            mediator.on('attribute-select:find-selected-attributes', this.onGetSelectedAttributes, this);

            var groups = this.getAllGroups();
            if (groups.length === 1) {
                this.hideRemoveButton(groups);
            } else {
                this.showRemoveButton(groups);
            }
            var self = this;
            $(this.options._sourceElement).parent().find(this.removeBtn).on('click', function(event) {
                var systemAttributesSelected = self.getAttributeSelect().find('option[locked="locked"]:selected');
                if (systemAttributesSelected.length) {
                    self.showConfirmModal(this);
                    return false;
                }
                return true;
            });
            //temporary width fix
            $(this.options._sourceElement).parents().find('.oro-item-collection .row-oro').width(960);
        },

        showConfirmModal: function(removeBtn) {
            var confirmDialog = new Modal({
                title:  _.__('oro.attribute.remove_confirmation_title'),
                content: _.__('oro.attribute.remove_confirmation_text'),
                className: 'modal oro-modal-danger'
            });

            confirmDialog.on('ok', function() {
                var item;
                var closest = '*[data-content]';
                if ($(removeBtn).data('closest')) {
                    closest = $(removeBtn).data('closest');
                }

                item = $(removeBtn).closest(closest);
                item.trigger('content:remove').remove();
                confirmDialog.close();
            });

            confirmDialog.open();
        },

        getAllGroups: function() {
            return $(this.groupSelector);
        },

        showRemoveButton: function(groups) {
            groups.parent().find(this.removeBtn).show();
        },

        hideRemoveButton: function(groups) {
            groups.parent().find(this.removeBtn).hide();
        },

        getAttributeSelect: function() {
            return $(this.options._sourceElement).find('[data-name="field__attribute-relations"]');
        },

        onGetSelectedAttributes: function(eventData) {
            var groupLabel = $(this.options._sourceElement).find('[data-attribute-select-group]').val();

            eventData.attributeSelects.push({groupLabel: groupLabel, attributesSelect: this.getAttributeSelect()});
        },

        dispose: function() {
            var groups = this.getAllGroups();
            if (groups.length === 2) { // Disable remove button for the last group
                this.hideRemoveButton(groups);
            }

            mediator.off('attribute-select:find-selected-attributes', this.onGetSelectedAttributes, this);
            mediator.trigger('attribute-group:remove', {
                attributeSelectFtid: this.getAttributeSelect().data('ftid'),
                firstGroup: groups.not(this.options._sourceElement).first()
            });

            AttributeGroupComponent.__super__.dispose.call(this);
        }
    });

    return AttributeGroupComponent;
});
<|endoftext|>


<commit_before>(function () {
    'use strict';

    /**
     * A component for split view content editing
     */
    var umbVariantContentEditors = {
        templateUrl: 'views/components/content/umb-variant-content-editors.html',
        bindings: {
            page: "<",
            content: "<",
            culture: "<",
            segment: "<",
            onSelectApp: "&?",
            onSelectAppAnchor: "&?",
            onBack: "&?",
            showBack: "<?"
        },
        controllerAs: 'vm',
        controller: umbVariantContentEditorsController
    };

    function umbVariantContentEditorsController($scope, $location, contentEditingHelper) {

        var prevContentDateUpdated = null;

        var vm = this;

        vm.$onInit = onInit;
        vm.$onChanges = onChanges;
        vm.$doCheck = doCheck;
        vm.$postLink = postLink;

        vm.openSplitView = openSplitView;
        vm.closeSplitView = closeSplitView;
        vm.selectVariant = selectVariant;
        vm.selectApp = selectApp;
        vm.selectAppAnchor = selectAppAnchor;

        //Used to track how many content views there are (for split view there will be 2, it could support more in theory)
        vm.editors = [];

        /** Called when the component initializes */
        function onInit() {
            prevContentDateUpdated = Utilities.copy(vm.content.updateDate);
            setActiveVariant();
        }

        /** Called when the component has linked all elements, this is when the form controller is available */
        function postLink() {

        }

        /**
         * Watch for model changes
         * @param {any} changes
         */
        function onChanges(changes) {

            if (changes.culture && !changes.culture.isFirstChange() && changes.culture.currentValue !== changes.culture.previousValue) {
                setActiveVariant();
            } else if (changes.segment && !changes.segment.isFirstChange() && changes.segment.currentValue !== changes.segment.previousValue) {
                setActiveVariant();
            }
        }

        /** Allows us to deep watch whatever we want - executes on every digest cycle */
        function doCheck() {
            if (!angular.equals(vm.content.updateDate, prevContentDateUpdated)) {
                setActiveVariant();
                prevContentDateUpdated = Utilities.copy(vm.content.updateDate);
            }
        }

        /** This is called when the split view changes based on the umb-variant-content */
        function splitViewChanged() {
            //send an event downwards
            $scope.$broadcast("editors.content.splitViewChanged", { editors: vm.editors });
        }

        /**
         * Set the active variant based on the current culture or segment (query string)
         */
        function setActiveVariant() {
            // set the active variant
            var activeVariant = null;
            _.each(vm.content.variants, function (v) {
                if ((vm.culture === "invariant" || v.language && v.language.culture === vm.culture) && v.segment === vm.segment) {
                    activeVariant = v;
                }
            });
            if (!activeVariant) {
                // Set the first variant to active if we can't find it.
                // If the content item is invariant, then only one item exists in the array.
                activeVariant = vm.content.variants[0];
            }

            insertVariantEditor(0, activeVariant);

            if (vm.editors.length > 1) {
                //now re-sync any other editor content (i.e. if split view is open)
                for (var s = 1; s < vm.editors.length; s++) {
                    //get the variant from the scope model
                    var variant = _.find(vm.content.variants, function (v) {
                        return (!v.language || v.language.culture === vm.editors[s].content.language.culture) && v.segment === vm.editors[s].content.segment;
                    });
                    vm.editors[s].content = variant;
                }
            }

        }

        /**
         * Updates the editors collection for a given index for the specified variant
         * @param {any} index
         * @param {any} variant
         */
        function insertVariantEditor(index, variant) {

            if (vm.editors[index]) {
                if (vm.editors[index].content === variant) {
                    // This variant is already the content of the editor in this index.
                    return;
                }
                vm.editors[index].content.active = false;
            }
            variant.active = true;

            var variantCulture = variant.language ? variant.language.culture : "invariant";
            var variantSegment = variant.segment;

            var currentCulture = index < vm.editors.length ? vm.editors[index].culture : null;
            var currentSegment = index < vm.editors.length ? vm.editors[index].segment : null;

            // if index not already exists or if the culture or segment isnt identical then we do a replacement.
            if (index >= vm.editors.length || currentCulture !== variantCulture || currentSegment !== variantSegment) {

                //Not the current culture or segment which means we need to modify the array.
                //NOTE: It is not good enough to just replace the `content` object at a given index in the array
                // since that would mean that directives are not re-initialized.
                vm.editors.splice(index, 1, {
                    compositeId: variant.compositeId,
                    content: variant,
                    culture: variantCulture,
                    segment: variantSegment
                });
            }
            else {
                //replace the content of the editor, since the culture and segment is the same.
                vm.editors[index].content = variant;
            }

        }

        /**
         * Adds a new editor to the editors array to show content in a split view
         * @param {any} selectedVariant
         */
        function openSplitView(selectedVariant) {
            // enforce content contentApp in splitview.
            var contentApp = vm.content.apps.find((app) => app.alias === "umbContent");
            if(contentApp) {
                selectApp(contentApp);
            }

            insertVariantEditor(vm.editors.length, selectedVariant);

            splitViewChanged();

        }

        $scope.$on("editors.content.splitViewRequest", function(event, args) {requestSplitView(args);});
        vm.requestSplitView = requestSplitView;
        function requestSplitView(args) {
            var culture = args.culture;
            var segment = args.segment;

            var variant = _.find(vm.content.variants, function (v) {
                return (!v.language || v.language.culture === culture) && v.segment === segment;
            });

            if (variant != null) {
                openSplitView(variant);
            }
        }

        /** Closes the split view */
        function closeSplitView(editorIndex) {
            // TODO: hacking animation states - these should hopefully be easier to do when we upgrade angular
            var editor = vm.editors[editorIndex];
            vm.editors.splice(editorIndex, 1);
            editor.content.active = false;

            //update the current culture to reflect the last open variant (closing the split view corresponds to selecting the other variant)

            $location.search({"cculture": vm.editors[0].content.language ? vm.editors[0].content.language.culture : null, "csegment": vm.editors[0].content.segment});
            splitViewChanged();
        }

        /**
         * Changes the currently selected variant
         * @param {any} variant This is the model of the variant/language drop down item in the editor header
         * @param {any} editorIndex The index of the editor being changed
         */
        function selectVariant(variant, editorIndex) {

            var variantCulture = variant.language ? variant.language.culture : "invariant";
            var variantSegment = variant.segment || null;

            // Check if we already have this editor open, if so, do nothing.
            if (vm.editors.find((editor) => (!editor.content.language || editor.content.language.culture === variantCulture) && editor.content.segment === variantSegment)) {
                return;
            }

            //if the editor index is zero, then update the query string to track the lang selection, otherwise if it's part
            //of a 2nd split view editor then update the model directly.
            if (editorIndex === 0) {
                //If we've made it this far, then update the query string.
                //The editor will respond to this query string changing.
                $location.search("cculture", variantCulture).search("csegment", variantSegment);
            }
            else {

                //update the editors collection
                insertVariantEditor(editorIndex, variant);

            }
        }

        /**
         * Stores the active app in a variable so we can remember it when changing language
         * @param {any} app This is the model of the selected app
         */
        function selectApp(app) {
            if(vm.onSelectApp) {
                vm.onSelectApp({"app": app});
            }
        }

        function selectAppAnchor(app, anchor) {
            if(vm.onSelectAppAnchor) {
                vm.onSelectAppAnchor({"app": app, "anchor": anchor});
            }
        }

    }

    angular.module('umbraco.directives').component('umbVariantContentEditors', umbVariantContentEditors);

})();
<commit_msg>emit event when changing variant, quick syntax/style tidyup<commit_after>(function () {
    'use strict';

    /**
     * A component for split view content editing
     */
    var umbVariantContentEditors = {
        templateUrl: 'views/components/content/umb-variant-content-editors.html',
        bindings: {
            page: "<",
            content: "<",
            culture: "<",
            segment: "<",
            onSelectApp: "&?",
            onSelectAppAnchor: "&?",
            onBack: "&?",
            showBack: "<?"
        },
        controllerAs: 'vm',
        controller: umbVariantContentEditorsController
    };

    function umbVariantContentEditorsController($scope, $location, eventsService) {

        var prevContentDateUpdated = null;

        var vm = this;

        vm.$onInit = onInit;
        vm.$onChanges = onChanges;
        vm.$doCheck = doCheck;
        vm.$postLink = postLink;

        vm.openSplitView = openSplitView;
        vm.closeSplitView = closeSplitView;
        vm.selectVariant = selectVariant;
        vm.selectApp = selectApp;
        vm.selectAppAnchor = selectAppAnchor;
        vm.requestSplitView = requestSplitView;

        //Used to track how many content views there are (for split view there will be 2, it could support more in theory)
        vm.editors = [];

        /** Called when the component initializes */
        function onInit() {
            prevContentDateUpdated = Utilities.copy(vm.content.updateDate);
            setActiveVariant();
        }

        /** Called when the component has linked all elements, this is when the form controller is available */
        function postLink() {

        }

        /**
         * Watch for model changes
         * @param {any} changes
         */
        function onChanges(changes) {

            if (changes.culture && !changes.culture.isFirstChange() && changes.culture.currentValue !== changes.culture.previousValue) {
                setActiveVariant();
            } else if (changes.segment && !changes.segment.isFirstChange() && changes.segment.currentValue !== changes.segment.previousValue) {
                setActiveVariant();
            }
        }

        /** Allows us to deep watch whatever we want - executes on every digest cycle */
        function doCheck() {
            if (!Utilities.equals(vm.content.updateDate, prevContentDateUpdated)) {
                setActiveVariant();
                prevContentDateUpdated = Utilities.copy(vm.content.updateDate);
            }
        }

        /** This is called when the split view changes based on the umb-variant-content */
        function splitViewChanged() {
            //send an event downwards
            $scope.$broadcast("editors.content.splitViewChanged", { editors: vm.editors });
        }

        /**
         * Set the active variant based on the current culture or segment (query string)
         */
        function setActiveVariant() {
            // set the active variant
            var activeVariant = null;
            vm.content.variants.forEach(v => {
                if ((vm.culture === "invariant" || v.language && v.language.culture === vm.culture) && v.segment === vm.segment) {
                    activeVariant = v;
                }
            });

            if (!activeVariant) {
                // Set the first variant to active if we can't find it.
                // If the content item is invariant, then only one item exists in the array.
                activeVariant = vm.content.variants[0];
            }

            insertVariantEditor(0, activeVariant);

            if (vm.editors.length > 1) {
                //now re-sync any other editor content (i.e. if split view is open)
                for (var s = 1; s < vm.editors.length; s++) {
                    //get the variant from the scope model
                    var variant = vm.content.variants.find(v =>
                        (!v.language || v.language.culture === vm.editors[s].content.language.culture) && v.segment === vm.editors[s].content.segment);

                    vm.editors[s].content = variant;
                }
            }

            eventsService.emit('editors.content.cultureChanged', activeVariant.language.culture);
        }

        /**
         * Updates the editors collection for a given index for the specified variant
         * @param {any} index
         * @param {any} variant
         */
        function insertVariantEditor(index, variant) {

            if (vm.editors[index]) {
                if (vm.editors[index].content === variant) {
                    // This variant is already the content of the editor in this index.
                    return;
                }
                vm.editors[index].content.active = false;
            }
            variant.active = true;

            var variantCulture = variant.language ? variant.language.culture : "invariant";
            var variantSegment = variant.segment;

            var currentCulture = index < vm.editors.length ? vm.editors[index].culture : null;
            var currentSegment = index < vm.editors.length ? vm.editors[index].segment : null;

            // if index not already exists or if the culture or segment isnt identical then we do a replacement.
            if (index >= vm.editors.length || currentCulture !== variantCulture || currentSegment !== variantSegment) {

                //Not the current culture or segment which means we need to modify the array.
                //NOTE: It is not good enough to just replace the `content` object at a given index in the array
                // since that would mean that directives are not re-initialized.
                vm.editors.splice(index, 1, {
                    compositeId: variant.compositeId,
                    content: variant,
                    culture: variantCulture,
                    segment: variantSegment
                });
            }
            else {
                //replace the content of the editor, since the culture and segment is the same.
                vm.editors[index].content = variant;
            }

        }

        /**
         * Adds a new editor to the editors array to show content in a split view
         * @param {any} selectedVariant
         */
        function openSplitView(selectedVariant) {
            // enforce content contentApp in splitview.
            var contentApp = vm.content.apps.find(app => app.alias === "umbContent");
            if(contentApp) {
                selectApp(contentApp);
            }

            insertVariantEditor(vm.editors.length, selectedVariant);

            splitViewChanged();
        }

        function requestSplitView(args) {
            var culture = args.culture;
            var segment = args.segment;

            var variant = vm.content.variants.find(v =>
                (!v.language || v.language.culture === culture) && v.segment === segment);

            if (variant != null) {
                openSplitView(variant);
            }
        }

        eventsService.on("editors.content.splitViewRequest", (_, args) => requestSplitView(args));

        /** Closes the split view */
        function closeSplitView(editorIndex) {
            // TODO: hacking animation states - these should hopefully be easier to do when we upgrade angular
            var editor = vm.editors[editorIndex];
            vm.editors.splice(editorIndex, 1);
            editor.content.active = false;

            //update the current culture to reflect the last open variant (closing the split view corresponds to selecting the other variant)
            const culture = vm.editors[0].content.language ? vm.editors[0].content.language.culture : null;

            $location.search({"cculture": culture, "csegment": vm.editors[0].content.segment});
            splitViewChanged();
        }

        /**
         * Changes the currently selected variant
         * @param {any} variant This is the model of the variant/language drop down item in the editor header
         * @param {any} editorIndex The index of the editor being changed
         */
        function selectVariant(variant, editorIndex) {

            var variantCulture = variant.language ? variant.language.culture : "invariant";
            var variantSegment = variant.segment || null;

            // Check if we already have this editor open, if so, do nothing.
            if (vm.editors.find((editor) => (!editor.content.language || editor.content.language.culture === variantCulture) && editor.content.segment === variantSegment)) {
                return;
            }

            //if the editor index is zero, then update the query string to track the lang selection, otherwise if it's part
            //of a 2nd split view editor then update the model directly.
            if (editorIndex === 0) {
                //If we've made it this far, then update the query string.
                //The editor will respond to this query string changing.
                $location.search("cculture", variantCulture).search("csegment", variantSegment);
            }
            else {
                //update the editors collection
                insertVariantEditor(editorIndex, variant);
            }
        }

        /**
         * Stores the active app in a variable so we can remember it when changing language
         * @param {any} app This is the model of the selected app
         */
        function selectApp(app) {
            if(vm.onSelectApp) {
                vm.onSelectApp({"app": app});
            }
        }

        function selectAppAnchor(app, anchor) {
            if(vm.onSelectAppAnchor) {
                vm.onSelectAppAnchor({"app": app, "anchor": anchor});
            }
        }
    }

    angular.module('umbraco.directives').component('umbVariantContentEditors', umbVariantContentEditors);

})();
<|endoftext|>


<commit_before>'use strict';
define(
    ['three', 'app/classes/SpaceObject'],
    function(THREE, SpaceObject) {
        return class TrailParticle extends SpaceObject {
            constructor(scene, parent, position, size, lifetime) {
                super();

                this.scene    = scene;

                this.size     = size;
                this.position = position;
                this.color    = parent.color;

                this.lifetime = lifetime;
                this.life     = 0.0;
                this.alive    = this.life < this.lifetime;

                this.mesh     = null;
                this._createMesh();
                this.scene.add(this.mesh);
            }

            _createMesh() {
                let geometry = new THREE.BoxGeometry(this.size, this.size, this.size);
                let material = new THREE.MeshBasicMaterial({color: this.color});

                this.mesh = new THREE.Mesh(geometry, material);
                this.mesh.position.set(this.position.x, this.position.y, this.position.z);
            }

            update(deltaT) {
                super.update(deltaT);

                if(this.life <= this.lifetime) {
                    let factor = 1 - this.life / this.lifetime / 50;

                    // Fade to black (alpha not available)
                    this.mesh.material.color.r *= factor;
                    this.mesh.material.color.g *= factor;
                    this.mesh.material.color.b *= factor;

                    this.life += deltaT / 1000;
                } else {
                    this.alive = false;
                    this.scene.remove(this.mesh);
                }
            }

        }
    }
);<commit_msg>Using plane geometry for the trail<commit_after>'use strict';
define(
    ['three', 'app/classes/SpaceObject'],
    function(THREE, SpaceObject) {
        return class TrailParticle extends SpaceObject {
            constructor(scene, parent, position, size, lifetime) {
                super();

                this.scene    = scene;

                this.size     = size;
                this.position = position;
                this.color    = parent.color;

                this.lifetime = lifetime;
                this.life     = 0.0;
                this.alive    = this.life < this.lifetime;

                this.mesh     = null;
                this._createMesh();
                this.scene.add(this.mesh);
            }

            _createMesh() {
                let geometry = new THREE.PlaneGeometry(this.size, this.size, this.size);
                let material = new THREE.MeshBasicMaterial({color: this.color});

                this.mesh = new THREE.Mesh(geometry, material);
                this.mesh.position.set(this.position.x, this.position.y, this.position.z);
            }

            update(deltaT) {
                super.update(deltaT);

                if(this.life <= this.lifetime) {
                    let factor = 1 - this.life / this.lifetime / 50;

                    // Fade to black (alpha not available)
                    this.mesh.material.color.r *= factor;
                    this.mesh.material.color.g *= factor;
                    this.mesh.material.color.b *= factor;

                    this.life += deltaT / 1000;
                } else {
                    this.alive = false;
                    this.scene.remove(this.mesh);
                }
            }

        }
    }
);<|endoftext|>


<commit_before>import React, { PropTypes } from 'react'

import fetch from 'isomorphic-fetch'

import { Panel, Grid, Row, Col, Button } from 'react-bootstrap'

import { PureComponent, shallowEqual } from 'react-pure-render'

import GtfsMap from './gtfsmap'
import GtfsSearch from './gtfssearch'

export default class GtfsMapSearch extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      stops: [],
      routes: [],
      patterns: [],
      position: null,
      message: '',
      map: {}
    }
  }

  componentDidMount() {
    // this.fetchUsers()
    console.log(this.props)

  }

  render() {
    const {attribution, centerCoordinates, geojson, markers, transitive, url, zoom} = this.props
    const getPatterns = (input) => {
      return fetch(`/api/manager/patterns?route=${input.route.route_id}&feed=${input.route.feed_id}`)
      .then((response) => {
        return response.json()
      })
      .then((json) => {

        const pattern = json[0]
        console.log(pattern)
        // hack to associate route to pattern
        pattern.associatedRoutes = []
        pattern.associatedRoutes.push(input.route)
        return pattern
      })
    }
    const handleSelection = (input) => {
      if (!input) {
        this.setState({stops: null, routes: null, patterns: null, searchFocus: false})
      }
      else if (input && input.stop) {
        this.setState(Object.assign({}, this.state, { stops: [input.stop], position: [input.stop.stop_lat, input.stop.stop_lon], routes: null, patterns: null, searchFocus: true }))
      }
      else if (input && input.route) {
        return Promise.all([getPatterns(input)]).then((results) => {
          const patterns = results[0]
          console.log('patterns for route ' + input.route.route_id, patterns)
          this.setState(Object.assign({}, this.state, { routes: null, patterns: null, stops: null, searchFocus: true }))
          this.setState(Object.assign({}, this.state, { routes: [input.route], patterns: [patterns], stops: null, searchFocus: true }))
          return patterns
        })

      }
    }

    return (

      <GtfsSearch
        ref='mapSearch'
        feeds={this.props.feeds}
        limit={100}
        placeholder={this.props.placeholder}
        onChange={handleSelection}
        entities={['stops', 'routes']}
      />
      <GtfsMap
        feeds={this.props.feeds}
        position={this.state.position}
        onStopClick={this.props.onStopClick}
        onRouteClick={this.props.onRouteClick}
        stops={this.state.stops}
        searchFocus={this.state.searchFocus}
        patterns={this.state.patterns}
        popupAction={this.props.popupAction}
      />

    )
  }
}
<commit_msg>commented on state setting issue<commit_after>import React, { PropTypes } from 'react'

import fetch from 'isomorphic-fetch'

import { Panel, Grid, Row, Col, Button } from 'react-bootstrap'

import { PureComponent, shallowEqual } from 'react-pure-render'

import GtfsMap from './gtfsmap'
import GtfsSearch from './gtfssearch'

export default class GtfsMapSearch extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      stops: [],
      routes: [],
      patterns: [],
      position: null,
      message: '',
      map: {}
    }
  }

  componentDidMount() {
    // this.fetchUsers()
    console.log(this.props)

  }

  render() {
    const {attribution, centerCoordinates, geojson, markers, transitive, url, zoom} = this.props
    const getPatterns = (input) => {
      return fetch(`/api/manager/patterns?route=${input.route.route_id}&feed=${input.route.feed_id}`)
      .then((response) => {
        return response.json()
      })
      .then((json) => {

        const pattern = json[0]
        console.log(pattern)
        // hack to associate route to pattern
        pattern.associatedRoutes = []
        pattern.associatedRoutes.push(input.route)
        return pattern
      })
    }
    const handleSelection = (input) => {
      if (!input) {
        this.setState({stops: null, routes: null, patterns: null, searchFocus: false})
      }
      else if (input && input.stop) {
        this.setState(Object.assign({}, this.state, { stops: [input.stop], position: [input.stop.stop_lat, input.stop.stop_lon], routes: null, patterns: null, searchFocus: true }))
      }
      else if (input && input.route) {
        return Promise.all([getPatterns(input)]).then((results) => {
          const patterns = results[0]
          console.log('patterns for route ' + input.route.route_id, patterns)

          // hack to clear out patterns to ensure map GeoJson is cleared
          if (this.state.patterns) {
            this.setState(Object.assign({}, this.state, { routes: null, patterns: null, stops: null, searchFocus: true }))
            this.setState(Object.assign({}, this.state, { routes: [input.route], patterns: [patterns], stops: null, searchFocus: true }))
          }
          else {
            this.setState(Object.assign({}, this.state, { routes: [input.route], patterns: [patterns], stops: null, searchFocus: true }))
          }

          return patterns
        })

      }
    }

    return (

      <GtfsSearch
        ref='mapSearch'
        feeds={this.props.feeds}
        limit={100}
        placeholder={this.props.placeholder}
        onChange={handleSelection}
        entities={['stops', 'routes']}
      />
      <GtfsMap
        feeds={this.props.feeds}
        position={this.state.position}
        onStopClick={this.props.onStopClick}
        onRouteClick={this.props.onRouteClick}
        stops={this.state.stops}
        searchFocus={this.state.searchFocus}
        patterns={this.state.patterns}
        popupAction={this.props.popupAction}
      />

    )
  }
}
<|endoftext|>


<commit_before>(function () {
    var ns = $.namespace("pskl.controller");

    ns.ToolController = function () {

        this.toolInstances = {
            "simplePen" : new pskl.drawingtools.SimplePen(),
            "verticalMirrorPen" : new pskl.drawingtools.VerticalMirrorPen(),
            "eraser" : new pskl.drawingtools.Eraser(),
            "paintBucket" : new pskl.drawingtools.PaintBucket(),
            "stroke" : new pskl.drawingtools.Stroke(),
            "rectangle" : new pskl.drawingtools.Rectangle(),
            "circle" : new pskl.drawingtools.Circle(),
            "move" : new pskl.drawingtools.Move(),
            "rectangleSelect" : new pskl.drawingtools.RectangleSelect(),
            "shapeSelect" : new pskl.drawingtools.ShapeSelect(),
            "colorPicker" : new pskl.drawingtools.ColorPicker()
        };

        this.currentSelectedTool = this.toolInstances.simplePen;
        this.previousSelectedTool = this.toolInstances.simplePen;
    };

    /**
     * @private
     */
    ns.ToolController.prototype.activateToolOnStage_ = function(tool) {
        var stage = $("body");
        var previousSelectedToolClass = stage.data("selected-tool-class");
        if(previousSelectedToolClass) {
          stage.removeClass(previousSelectedToolClass);
        }
        stage.addClass(tool.toolId);
        stage.data("selected-tool-class", tool.toolId);
    };

    /**
     * @private
     */
    ns.ToolController.prototype.selectTool_ = function(tool) {
        console.log("Selecting Tool:" , this.currentSelectedTool);
        this.currentSelectedTool = tool;
        this.activateToolOnStage_(this.currentSelectedTool);
        $.publish(Events.TOOL_SELECTED, [tool]);
    };

    /**
     * @private
     */
    ns.ToolController.prototype.onToolIconClicked_ = function(evt) {
        var target = $(evt.target);
        var clickedTool = target.closest(".tool-icon");

        if(clickedTool.length) {
            for(var tool in this.toolInstances) {
                if (this.toolInstances[tool].toolId == clickedTool.data().toolId) {
                    this.selectTool_(this.toolInstances[tool]);

                    // Show tool as selected:
                    $('#menubar .tool-icon.selected').removeClass('selected');
                    clickedTool.addClass('selected');
                }
            }
        }
    };

    /**
     * @private
     */
    ns.ToolController.prototype.createToolMarkup_ = function() {
        var currentTool, toolMarkup = '';
        // TODO(vincz): Tools rendering order is not enforced by the data stucture (this.toolInstances), fix that.
        for (var toolKey in this.toolInstances) {
            currentTool = this.toolInstances[toolKey];
            toolMarkup += '

';
        }
        $('#tools-container').html(toolMarkup);
    };

    /**
     * Get state for the checkbox that control the display of the grid
     * on the drawing canvas.
     * @private
     */
    ns.ToolController.prototype.isShowGridChecked_ = function() {
        var showGridCheckbox = $('#show-grid');
        var isChecked = showGridCheckbox.is(':checked');
        return isChecked;
    };

    /**
     * @public
     */
    ns.ToolController.prototype.init = function() {

        this.createToolMarkup_();

        // Initialize tool:
        // Set SimplePen as default selected tool:
        this.selectTool_(this.toolInstances.simplePen);
        // Activate listener on tool panel:
        $("#menubar").click($.proxy(this.onToolIconClicked_, this));

        // Show/hide the grid on drawing canvas:
        $.publish(Events.GRID_DISPLAY_STATE_CHANGED, [this.isShowGridChecked_()]);
        $('#show-grid').change($.proxy(function(evt) {
            var checked = this.isShowGridChecked_();
            $.publish(Events.GRID_DISPLAY_STATE_CHANGED, [checked]);
        }, this));
    };
})();<commit_msg>Fix selected state for tools on load<commit_after>(function () {
    var ns = $.namespace("pskl.controller");

    ns.ToolController = function () {

        this.toolInstances = {
            "simplePen" : new pskl.drawingtools.SimplePen(),
            "verticalMirrorPen" : new pskl.drawingtools.VerticalMirrorPen(),
            "eraser" : new pskl.drawingtools.Eraser(),
            "paintBucket" : new pskl.drawingtools.PaintBucket(),
            "stroke" : new pskl.drawingtools.Stroke(),
            "rectangle" : new pskl.drawingtools.Rectangle(),
            "circle" : new pskl.drawingtools.Circle(),
            "move" : new pskl.drawingtools.Move(),
            "rectangleSelect" : new pskl.drawingtools.RectangleSelect(),
            "shapeSelect" : new pskl.drawingtools.ShapeSelect(),
            "colorPicker" : new pskl.drawingtools.ColorPicker()
        };

        this.currentSelectedTool = this.toolInstances.simplePen;
        this.previousSelectedTool = this.toolInstances.simplePen;
    };

    /**
     * @private
     */
    ns.ToolController.prototype.activateToolOnStage_ = function(tool) {
        var stage = $("body");
        var previousSelectedToolClass = stage.data("selected-tool-class");
        if(previousSelectedToolClass) {
          stage.removeClass(previousSelectedToolClass);
        }
        stage.addClass(tool.toolId);
        stage.data("selected-tool-class", tool.toolId);
    };

    /**
     * @private
     */
    ns.ToolController.prototype.selectTool_ = function(tool) {
        console.log("Selecting Tool:" , this.currentSelectedTool);
        this.currentSelectedTool = tool;
        this.activateToolOnStage_(this.currentSelectedTool);
        $.publish(Events.TOOL_SELECTED, [tool]);
    };

    /**
     * @private
     */
    ns.ToolController.prototype.onToolIconClicked_ = function(evt) {
        var target = $(evt.target);
        var clickedTool = target.closest(".tool-icon");

        if(clickedTool.length) {
            for(var tool in this.toolInstances) {
                if (this.toolInstances[tool].toolId == clickedTool.data().toolId) {
                    this.selectTool_(this.toolInstances[tool]);

                    // Show tool as selected:
                    $('#menubar .tool-icon.selected').removeClass('selected');
                    clickedTool.addClass('selected');
                }
            }
        }
    };

    /**
     * @private
     */
    ns.ToolController.prototype.createToolMarkup_ = function() {
        var currentTool, toolMarkup = '', extraClass;
        // TODO(vincz): Tools rendering order is not enforced by the data stucture (this.toolInstances), fix that.
        for (var toolKey in this.toolInstances) {
            currentTool = this.toolInstances[toolKey];
            extraClass = currentTool.toolId;
            if (this.currentSelectedTool == currentTool) {
                extraClass = extraClass + " selected";
            }
            toolMarkup += '

';
        }
        $('#tools-container').html(toolMarkup);
    };

    /**
     * Get state for the checkbox that control the display of the grid
     * on the drawing canvas.
     * @private
     */
    ns.ToolController.prototype.isShowGridChecked_ = function() {
        var showGridCheckbox = $('#show-grid');
        var isChecked = showGridCheckbox.is(':checked');
        return isChecked;
    };

    /**
     * @public
     */
    ns.ToolController.prototype.init = function() {

        this.createToolMarkup_();

        // Initialize tool:
        // Set SimplePen as default selected tool:
        this.selectTool_(this.toolInstances.simplePen);
        // Activate listener on tool panel:
        $("#menubar").click($.proxy(this.onToolIconClicked_, this));

        // Show/hide the grid on drawing canvas:
        $.publish(Events.GRID_DISPLAY_STATE_CHANGED, [this.isShowGridChecked_()]);
        $('#show-grid').change($.proxy(function(evt) {
            var checked = this.isShowGridChecked_();
            $.publish(Events.GRID_DISPLAY_STATE_CHANGED, [checked]);
        }, this));
    };
})();<|endoftext|>


<commit_before>/**
 *  Copyright (C) 2014 3D Repo Ltd
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
var logIface = require('../logger.js');
var logger = logIface.logger;
var repoNodeMesh = require('../repoNodeMesh.js');
var responseCodes = require('../response_codes.js');

/*******************************************************************************
 * Render Binary format data of parts of a mesh
 *
 * @param {dbInterface} dbInterface - Database interface object
 * @param {string} dbName - Database containing the project to get object from
 * @param {string} project - The name of the project containing the mesh
 * @param {string} rid - Revision ID of the mesh to be rendered
 * @param {string} sid - Shared ID of the mesh to be rendered
 * @param {string} uuid - UUID of the mesh to be rendered
 * @param {string} type - Part of the mesh to be rendered (normals, texcoords, indices, coords)
 * @param {Object} res - The http response object
 * @param {function} callback - Callback function that takes an Error object
 *******************************************************************************/
function render(dbInterface, dbName, project, uuid, rid, sid, type, callback) {
	logger.log('debug', 'Requesting ' + type + ' for ' + uuid);

	dbInterface.getObject(dbName, project, uuid, rid, sid, true, {}, function(err, objType, uid, obj) {
		if (err.value)
			return callback(err);

		var mesh = obj.meshes[uuid];

		switch (type) {
			case "normals":
				return callback(responseCodes.OK, mesh['normals'].buffer);
			case "texcoords":
				return callback(responseCodes.OK, mesh['uv_channels'].buffer);
			case "indices":
				var buf = new Buffer(mesh.faces_count * 2 * 3);
				var copy_idx = 0;
				var orig_idx = mesh['faces'].buffer;

				for (var face_idx = 0; face_idx < mesh.faces_count; face_idx++) {
					for (var vert_comp = 0; vert_comp < 3; vert_comp++) {
						var byte_position = (16 * face_idx) + (vert_comp + 1) * 4;
						var idx_val = orig_idx.readUInt16LE(byte_position);

						buf.writeUInt16LE(idx_val, copy_idx);
						copy_idx += 2;
					}
				}

				return callback(responseCodes.OK, buf);
			case "coords":
				return callback(responseCodes.OK, mesh['vertices'].buffer);
		}
	});
};

// Set up REST routing calls
exports.route = function(router)
{
    router.get('bin', '/:account/:project/:uid', function(res, params, err_callback) {
		var type = params.query.mode;

        dbInterface(req[C.REQ_REPO].logger).cacheFunction(params.account, params.project, req.url, req.params[C.REPO_REST_API_FORMAT].toLowerCase(), function (callback) {
            render(router.dbInterface, params.account, params.project, params.uid, null, null, type, err_callback);
        }, err_callback);

    });

	router.get('bin', '/:account/:project/revision/:rid/:sid', function(res, params, err_callback) {
		var type = params.query.mode;

        render(router.dbInterface, params.account, params.project, null, params.rid, params.sid, type, err_callback);
	});

};

<commit_msg>#181 hijack the unused bin_encoder to retrieve gltf binary files from stash<commit_after>/**
 *	Copyright (C) 2014 3D Repo Ltd
 *
 *	This program is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU Affero General Public License as
 *	published by the Free Software Foundation, either version 3 of the
 *	License, or (at your option) a/exitny later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU Affero General Public License for more details.
 *
 *	You should have received a copy of the GNU Affero General Public License
 *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
var logIface = require("../logger.js");
var C = require("../constants.js");
var uuidToString = require("../db_interface.js").uuidToString;
var responseCodes = require("../response_codes.js");

var utils = require("../utils.js");

var dbInterface = require("../db_interface.js");

var config = require("app-config").config;

// Set up REST routing calls
exports.route = function (router) {
    router.get("bin", "/:account/:project/:uid", function (req, res, params, err_callback) {
        //The Repo IO server has no capability of generating gltf binary files on the fly.
        //So this is going to call the stash and see if we have a stashed gltf in the database
        //if not, just return a NOT_FOUND

        dbInterface(req[C.REQ_REPO].logger).cacheFunction(params.account, params.project, req.url, "gltf", function (callback) {
            err_callback(responseCodes.FILE_DOESNT_EXIST);
        }, err_callback);
    });
};

<|endoftext|>


<commit_before>'use strict'
const Promise = require('bluebird')
const config = require('../server/init/configuration')
const log = require('../server/init/logging')
const EventEmitter = require('events')
const eventEmitter = new EventEmitter()

const {addDescription} = require('message-type')
const handleMessage = require('./handleMessage')
require('colors')
const {Client: AMQPClient, Policy} = require('amqp10')
const urlencode = require('urlencode')
const client = new AMQPClient(Policy.Utils.RenewOnSettle(1, 1, Policy.ServiceBusQueue))

function start () {
  log.info('connecting with the following azure url:', `amqps://${config.full.azure.SharedAccessKeyName}:${(config.secure.azure.SharedAccessKey || '').replace(/\w/g, 'x')}@${config.full.azure.host}`)
  const queueName = config.secure.azure.queueName || config.full.azure.queueName
  log.info('connecting to the queue with name ', queueName)

  client.on('connection:closed', msg => log.info('connection:closed event received', msg))
  client.on('connection:opened', msg => log.info('connection to azure opened'))

  return client.connect(`amqps://${config.full.azure.SharedAccessKeyName}:${urlencode(config.secure.azure.SharedAccessKey)}@${config.full.azure.host}`)
    .then(() => client.createReceiver(queueName))
    .then(receiver => {
      log.info('receiver created....')

      receiver.on('errorReceived', err => log.warn('An error occured when trying to receive message from queue', err))

      receiver.on('detached', msg => {
        log.info('Got a detached event, restart the azure client')
        client.disconnect()
        .then(() => log.info('Client disconnected'))
        .then(start)
        .catch(e => log.error(e))
      })

      receiver.on('message', message => {
        log.info('New message from ug queue', message)

        Promise.resolve(message)
        .then(initLogger)
        .then(() => {
          if (message.body) {
            return _processMessage(message)
          } else {
            log.info('Message is empty or undefined, deleting from queue...', message)
            return receiver.accept(message)
          }
        })
        .then(initLogger)
      })

      function _processMessage (MSG) {
        let result
        return Promise.resolve(MSG.body)
        .then(() => eventEmitter.emit('processMessageStart', MSG, result))
        .then(addDescription)
        .then(handleMessage)
        .then(_result => {
          log.info('result from handleMessage', _result)
          result = _result
        })
        .then(() => receiver.accept(MSG))
        .then(() => eventEmitter.emit('messageProcessed', MSG, result))
        .catch(e => {
          log.error(e)
          log.info('Error Occured, releasing message back to queue...', MSG)
          return receiver.modify(MSG, {undeliverableHere: false, deliveryFailed: true})
        })
      }

      return receiver
    })
    .catch(e => log.error('An error occured:', e))
}

function initLogger (msg) {
  let config
  if (msg) {
    const {body} = msg
    config = {
      kthid: body && body.kthid,
      ug1Name: body && body.ug1Name,
      ugversion: (msg && msg.applicationProperties && msg.applicationProperties.UGVersion) || undefined,
      messageId: (msg && msg.properties && msg.properties.messageId) || undefined
    }
  } else {
    config = {}
  }

  log.init(config)

  return msg && msg.body
}

module.exports = {
  start, eventEmitter
}
<commit_msg>added logging for detached events<commit_after>'use strict'
const Promise = require('bluebird')
const config = require('../server/init/configuration')
const log = require('../server/init/logging')
const EventEmitter = require('events')
const eventEmitter = new EventEmitter()

const {addDescription} = require('message-type')
const handleMessage = require('./handleMessage')
require('colors')
const {Client: AMQPClient, Policy} = require('amqp10')
const urlencode = require('urlencode')
const client = new AMQPClient(Policy.Utils.RenewOnSettle(1, 1, Policy.ServiceBusQueue))

client.on('connection:closed', msg => log.info('connection:closed event received', msg))
client.on('connection:opened', msg => log.info('connection to azure opened'))
client.on('connection:disconnected', msg => log.info('connection to azure disconnected'))

function start () {
  log.info('connecting with the following azure url:', `amqps://${config.full.azure.SharedAccessKeyName}:${(config.secure.azure.SharedAccessKey || '').replace(/\w/g, 'x')}@${config.full.azure.host}`)
  const queueName = config.secure.azure.queueName || config.full.azure.queueName
  log.info('connecting to the queue with name ', queueName)

  return client.connect(`amqps://${config.full.azure.SharedAccessKeyName}:${urlencode(config.secure.azure.SharedAccessKey)}@${config.full.azure.host}`)
    .then(() => client.createReceiver(queueName))
    .then(receiver => {
      log.info('receiver created:', receiver.id)

      receiver.on('errorReceived', err => log.warn('An error occured when trying to receive message from queue', err))

      receiver.on('detached', msg => {
        log.info(`Got a detached event for receiver ${receiver.id}, restart the azure client`)
        client.disconnect()
        .then(() => log.info('Client disconnected'))
        .then(start)
        .catch(e => log.error(e))
      })

      receiver.on('message', message => {
        log.info(`New message from ug queue for receiver ${receiver.id}`, message)

        Promise.resolve(message)
        .then(initLogger)
        .then(() => {
          if (message.body) {
            return _processMessage(message)
          } else {
            log.info('Message is empty or undefined, deleting from queue...', message)
            return receiver.accept(message)
          }
        })
        .then(initLogger)
      })

      function _processMessage (MSG) {
        let result
        return Promise.resolve(MSG.body)
        .then(() => eventEmitter.emit('processMessageStart', MSG, result))
        .then(addDescription)
        .then(handleMessage)
        .then(_result => {
          log.info('result from handleMessage', _result)
          result = _result
        })
        .then(() => receiver.accept(MSG))
        .then(() => eventEmitter.emit('messageProcessed', MSG, result))
        .catch(e => {
          log.error(e)
          log.info('Error Occured, releasing message back to queue...', MSG)
          return receiver.modify(MSG, {undeliverableHere: false, deliveryFailed: true})
        })
      }

      return receiver
    })
    .catch(e => log.error('An error occured:', e))
}

function initLogger (msg) {
  let config
  if (msg) {
    const {body} = msg
    config = {
      kthid: body && body.kthid,
      ug1Name: body && body.ug1Name,
      ugversion: (msg && msg.applicationProperties && msg.applicationProperties.UGVersion) || undefined,
      messageId: (msg && msg.properties && msg.properties.messageId) || undefined
    }
  } else {
    config = {}
  }

  log.init(config)

  return msg && msg.body
}

module.exports = {
  start, eventEmitter
}
<|endoftext|>


<commit_before>var request = require('request');

process.env.HTTP_PROXY = "";
process.env.HTTPS_PROXY = "";

var options = {
				uri:'http://www.google.com'
			  };

request(options, function(err, res, data){
	if(err){
		console.log(JSON.stringify(err));
	}
	console.log("res: " +res);
	console.log("data: " +data);
})<commit_msg>Removed random test files<commit_after><|endoftext|>


<commit_before>/* Copyright 2019 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

import {ClientController} from './client_controller.js';
import {PlaylistController} from './playlist_controller.js';
import {ErrorController} from './error_controller.js';
import {PlaylistCreator} from './playlist_creator.js';
import {WS} from '/lib/websocket.js';
import {addLogger} from '/lib/log.js';
import {makeConsoleLogger} from '/lib/console_logger.js';

function makeConsoleColorFn(css) {
  const ret = str => {
    return `%c${str}%c`;
  }
  ret.desc = css;
  return ret;
}

addLogger(makeConsoleLogger(c => {
  const ret = makeConsoleColorFn([`color: ${c}`]);
  ret.bold = {
    bgRed: makeConsoleColorFn(['font-weight: bolder', 'background-color: red', `color: ${c}`]),
  };
  ret.bgBlue = makeConsoleColorFn(['background-color: blue', `color: ${c}`]);
  return ret;
}, () => performance.now()));

let lastUpdateFromServer = 0;
let timeOfLastUpdateFromServer = window.performance.now();
let connected = false;
function getTime() {
  if (!connected) {
    return lastUpdateFromServer;
  }
  return lastUpdateFromServer + window.performance.now() - timeOfLastUpdateFromServer;
}
const host = new URL(location).searchParams.get('host') || 'localhost:6001';
const control = WS.clientWrapper(`ws://${host}/`);
const creatorEl = document.querySelector('#playlist-creator');

function applyNewPlaylist(playlist, moduleConfig) {
  // TODO(applmak): Passing a string here is a bit hacky.
  if (playlist == 'reset') {
    control.emit('resetPlaylist');
  } else {
    control.emit('newPlaylist', {playlist, moduleConfig});
  }
}

const playlistCreator = new PlaylistCreator(creatorEl, applyNewPlaylist);
const playlistController = new PlaylistController(document.querySelector('.playlist-container'), getTime);
const errorController = new ErrorController(document.querySelector('footer'));
const clientController = new ClientController(
  document.querySelector('.diagram'),
  req => control.emit('takeSnapshot', req),
  errorController,
  getTime,
);

function convertMsDurationToText(ms) {
  const secs = Math.floor(ms / 1000);
  const mins = Math.floor(secs / 60);

  return mins ? `${mins} minutes` : secs ? `${secs} seconds` : String(ms);
}

let transitionData = {};
control.on('transition', data => {
  transitionData = data;

  const duration = data.nextDeadline - data.deadline;
  const moduleNameEl = document.querySelector('#module');
  moduleNameEl.textContent = data.module;
  const durationEl = document.querySelector('#duration');
  durationEl.textContent = convertMsDurationToText(duration);

  playlistController.updateTransitionData(data);
  playlistCreator.setLivePlaylist(data.layouts);
  playlistCreator.setModuleConfig(data.configMap);
});
control.on('clients', data => {
  clientController.setClients(data);
})
control.on('connect', () => {
  connected = true;
  document.querySelector('#disconnected-warning').style.visibility = 'hidden';
});
control.on('disconnect', () => {
  connected = false;
  document.querySelector('#disconnected-warning').style.visibility = 'visible';
  playlistController.disconnect();
  errorController.disconnect();
  clientController.disconnect();
});
control.on('time', data => {
  lastUpdateFromServer = data.time;
  timeOfLastUpdateFromServer = window.performance.now();
});
control.on('error', e => {
  playlistController.error(e);
  errorController.error(e);
});
control.on('errors', es => {
  es.forEach(e => {
    errorController.error(e);
  });
});
control.on('new-client', c => {
  clientController.newClient(c);
});
control.on('lost-client', c => {
  clientController.lostClient(c);
});
control.on('wallGeometry', p => {
  clientController.setWallGeometry(p);
});
control.on('takeSnapshotRes', res => {
  clientController.takeSnapshotRes(res);
});

const openCreatorEl = document.querySelector('#open-creator');
openCreatorEl.addEventListener('click', () => {
  playlistCreator.open();
});

const timeEl = document.querySelector('#time');
const remainingEl = document.querySelector('#remaining');
function render() {
  timeEl.textContent = getTime().toFixed(0);
  const remainingMs = transitionData.nextDeadline - getTime();
  if (remainingMs < 0) {
    remainingEl.textContent = `Fading (${-remainingMs})`;
    remainingEl.classList.add('transitioning');
  } else {
    remainingEl.classList.remove('transitioning');
    remainingEl.textContent = convertMsDurationToText(remainingMs);
  }

  playlistController.render();

  window.requestAnimationFrame(render);
}
render();
<commit_msg>Fix a bug with the status control page. - Forgot to rename some 'emits' to 'sends'.<commit_after>/* Copyright 2019 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

import {ClientController} from './client_controller.js';
import {PlaylistController} from './playlist_controller.js';
import {ErrorController} from './error_controller.js';
import {PlaylistCreator} from './playlist_creator.js';
import {WS} from '/lib/websocket.js';
import {addLogger} from '/lib/log.js';
import {makeConsoleLogger} from '/lib/console_logger.js';

function makeConsoleColorFn(css) {
  const ret = str => {
    return `%c${str}%c`;
  }
  ret.desc = css;
  return ret;
}

addLogger(makeConsoleLogger(c => {
  const ret = makeConsoleColorFn([`color: ${c}`]);
  ret.bold = {
    bgRed: makeConsoleColorFn(['font-weight: bolder', 'background-color: red', `color: ${c}`]),
  };
  ret.bgBlue = makeConsoleColorFn(['background-color: blue', `color: ${c}`]);
  return ret;
}, () => performance.now()));

let lastUpdateFromServer = 0;
let timeOfLastUpdateFromServer = window.performance.now();
let connected = false;
function getTime() {
  if (!connected) {
    return lastUpdateFromServer;
  }
  return lastUpdateFromServer + window.performance.now() - timeOfLastUpdateFromServer;
}
const host = new URL(location).searchParams.get('host') || 'localhost:6001';
const control = WS.clientWrapper(`ws://${host}/`);
const creatorEl = document.querySelector('#playlist-creator');

function applyNewPlaylist(playlist, moduleConfig) {
  // TODO(applmak): Passing a string here is a bit hacky.
  if (playlist == 'reset') {
    control.send('resetPlaylist');
  } else {
    control.send('newPlaylist', {playlist, moduleConfig});
  }
}

const playlistCreator = new PlaylistCreator(creatorEl, applyNewPlaylist);
const playlistController = new PlaylistController(document.querySelector('.playlist-container'), getTime);
const errorController = new ErrorController(document.querySelector('footer'));
const clientController = new ClientController(
  document.querySelector('.diagram'),
  req => control.send('takeSnapshot', req),
  errorController,
  getTime,
);

function convertMsDurationToText(ms) {
  const secs = Math.floor(ms / 1000);
  const mins = Math.floor(secs / 60);

  return mins ? `${mins} minutes` : secs ? `${secs} seconds` : String(ms);
}

let transitionData = {};
control.on('transition', data => {
  transitionData = data;

  const duration = data.nextDeadline - data.deadline;
  const moduleNameEl = document.querySelector('#module');
  moduleNameEl.textContent = data.module;
  const durationEl = document.querySelector('#duration');
  durationEl.textContent = convertMsDurationToText(duration);

  playlistController.updateTransitionData(data);
  playlistCreator.setLivePlaylist(data.layouts);
  playlistCreator.setModuleConfig(data.configMap);
});
control.on('clients', data => {
  clientController.setClients(data);
})
control.on('connect', () => {
  connected = true;
  document.querySelector('#disconnected-warning').style.visibility = 'hidden';
});
control.on('disconnect', () => {
  connected = false;
  document.querySelector('#disconnected-warning').style.visibility = 'visible';
  playlistController.disconnect();
  errorController.disconnect();
  clientController.disconnect();
});
control.on('time', data => {
  lastUpdateFromServer = data.time;
  timeOfLastUpdateFromServer = window.performance.now();
});
control.on('error', e => {
  playlistController.error(e);
  errorController.error(e);
});
control.on('errors', es => {
  es.forEach(e => {
    errorController.error(e);
  });
});
control.on('new-client', c => {
  clientController.newClient(c);
});
control.on('lost-client', c => {
  clientController.lostClient(c);
});
control.on('wallGeometry', p => {
  clientController.setWallGeometry(p);
});
control.on('takeSnapshotRes', res => {
  clientController.takeSnapshotRes(res);
});

const openCreatorEl = document.querySelector('#open-creator');
openCreatorEl.addEventListener('click', () => {
  playlistCreator.open();
});

const timeEl = document.querySelector('#time');
const remainingEl = document.querySelector('#remaining');
function render() {
  timeEl.textContent = getTime().toFixed(0);
  const remainingMs = transitionData.nextDeadline - getTime();
  if (remainingMs < 0) {
    remainingEl.textContent = `Fading (${-remainingMs})`;
    remainingEl.classList.add('transitioning');
  } else {
    remainingEl.classList.remove('transitioning');
    remainingEl.textContent = convertMsDurationToText(remainingMs);
  }

  playlistController.render();

  window.requestAnimationFrame(render);
}
render();
<|endoftext|>


<commit_before>/*! Copyright (c) 2011 by Jonas Mosbech - https://github.com/jmosbech/StickyTableHeaders
    MIT license info: https://github.com/jmosbech/StickyTableHeaders/blob/master/license.txt */

;(function ($, window, undefined) {
	'use strict';

	var pluginName = 'stickyTableHeaders';
	var defaults = {
			fixedOffset: 0
		};

	function Plugin (el, options) {
		// To avoid scope issues, use 'base' instead of 'this'
		// to reference this class from internal events and functions.
		var base = this;

		// Access to jQuery and DOM versions of element
		base.$el = $(el);
		base.el = el;

		// Cache DOM refs for performance reasons
		base.$window = $(window);
		base.$clonedHeader = null;
		base.$originalHeader = null;

		// Keep track of state
		base.isCloneVisible = false;
		base.leftOffset = null;
		base.topOffset = null;

		base.init = function () {
			base.options = $.extend({}, defaults, options);

			base.$el.each(function () {
				var $this = $(this);

				// remove padding on
 to fix issue #7
				$this.css('padding', 0);

				$this.wrap('

');

				base.$originalHeader = $('thead:first', this);
				base.$clonedHeader = base.$originalHeader.clone();

				base.$clonedHeader.addClass('tableFloatingHeader');
				base.$clonedHeader.css({
					'position': 'fixed',
					'top': 0,
					'z-index': 1, // #18: opacity bug
					'display': 'none'
				});

				base.$originalHeader.addClass('tableFloatingHeaderOriginal');

				base.$originalHeader.after(base.$clonedHeader);

				// enabling support for jquery.tablesorter plugin
				// forward clicks on clone to original
				$('th', base.$clonedHeader).click(function (e) {
					var index = $('th', base.$clonedHeader).index(this);
					$('th', base.$originalHeader).eq(index).click();
				});
				$this.bind('sortEnd', base.updateWidth);
			});

			base.updateWidth();
			base.toggleHeaders();

			base.$window.scroll(base.toggleHeaders);
			base.$window.resize(base.toggleHeaders);
			base.$window.resize(base.updateWidth);
		};

		base.toggleHeaders = function () {
			base.$el.each(function () {
				var $this = $(this);

				var newTopOffset = isNaN(base.options.fixedOffset) ?
					base.options.fixedOffset.height() : base.options.fixedOffset;

				var offset = $this.offset();
				var scrollTop = base.$window.scrollTop() + newTopOffset;
				var scrollLeft = base.$window.scrollLeft();

				if ((scrollTop > offset.top) && (scrollTop < offset.top + $this.height())) {
					var newLeft = offset.left - scrollLeft;
					if (base.isCloneVisible && (newLeft === base.leftOffset) && (newTopOffset === base.topOffset)) {
						return;
					}

					base.$clonedHeader.css({
						'top': newTopOffset,
						'margin-top': 0,
						'left': newLeft,
						'display': 'block'
					});
					base.$originalHeader.css('visibility', 'hidden');
					base.isCloneVisible = true;
					base.leftOffset = newLeft;
					base.topOffset = newTopOffset;
				}
				else if (base.isCloneVisible) {
					base.$clonedHeader.css('display', 'none');
					base.$originalHeader.css('visibility', 'visible');
					base.isCloneVisible = false;
				}
			});
		};

		base.updateWidth = function () {
			// Copy cell widths and classes from original header
			$('th', base.$clonedHeader).each(function (index) {
				var $this = $(this);
				var origCell = $('th', base.$originalHeader).eq(index);
				$this.removeClass().addClass(origCell.attr('class'));
				$this.css('width', origCell.width());
			});

			// Copy row width from whole table
			base.$clonedHeader.css('width', base.$originalHeader.width());
		};

		// Run initializer
		base.init();
	}

	// A really lightweight plugin wrapper around the constructor,
	// preventing against multiple instantiations
	$.fn[pluginName] = function ( options ) {
		return this.each(function () {
			if (!$.data(this, 'plugin_' + pluginName)) {
				$.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
			}
		});
	};

})(jQuery, window);
<commit_msg>small optimization and naming change<commit_after>/*! Copyright (c) 2011 by Jonas Mosbech - https://github.com/jmosbech/StickyTableHeaders
    MIT license info: https://github.com/jmosbech/StickyTableHeaders/blob/master/license.txt */

;(function ($, window, undefined) {
	'use strict';

	var pluginName = 'stickyTableHeaders';
	var defaults = {
			fixedOffset: 0
		};

	function Plugin (el, options) {
		// To avoid scope issues, use 'base' instead of 'this'
		// to reference this class from internal events and functions.
		var base = this;

		// Access to jQuery and DOM versions of element
		base.$el = $(el);
		base.el = el;

		// Cache DOM refs for performance reasons
		base.$window = $(window);
		base.$clonedHeader = null;
		base.$originalHeader = null;

		// Keep track of state
		base.isCloneVisible = false;
		base.leftOffset = null;
		base.topOffset = null;

		base.init = function () {
			base.options = $.extend({}, defaults, options);

			base.$el.each(function () {
				var $this = $(this);

				// remove padding on
 to fix issue #7
				$this.css('padding', 0);

				$this.wrap('

');

				base.$originalHeader = $('thead:first', this);
				base.$clonedHeader = base.$originalHeader.clone();

				base.$clonedHeader.addClass('tableFloatingHeader');
				base.$clonedHeader.css({
					'position': 'fixed',
					'top': 0,
					'z-index': 1, // #18: opacity bug
					'display': 'none'
				});

				base.$originalHeader.addClass('tableFloatingHeaderOriginal');

				base.$originalHeader.after(base.$clonedHeader);

				// enabling support for jquery.tablesorter plugin
				// forward clicks on clone to original
				$('th', base.$clonedHeader).click(function (e) {
					var index = $('th', base.$clonedHeader).index(this);
					$('th', base.$originalHeader).eq(index).click();
				});
				$this.bind('sortEnd', base.updateWidth);
			});

			base.updateWidth();
			base.toggleHeaders();

			base.$window.scroll(base.toggleHeaders);
			base.$window.resize(base.toggleHeaders);
			base.$window.resize(base.updateWidth);
		};

		base.toggleHeaders = function () {
			base.$el.each(function () {
				var $this = $(this);

				var newTopOffset = isNaN(base.options.fixedOffset) ?
					base.options.fixedOffset.height() : base.options.fixedOffset;

				var offset = $this.offset();
				var scrollTop = base.$window.scrollTop() + newTopOffset;
				var scrollLeft = base.$window.scrollLeft();

				if ((scrollTop > offset.top) && (scrollTop < offset.top + $this.height())) {
					var newLeft = offset.left - scrollLeft;
					if (base.isCloneVisible && (newLeft === base.leftOffset) && (newTopOffset === base.topOffset)) {
						return;
					}

					base.$clonedHeader.css({
						'top': newTopOffset,
						'margin-top': 0,
						'left': newLeft,
						'display': 'block'
					});
					base.$originalHeader.css('visibility', 'hidden');
					base.isCloneVisible = true;
					base.leftOffset = newLeft;
					base.topOffset = newTopOffset;
				}
				else if (base.isCloneVisible) {
					base.$clonedHeader.css('display', 'none');
					base.$originalHeader.css('visibility', 'visible');
					base.isCloneVisible = false;
				}
			});
		};

		base.updateWidth = function () {
			// Copy cell widths and classes from original header
			$('th', base.$clonedHeader).each(function (index) {
				var $this = $(this);
				var $origCell = $('th', base.$originalHeader).eq(index);
				this.className = $origCell.attr('class') || '';
				$this.css('width', $origCell.width());
			});

			// Copy row width from whole table
			base.$clonedHeader.css('width', base.$originalHeader.width());
		};

		// Run initializer
		base.init();
	}

	// A really lightweight plugin wrapper around the constructor,
	// preventing against multiple instantiations
	$.fn[pluginName] = function ( options ) {
		return this.each(function () {
			if (!$.data(this, 'plugin_' + pluginName)) {
				$.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
			}
		});
	};

})(jQuery, window);
<|endoftext|>


<commit_before>'use strict';
if (typeof Levels === 'undefined') var Levels = {};

Levels.RocketLandingMulti = function()
{
	this.name = "RocketLandingMulti";
	this.title = "Multiple Rocket Landing";
	this.boilerPlateCode = "function controlFunction(rocket)\n{\n  return {throttle:1,gimbalAngle:-0.2};\n}";
	this.sampleSolution = "// Unsolved, sorry.\nfunction controlFunction(rocket)\n{\n  return {throttle:1,gimbalAngle:-0.2};\n}";
	this.description = "Now there are multiple rockets. The point of this level is to see if the same controller can land the rocket from different initial conditions. Collision avoidance between the different rockets is not necessary. They pass through each other.";
	this.models = [];
	this.models.push(new Models.RocketLanding({TWR: 2,theta: 0,dtheta: -0.1,Length: 40,Width: 5,x: 100,dx: 20,y: 300,dy: -10,T: 0}));
	this.models.push(new Models.RocketLanding({TWR: 2,theta: 0,dtheta: 0,   Length: 40,Width: 5,x: -180,dx: 50,y: 60,dy: 0,T: 0}));
	this.models.push(new Models.RocketLanding({TWR: 2,theta: 3.14,dtheta: -2,   Length: 40,Width: 5,x: -180,dx: 0,y: 300,dy: 0,T: 0}));
}

Levels.RocketLandingMulti.prototype.levelComplete = function(){return this.models.every(function(m){return m.landed();});}

Levels.RocketLandingMulti.prototype.levelFailed = function(){return this.models.some(function(m){return m.crashed();});}

Levels.RocketLandingMulti.prototype.simulate = function (dt, controlFunc)
{
	for (var i = 0; i < this.models.length; i++)
		this.models[i] = this.models[i].simulate (dt, controlFunc);
}

Levels.RocketLandingMulti.prototype.getSimulationTime = function()	{return this.models[0].T;}

Levels.RocketLandingMulti.prototype.draw = function (ctx, canvas)
{
	// clear canvas
	ctx.setTransform(1,0,0,1,0,0);
	ctx.clearRect(0,0,canvas.width,canvas.height);

	ctx.translate(canvas.width/2,0.95*canvas.height);
	ctx.scale(2,-2);

	for (var i = 0; i < this.models.length; i++){
		this.models[i].drawRocket(ctx, canvas, i);
		this.models[i].drawGround(ctx, canvas);
	}

	if(this.levelFailed())
	{
		ctx.save();
		ctx.scale(1,-1);
		ctx.font="10px Verdana";
		ctx.textAlign="center";
		ctx.fillStyle="#990000";
		ctx.fillText("CRASHED!",0,-80);
		ctx.restore();
	}
}

Levels.RocketLandingMulti.prototype.infoText = function(ctx, canvas){
	var s = '';
	for (var i = 0; i < this.models.length; i++) {
		if (i!=0) s+="\n";
		s += "Rocket #" + i + "\n";
		s += this.models[i].infoText();
	}
	return s;
}<commit_msg>RocketLandingMulti.js sample soln<commit_after>'use strict';
if (typeof Levels === 'undefined') var Levels = {};

Levels.RocketLandingMulti = function()
{
	this.name = "RocketLandingMulti";
	this.title = "Multiple Rocket Landing";
	this.boilerPlateCode = "function controlFunction(rocket)\n{\n  return {throttle:1,gimbalAngle:-0.2};\n}";
	this.sampleSolution = "function limit_to(lower,upper,x){ return Math.max(lower, Math.min(upper, x)); }\n\nfunction controlFunction(rocket)\n{\n  rocket = rocket.simulate(1,function(){return {throttle:.5,gimbalAngle:0};});\n  var a=0;\n  var t=.5;\n  var x = Math.max(0,Math.abs(rocket.x)-20) * Math.sign(rocket.x);\n  var targetAngle = -0.02*(x + 0.5*(Math.abs(rocket.dx)+1)*rocket.dx);\n  a += 30*rocket.dtheta;\n  a += 15*(rocket.theta-limit_to(-1,1,targetAngle));\n  t -= limit_to(0.06,0.1,Math.abs(x))*rocket.dy;\n  var y_ref = 50 - 29 * limit_to(0,1,1 - 0.5*Math.abs(rocket.dx) - 10*Math.abs(rocket.theta));\n  t += limit_to(-.5,.5,1e-1*(y_ref-rocket.y));\n  return {throttle:t,gimbalAngle:a};\n}";
	this.description = "Now there are multiple rockets. The point of this level is to see if the same controller can land the rocket from different initial conditions. Collision avoidance between the different rockets is not necessary. They pass through each other.";
	this.models = [];
	this.models.push(new Models.RocketLanding({TWR: 2,theta: 0,dtheta: -0.1,Length: 40,Width: 5,x: 100,dx: 20,y: 300,dy: -10,T: 0}));
	this.models.push(new Models.RocketLanding({TWR: 2,theta: 0,dtheta: 0,   Length: 40,Width: 5,x: -180,dx: 50,y: 60,dy: 0,T: 0}));
	this.models.push(new Models.RocketLanding({TWR: 2,theta: 3.14,dtheta: -2,   Length: 40,Width: 5,x: -180,dx: 0,y: 300,dy: 0,T: 0}));
}

Levels.RocketLandingMulti.prototype.levelComplete = function(){return this.models.every(function(m){return m.landed();});}

Levels.RocketLandingMulti.prototype.levelFailed = function(){return this.models.some(function(m){return m.crashed();});}

Levels.RocketLandingMulti.prototype.simulate = function (dt, controlFunc)
{
	for (var i = 0; i < this.models.length; i++)
		this.models[i] = this.models[i].simulate (dt, controlFunc);
}

Levels.RocketLandingMulti.prototype.getSimulationTime = function()	{return this.models[0].T;}

Levels.RocketLandingMulti.prototype.draw = function (ctx, canvas)
{
	// clear canvas
	ctx.setTransform(1,0,0,1,0,0);
	ctx.clearRect(0,0,canvas.width,canvas.height);

	ctx.translate(canvas.width/2,0.95*canvas.height);
	ctx.scale(2,-2);

	for (var i = 0; i < this.models.length; i++){
		this.models[i].drawRocket(ctx, canvas, i);
		this.models[i].drawGround(ctx, canvas);
	}

	if(this.levelFailed())
	{
		ctx.save();
		ctx.scale(1,-1);
		ctx.font="10px Verdana";
		ctx.textAlign="center";
		ctx.fillStyle="#990000";
		ctx.fillText("CRASHED!",0,-80);
		ctx.restore();
	}
}

Levels.RocketLandingMulti.prototype.infoText = function(ctx, canvas){
	var s = '';
	for (var i = 0; i < this.models.length; i++) {
		if (i!=0) s+="\n";
		s += "Rocket #" + i + "\n";
		s += this.models[i].infoText();
	}
	return s;
}<|endoftext|>


<commit_before>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * The user's current locale
 */
Saiku.i18n = {
    locale: (navigator.language || navigator.browserLanguage ||
        navigator.systemLanguage || navigator.userLanguage).substring(0, 2).toLowerCase(),
    po_file: {},
    translate: function () {
        $('.i18n').i18n(Saiku.i18n.po_file);
    },
    automatic_i18n: function () {
        // Load language file if it isn't English

        //compatible 'zh-CN';
        if (Saiku.i18n.locale == 'zh-CN') Saiku.i18n.locale = 'cn';

        if (Saiku.i18n.locale != "en") {
            $.ajax({
                url: "js/saiku/plugins/I18n/po/" + Saiku.i18n.locale + ".json",
                type: 'GET',
                dataType: 'json',
                success: function (data) {
                    Saiku.i18n.po_file = data;
                    Saiku.i18n.translate();
                }
            });
        }

        return true;
    },
    elements: [],
    improve_translation: function () {
        Saiku.tabs.add(new TranslationTab());
        return false;
    }
};

/**
 * jQuery plugin for i18n
 */
(function( $ ){
	/**
	 * Internationalize selected elements with the provided PO file
	 */
	$.fn.i18n = function(po_file) {
		// If no PO file is provided, then don't translate anything
		if (! po_file)
			return this;

		// If key is not found, return original language
		var translate = function(key, po_file) {
			if (typeof po_file[key] == "undefined") {
				return "";
			} else {
				return po_file[key];
			}
		};

		// Iterate over UI elements that need to be translated
		return $.each(this, function() {
			element = $(this);

			// Translate text
			if (element.html()) {
				translated_text = translate( element.html(), po_file );
                if (Saiku.i18n.elements.indexOf &&
                    Saiku.i18n.elements.indexOf(element.html()) === -1) {
                    Saiku.i18n.elements.push(element.html());
                }
				if (translated_text) {
					element.data('original', element.html());
					element.html(translated_text);
					element.removeClass('i18n');
				}
			}

			// Translate title
			if (element.attr('title')) {
				translated_title = translate( element.attr('title'), po_file );
                if (Saiku.i18n.elements.indexOf &&
                    Saiku.i18n.elements.indexOf(element.attr('title')) === -1) {
                    Saiku.i18n.elements.push(element.attr('title'));
                }
				if (translated_title) {
					element.data('original', element.attr('title'));
					element.attr({ 'title': translated_title });
					element.removeClass('i18n');
				}
			}

			// Remove class so this element isn't repeatedly translated
			if (element.hasClass('i18n')) {
			    element.addClass('i18n_failed');
			}
			element.addClass('i18n_translated');
		});
	};

	$.fn.un_i18n = function() {
		// Iterate over UI elements to replace the original text
		return $.each(this, function() {
			element = $(this);

			if (element.text())
				element.text(element.data('original'));

			if (element.attr('title'))
				element.attr({ 'title': element.data('original') });

			element.addClass('i18n');
			element.removeClass('i18n_translated')
			    .removeClass('i18n_failed');
		});
	};
})( jQuery );

/**
 * Backbone view which allows users to submit translations
 */
var TranslationTab = Backbone.View.extend({
    className: 'workspace_area',
    caption: function() {
        return Saiku.i18n.po_file["Improve this translation"] ?
            Saiku.i18n.po_file["Improve this translation"] :
            "Improve this translation";
    },
    events: {
        'submit form': 'submit',
        'change input': 'mark'
    },
    initialize: function() {
        $(window).resize(this.adjust);
        $(this.el).focus(this.adjust);
        this.adjust();
    },
    render: function() {
        var translation_table = {};
        for (var i = 0; i < Saiku.i18n.elements.length; i++) {
            translation_table[Saiku.i18n.elements[i]] = {
                value: Saiku.i18n.po_file[Saiku.i18n.elements[i]],
                name: encodeURI(Saiku.i18n.elements[i])
            };
        }
        var table = _.template("<form class='workspace_results'>" +
        	"Your name: <input type='text' name='translator_name' />" +
            "
Please fill in the appropriate translation in the blanks provided:
" +
        	"<% _.each(translation_table, function(val, key) { %>" +
            "
<%= key %>
" +
            "<input type='text' value='<%= val.value %>' name='<%= val.name %>' />" +
            "
" +
            "<% }); %>" +
            "
<input class='submit-translation' type='submit' value='Submit translation' />
" +
            "</form>")({
                translation_table: translation_table
            });
        $(this.el).html(table).find('div').css({
            'float': 'left',
            'padding': '20px'
        }).find('input').css({
            'width': '300px'
        });
        $(this.el).find('.submit-translation').css({
            'padding': '20px'
        });
    },

    mark: function(event) {
        $(event.target).addClass('changed');
    },

    submit: function() {
        var translation = { locale: Saiku.i18n.locale };
        $(this.el).find('.changed').each(function(element) {
            translation[decodeURI($(this).attr('name'))] = encodeURI($(this).val());
        });
        Translate.log(translation);
        Saiku.ui.block('Thank you for improving our translation!');
        this.tab.remove();
        _.delay(function() {
            Saiku.ui.unblock();
        }, 1000);
        return false;
    },

    adjust: function() {
        $(this.el).height($("body").height() - 87);
    }
});

/**
 * Automatically internationalize the UI based on the user's locale
 */
Saiku.i18n.automatic_i18n();

/**
 * Add translate button
 */
Saiku.events.bind('toolbar:render', function(args) {
    if (Saiku.i18n.locale != "en") {
        var $link = $("").text(Saiku.i18n.locale)
            .attr({
                href: "#translate",
                title: "Improve this translation"
            })
            .click(Saiku.i18n.improve_translation)
            .addClass('sprite translate i18n');
        var $li = $("
").append($link);
        $(args.toolbar.el).find('ul').append($li);
    }
});

/**
 * Bind to new workspace
 */
Saiku.events.bind('session:new', function() {
    // Translate elements already rendered
    Saiku.i18n.translate();

    // Translate new workspaces
    Saiku.session.bind('tab:add', Saiku.i18n.translate);
});

/**
 * Initialize Loggly input for user-provided translations
 */
if (window.logger) {
    window.Translate = new logger({
        url: Settings.TELEMETRY_SERVER + '/input/translations'
    });
}
<commit_msg>=zh-CN<commit_after>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * The user's current locale
 */
Saiku.i18n = {
    locale: (navigator.language || navigator.browserLanguage ||
        navigator.systemLanguage || navigator.userLanguage).substring(0, 2).toLowerCase(),
    po_file: {},
    translate: function () {
        $('.i18n').i18n(Saiku.i18n.po_file);
    },
    automatic_i18n: function () {
        // Load language file if it isn't English

        //compatible 'zh-CN' -> 'zh';
        if (Saiku.i18n.locale == 'zh') Saiku.i18n.locale = 'cn';

        if (Saiku.i18n.locale != "en") {
            $.ajax({
                url: "js/saiku/plugins/I18n/po/" + Saiku.i18n.locale + ".json",
                type: 'GET',
                dataType: 'json',
                success: function (data) {
                    Saiku.i18n.po_file = data;
                    Saiku.i18n.translate();
                }
            });
        }

        return true;
    },
    elements: [],
    improve_translation: function () {
        Saiku.tabs.add(new TranslationTab());
        return false;
    }
};

/**
 * jQuery plugin for i18n
 */
(function( $ ){
	/**
	 * Internationalize selected elements with the provided PO file
	 */
	$.fn.i18n = function(po_file) {
		// If no PO file is provided, then don't translate anything
		if (! po_file)
			return this;

		// If key is not found, return original language
		var translate = function(key, po_file) {
			if (typeof po_file[key] == "undefined") {
				return "";
			} else {
				return po_file[key];
			}
		};

		// Iterate over UI elements that need to be translated
		return $.each(this, function() {
			element = $(this);

			// Translate text
			if (element.html()) {
				translated_text = translate( element.html(), po_file );
                if (Saiku.i18n.elements.indexOf &&
                    Saiku.i18n.elements.indexOf(element.html()) === -1) {
                    Saiku.i18n.elements.push(element.html());
                }
				if (translated_text) {
					element.data('original', element.html());
					element.html(translated_text);
					element.removeClass('i18n');
				}
			}

			// Translate title
			if (element.attr('title')) {
				translated_title = translate( element.attr('title'), po_file );
                if (Saiku.i18n.elements.indexOf &&
                    Saiku.i18n.elements.indexOf(element.attr('title')) === -1) {
                    Saiku.i18n.elements.push(element.attr('title'));
                }
				if (translated_title) {
					element.data('original', element.attr('title'));
					element.attr({ 'title': translated_title });
					element.removeClass('i18n');
				}
			}

			// Remove class so this element isn't repeatedly translated
			if (element.hasClass('i18n')) {
			    element.addClass('i18n_failed');
			}
			element.addClass('i18n_translated');
		});
	};

	$.fn.un_i18n = function() {
		// Iterate over UI elements to replace the original text
		return $.each(this, function() {
			element = $(this);

			if (element.text())
				element.text(element.data('original'));

			if (element.attr('title'))
				element.attr({ 'title': element.data('original') });

			element.addClass('i18n');
			element.removeClass('i18n_translated')
			    .removeClass('i18n_failed');
		});
	};
})( jQuery );

/**
 * Backbone view which allows users to submit translations
 */
var TranslationTab = Backbone.View.extend({
    className: 'workspace_area',
    caption: function() {
        return Saiku.i18n.po_file["Improve this translation"] ?
            Saiku.i18n.po_file["Improve this translation"] :
            "Improve this translation";
    },
    events: {
        'submit form': 'submit',
        'change input': 'mark'
    },
    initialize: function() {
        $(window).resize(this.adjust);
        $(this.el).focus(this.adjust);
        this.adjust();
    },
    render: function() {
        var translation_table = {};
        for (var i = 0; i < Saiku.i18n.elements.length; i++) {
            translation_table[Saiku.i18n.elements[i]] = {
                value: Saiku.i18n.po_file[Saiku.i18n.elements[i]],
                name: encodeURI(Saiku.i18n.elements[i])
            };
        }
        var table = _.template("<form class='workspace_results'>" +
        	"Your name: <input type='text' name='translator_name' />" +
            "
Please fill in the appropriate translation in the blanks provided:
" +
        	"<% _.each(translation_table, function(val, key) { %>" +
            "
<%= key %>
" +
            "<input type='text' value='<%= val.value %>' name='<%= val.name %>' />" +
            "
" +
            "<% }); %>" +
            "
<input class='submit-translation' type='submit' value='Submit translation' />
" +
            "</form>")({
                translation_table: translation_table
            });
        $(this.el).html(table).find('div').css({
            'float': 'left',
            'padding': '20px'
        }).find('input').css({
            'width': '300px'
        });
        $(this.el).find('.submit-translation').css({
            'padding': '20px'
        });
    },

    mark: function(event) {
        $(event.target).addClass('changed');
    },

    submit: function() {
        var translation = { locale: Saiku.i18n.locale };
        $(this.el).find('.changed').each(function(element) {
            translation[decodeURI($(this).attr('name'))] = encodeURI($(this).val());
        });
        Translate.log(translation);
        Saiku.ui.block('Thank you for improving our translation!');
        this.tab.remove();
        _.delay(function() {
            Saiku.ui.unblock();
        }, 1000);
        return false;
    },

    adjust: function() {
        $(this.el).height($("body").height() - 87);
    }
});

/**
 * Automatically internationalize the UI based on the user's locale
 */
Saiku.i18n.automatic_i18n();

/**
 * Add translate button
 */
Saiku.events.bind('toolbar:render', function(args) {
    if (Saiku.i18n.locale != "en") {
        var $link = $("").text(Saiku.i18n.locale)
            .attr({
                href: "#translate",
                title: "Improve this translation"
            })
            .click(Saiku.i18n.improve_translation)
            .addClass('sprite translate i18n');
        var $li = $("
").append($link);
        $(args.toolbar.el).find('ul').append($li);
    }
});

/**
 * Bind to new workspace
 */
Saiku.events.bind('session:new', function() {
    // Translate elements already rendered
    Saiku.i18n.translate();

    // Translate new workspaces
    Saiku.session.bind('tab:add', Saiku.i18n.translate);
});

/**
 * Initialize Loggly input for user-provided translations
 */
if (window.logger) {
    window.Translate = new logger({
        url: Settings.TELEMETRY_SERVER + '/input/translations'
    });
}
<|endoftext|>