<commit_before>/*global THREE*/
(function () {

  // Simulation
  // ----------

  var PTCL = Particulate;
  var PARTICLES = 150;
  var LINK_DISTANCE = 1;
  var GRAVITY = -0.05;
  var system = new PTCL.ParticleSystem(PARTICLES, 2);
  var bounds = new PTCL.BoxConstraint(-50, -50, -50, 50, 50, 50);
  var gravityForce = new PTCL.DirectionalForce();

  system.each(function (i) {
    if (i > 0) {
      system.setPosition(i,
        (Math.random() - 0.5) * 20,
        (Math.random() - 0.5) * 20,
        (Math.random() - 0.5) * 20);

      system.addConstraint(new PTCL.DistanceConstraint(LINK_DISTANCE, i - 1, i));
    }
  });

  system.addConstraint(bounds);
  system.addForce(gravityForce);

  // Visualization
  // -------------

  var demo = new PTCL.DemoScene();
  demo.camera.position.set(0, 200, 500);

  var geometry = new THREE.BufferGeometry();
  var position = new THREE.BufferAttribute();
  position.array = system.positions;
  position.itemSize = 3;

  geometry.addAttribute('position', position);
  geometry.computeBoundingSphere();

  var material = new THREE.ParticleSystemMaterial({size: 2});
  var particleSystem = new THREE.ParticleSystem(geometry, material);
  demo.scene.add(particleSystem);

  var box = new THREE.Mesh(
    new THREE.BoxGeometry(100, 100, 100, 1, 1, 1),
    new THREE.MeshBasicMaterial({
      wireframe : true
    }));
  demo.scene.add(box);

  var up = demo.controls.object.up;

  demo.animate(function () {
    gravityForce.set(up.x * GRAVITY, up.y * GRAVITY, up.z * GRAVITY);
    system.tick(1);
    system.setPosition(0, 0, 0, 0);
    geometry.attributes.position.needsUpdate = true;
    demo.update();
    demo.render();
  });
}());
<commit_msg>Update chain example<commit_after>/*global THREE*/
(function () {

  // Simulation
  // ----------

  var PTCL = Particulate;
  var PARTICLES = 150;
  var LINK_DISTANCE = 1;
  var GRAVITY = -0.05;
  var system = new PTCL.ParticleSystem(PARTICLES, 2);
  var bounds = new PTCL.BoxConstraint(-50, -50, -50, 50, 50, 50);
  var gravityForce = new PTCL.DirectionalForce();

  system.each(function (i) {
    if (i > 0 && i < PARTICLES - 1) {
      system.setPosition(i,
        (Math.random() - 0.5) * 20,
        (Math.random() - 0.5) * 20,
        (Math.random() - 0.5) * 20);
    }

    if (i > 0) {
      system.addConstraint(new PTCL.DistanceConstraint(LINK_DISTANCE, i - 1, i));
    }
  });

  system.addConstraint(bounds);
  system.addForce(gravityForce);

  // Visualization
  // -------------

  var demo = new PTCL.DemoScene();
  demo.camera.position.set(0, 200, 500);

  var geometry = new THREE.BufferGeometry();
  var position = new THREE.BufferAttribute();
  position.array = system.positions;
  position.itemSize = 3;

  geometry.addAttribute('position', position);
  geometry.computeBoundingSphere();

  var material = new THREE.ParticleSystemMaterial({size: 2});
  var particleSystem = new THREE.ParticleSystem(geometry, material);
  demo.scene.add(particleSystem);

  var box = new THREE.Mesh(
    new THREE.BoxGeometry(100, 100, 100, 1, 1, 1),
    new THREE.MeshBasicMaterial({
      wireframe : true
    }));
  demo.scene.add(box);

  var up = demo.controls.object.up;

  demo.animate(function () {
    gravityForce.set(up.x * GRAVITY, up.y * GRAVITY, up.z * GRAVITY);
    system.tick(1);
    system.setPosition(0, -10, 0, 0);
    system.setPosition(PARTICLES - 1, 10, 0, 0);
    geometry.attributes.position.needsUpdate = true;
    demo.update();
    demo.render();
  });
}());
<|endoftext|>


<commit_before>import Ember from 'ember';

var LeaveEditionModal = Ember.ObjectController.extend({
  masterController: null,
  haltedTransition: null,

  setupModal: function(masterController, haltedTransition) {
    this.set('model', masterController.get('model'));
    this.set('masterController', masterController);
    this.set('haltedTransition', haltedTransition);
  },

  actions: {
    confirm: function() {
      var model = this.get('model');
      var masterController = this.get('masterController');
      var haltedTransition = this.get('haltedTransition');

      if (Ember.isNone(haltedTransition) || Ember.isNone(masterController) || Ember.isNone(model)) {
          this.get('flashes').danger('An error occured.');

          // bubble the action
          return true;
      }

      masterController.rollbackEdition();

      haltedTransition.retry();
    }
  },

  // i18n for attributes values and components parameters
  i18n: function() {
    return {
      leaveQuestion: this.t('edition.leaveQuestion'),
      leave: this.t('edition.leave'),
      stay: this.t('edition.stay')
    };
  }.property() // @todo Watch current language
});

export default LeaveEditionModal;
<commit_msg>Fixes content edition leaving<commit_after>import Ember from 'ember';

var LeaveEditionModal = Ember.ObjectController.extend({
  masterController: null,
  haltedTransition: null,

  setupModal: function(masterController, haltedTransition) {
    this.set('model', masterController.get('model'));
    this.set('masterController', masterController);
    this.set('haltedTransition', haltedTransition);
  },

  actions: {
    confirm: function() {
      var model = this.get('model');
      var masterController = this.get('masterController');
      var haltedTransition = this.get('haltedTransition');

      if (Ember.isNone(haltedTransition) || Ember.isNone(masterController) || Ember.isNone(model)) {
          this.get('flashes').danger('An error occured.');

          // bubble the action
          return true;
      }

      masterController.rollbackEdition();

      // this is necessary so that transition retry works
      masterController.set('isDirty', false);

      haltedTransition.retry();
    }
  },

  // i18n for attributes values and components parameters
  i18n: function() {
    return {
      leaveQuestion: this.t('edition.leaveQuestion'),
      leave: this.t('edition.leave'),
      stay: this.t('edition.stay')
    };
  }.property() // @todo Watch current language
});

export default LeaveEditionModal;
<|endoftext|>


<commit_before>/// dependencies: Arte.js
(function ($) {
    $.Arte = $.Arte || {};
    $.Arte.TextArea = function (options) {
        var me = this;
        var configuration = $.Arte.configuration;
        var constants = $.Arte.constants;

        me.$element = $(options.element);
        // Create a mix-in of the user provided values and configuration defined default values
        var initialValues = $.extend({}, configuration.initialValues, options);

        var eventNames = constants.eventNames;
        this.editorType = options.editorType || constants.editorTypes.richText;

        //Stores the internal value (innerHTML) of the field
        this.currentValue = "";

        //Timer used to check for changes to the value, selection, and focus of the textarea
        var pollTimer = null;

        // Uses polling to trigger value change as user can change the value of the text field in multiple ways.
        // for example (keyboard, IME input, paste, multi-stroke keyboard, and context menu).
        var startPollingForValueChange = function () {
            if (!pollTimer) {
                pollTimer = setInterval(function () {
                    if (me.outerValue() != me.currentValue) {
                        var newValue = me.outerValue();
                        me.triggerEvent(eventNames.onvaluechange, { newValue: newValue, oldValue: me.currentValue });
                        me.currentValue = newValue;
                    }
                }, options.pollIntervalInMs);
            }
        };

        // Construct a dom element to host richtext editor or use if one already exist
        if (me.$element.children().length === 0) {
            me.$el = (me.editorType === constants.editorTypes.richText) ?
                $("<div>").attr({ contentEditable: "true" }) :
                me.$el = $("<textarea>").css({ height: "100%", width: "100%", padding: "0px", border: "0px" });
            me.$element.append(me.$el);
        }
        else {
            me.$el = me.$element.children().first();
            if (me.$el.is("div")) {
                me.$el.attr({ contentEditable: "true" });
            }
        }
        me.$el.css(initialValues.styles);
        $.each(initialValues.classes, function (index, className) {
            me.$el.addClass(className);
        });

        me.$element.attr(configuration.textFieldIdentifier, "1");

        /*
        * Whether the element has the focus
        */
        var isFocused = false;

        /*
        * Listen for the dom events on the text area or the content editable element.
        */
        me.$el.on({
            keydown: function (e) {
                me.triggerEvent(eventNames.onkeydown, { originalEvent: e });
                e.stopPropagation();
            },
            keyup: function (e) {
                me.triggerEvent(eventNames.onkeyup, { originalEvent: e });
                e.stopPropagation();
            },
            keypress: function (e) {
                me.triggerEvent(eventNames.onkeypress, { originalEvent: e });
                e.stopPropagation();
            },
            focus: function (e) {
                if (!isFocused) {
                    isFocused = true;
                    me.triggerEvent(eventNames.onfocus, { originalEvent: e });
                }
                startPollingForValueChange();
                e.stopPropagation();
            },
            blur: function (e) {
                isFocused = false;
                me.triggerEvent(eventNames.onselectionchange);
                me.triggerEvent(eventNames.onblur, { originalEvent: e });

                // Clear the value changed poll timer
                if (pollTimer) {
                    clearInterval(pollTimer);
                    pollTimer = null;
                }
                e.stopPropagation();
            },
            mouseup: function (e) {
                me.triggerEvent(eventNames.onselectionchange);
                me.triggerEvent(eventNames.onmouseup, { originalEvent: e });
            },
            mousedown: function (e) {
                me.triggerEvent(eventNames.onmousedown, { originalEvent: e });
            },
            click: function (e) {
                me.triggerEvent(eventNames.onclick, { originalEvent: e });
            },
            paste: function (e) {
                setTimeout(function () {
                    me.triggerEvent(eventNames.onpaste, { originalEvent: e });
                }, 50);
            }
        });
        $.Arte.pluginManager.init(me);

        me.value(initialValues.value);
        me.currentValue = me.outerValue();

        me.$element.on(options.on);
        me.triggerEvent(eventNames.oncreate);
    };

    $.extend($.Arte.TextArea.prototype, {
        // Get innerHtml of the contentEditable element
        "value": function (value) {
            var constants = $.Arte.constants;
            var op = this.editorType === constants.editorTypes.richText ? "html" : "val";

            if (typeof (value) === "undefined") {
                if ($.Arte.configuration.handleUnsanctionedTagsOnGetValue) {
                    // Save current selection
                    var savedSelection = rangy.saveSelection();
                    $.Arte.dom.handleUnsanctionedElements(this.$el.contents());
                    rangy.restoreSelection(savedSelection);
                }
                this.currentValue = this.$el[op]();
                return this.currentValue;
            }

            if (this.currentValue === value) {
                return;
            }

            var oldValue = this.$el[op]();
            this.currentValue = value;
            // Set the inner text
            this.$el[op](value);
            this.triggerEvent(constants.eventNames.onvaluechange, { newValue: this.currentValue, oldValue: oldValue });
        },
        // Get outerHtml of the contentEditable
        "outerValue": function (value) {
            if (typeof (value) === "undefined") {
                var clone = this.$element.clone();
                clone.children().removeAttr("contenteditable");
                return clone.html();
            }
            var newElement = $(value);

            if ($.Arte.dom.isEqual(this.$el, newElement)) {
                return;
            }

            this.$el.removeAttr("style"); // Clear the styles
            this.$el.attr("style", newElement.attr("style"));

            this.$el.removeAttr("class");
            this.$el.attr("class", newElement.attr("class"));

            this.value(newElement.html());
        },
        "focus": function () {
            var me = this;
            var focusHandler = function () {
                me.$el.off("focus", focusHandler);
                $.Arte.util.moveCursorToEndOfElement(me.$el.get(0));
                me.triggerEvent($.Arte.constants.eventNames.onselectionchange);
            };

            me.$el.on("focus", focusHandler);
            me.$el.focus();
        },
        "triggerEvent": function (name, data) {
            this.$element.trigger(name, $.extend(data, { textArea: this }));
        },
        "destroy": function (options) {
            // Converts the rich text editor to non-editable state and remove rich text state information
            this.$element.removeData("Arte");
            this.$element.removeAttr($.Arte.configuration.textFieldIdentifier);
            this.$element.off();

            this.$el.off();
            this.$el.removeAttr("contentEditable");
            this.triggerEvent($.Arte.constants.eventNames.ondestroy);

            if (options && options.removeContent) {
                this.$element.empty();
            }
        },
        /**
        *  on/off methods to support attaching events handler using a rich text instance
        */
        on: function (type, handler) {
            this.$element.on(type, handler);
        },
        off: function (type, handler) {
            this.$element.off(type, handler);
        }
    });
})(jQuery);<commit_msg>Fixed a typo in text area.<commit_after>/// dependencies: Arte.js
(function ($) {
    $.Arte = $.Arte || {};
    $.Arte.TextArea = function (options) {
        var me = this;
        var configuration = $.Arte.configuration;
        var constants = $.Arte.constants;

        me.$element = $(options.element);
        // Create a mix-in of the user provided values and configuration defined default values
        var initialValues = $.extend({}, configuration.initialValues, options);

        var eventNames = constants.eventNames;
        this.editorType = options.editorType || constants.editorTypes.richText;

        //Stores the internal value (innerHTML) of the field
        this.currentValue = "";

        //Timer used to check for changes to the value, selection, and focus of the textarea
        var pollTimer = null;

        // Uses polling to trigger value change as user can change the value of the text field in multiple ways.
        // for example (keyboard, IME input, paste, multi-stroke keyboard, and context menu).
        var startPollingForValueChange = function () {
            if (!pollTimer) {
                pollTimer = setInterval(function () {
                    if (me.outerValue() != me.currentValue) {
                        var newValue = me.outerValue();
                        me.triggerEvent(eventNames.onvaluechange, { newValue: newValue, oldValue: me.currentValue });
                        me.currentValue = newValue;
                    }
                }, configuration.pollIntervalInMs);
            }
        };

        // Construct a dom element to host richtext editor or use if one already exist
        if (me.$element.children().length === 0) {
            me.$el = (me.editorType === constants.editorTypes.richText) ?
                $("<div>").attr({ contentEditable: "true" }) :
                me.$el = $("<textarea>").css({ height: "100%", width: "100%", padding: "0px", border: "0px" });
            me.$element.append(me.$el);
        }
        else {
            me.$el = me.$element.children().first();
            if (me.$el.is("div")) {
                me.$el.attr({ contentEditable: "true" });
            }
        }
        me.$el.css(initialValues.styles);
        $.each(initialValues.classes, function (index, className) {
            me.$el.addClass(className);
        });

        me.$element.attr(configuration.textFieldIdentifier, "1");

        /*
        * Whether the element has the focus
        */
        var isFocused = false;

        /*
        * Listen for the dom events on the text area or the content editable element.
        */
        me.$el.on({
            keydown: function (e) {
                me.triggerEvent(eventNames.onkeydown, { originalEvent: e });
                e.stopPropagation();
            },
            keyup: function (e) {
                me.triggerEvent(eventNames.onkeyup, { originalEvent: e });
                e.stopPropagation();
            },
            keypress: function (e) {
                me.triggerEvent(eventNames.onkeypress, { originalEvent: e });
                e.stopPropagation();
            },
            focus: function (e) {
                if (!isFocused) {
                    isFocused = true;
                    me.triggerEvent(eventNames.onfocus, { originalEvent: e });
                }
                startPollingForValueChange();
                e.stopPropagation();
            },
            blur: function (e) {
                isFocused = false;
                me.triggerEvent(eventNames.onselectionchange);
                me.triggerEvent(eventNames.onblur, { originalEvent: e });

                // Clear the value changed poll timer
                if (pollTimer) {
                    clearInterval(pollTimer);
                    pollTimer = null;
                }
                e.stopPropagation();
            },
            mouseup: function (e) {
                me.triggerEvent(eventNames.onselectionchange);
                me.triggerEvent(eventNames.onmouseup, { originalEvent: e });
            },
            mousedown: function (e) {
                me.triggerEvent(eventNames.onmousedown, { originalEvent: e });
            },
            click: function (e) {
                me.triggerEvent(eventNames.onclick, { originalEvent: e });
            },
            paste: function (e) {
                setTimeout(function () {
                    me.triggerEvent(eventNames.onpaste, { originalEvent: e });
                }, 50);
            }
        });
        $.Arte.pluginManager.init(me);

        me.value(initialValues.value);
        me.currentValue = me.outerValue();

        me.$element.on(options.on);
        me.triggerEvent(eventNames.oncreate);
    };

    $.extend($.Arte.TextArea.prototype, {
        // Get innerHtml of the contentEditable element
        "value": function (value) {
            var constants = $.Arte.constants;
            var op = this.editorType === constants.editorTypes.richText ? "html" : "val";

            if (typeof (value) === "undefined") {
                if ($.Arte.configuration.handleUnsanctionedTagsOnGetValue) {
                    // Save current selection
                    var savedSelection = rangy.saveSelection();
                    $.Arte.dom.handleUnsanctionedElements(this.$el.contents());
                    rangy.restoreSelection(savedSelection);
                }
                this.currentValue = this.$el[op]();
                return this.currentValue;
            }

            if (this.currentValue === value) {
                return;
            }

            var oldValue = this.$el[op]();
            this.currentValue = value;
            // Set the inner text
            this.$el[op](value);
            this.triggerEvent(constants.eventNames.onvaluechange, { newValue: this.currentValue, oldValue: oldValue });
        },
        // Get outerHtml of the contentEditable
        "outerValue": function (value) {
            if (typeof (value) === "undefined") {
                var clone = this.$element.clone();
                clone.children().removeAttr("contenteditable");
                return clone.html();
            }
            var newElement = $(value);

            if ($.Arte.dom.isEqual(this.$el, newElement)) {
                return;
            }

            this.$el.removeAttr("style"); // Clear the styles
            this.$el.attr("style", newElement.attr("style"));

            this.$el.removeAttr("class");
            this.$el.attr("class", newElement.attr("class"));

            this.value(newElement.html());
        },
        "focus": function () {
            var me = this;
            var focusHandler = function () {
                me.$el.off("focus", focusHandler);
                $.Arte.util.moveCursorToEndOfElement(me.$el.get(0));
                me.triggerEvent($.Arte.constants.eventNames.onselectionchange);
            };

            me.$el.on("focus", focusHandler);
            me.$el.focus();
        },
        "triggerEvent": function (name, data) {
            this.$element.trigger(name, $.extend(data, { textArea: this }));
        },
        "destroy": function (options) {
            // Converts the rich text editor to non-editable state and remove rich text state information
            this.$element.removeData("Arte");
            this.$element.removeAttr($.Arte.configuration.textFieldIdentifier);
            this.$element.off();

            this.$el.off();
            this.$el.removeAttr("contentEditable");
            this.triggerEvent($.Arte.constants.eventNames.ondestroy);

            if (options && options.removeContent) {
                this.$element.empty();
            }
        },
        /**
        *  on/off methods to support attaching events handler using a rich text instance
        */
        on: function (type, handler) {
            this.$element.on(type, handler);
        },
        off: function (type, handler) {
            this.$element.off(type, handler);
        }
    });
})(jQuery);<|endoftext|>


<commit_before>angular.module('hotelApp')

.filter('itemsFilter', function(){

    return function(order, status){
        //order.status = stat
        //console.log(order);
        return order;
    }
})

.controller('OrderGridCtrl', ['$scope', '$location', '$timeout', 'OrderService', function($scope, $location, $timeout, OrderService) {

    var isFooterOpen = false;
    var statusClasses = [
        '',
        'pending',
        'process',
        'completed',
        'delivered',
        'cancelled'
    ];

    $scope.editOrder = function(order){
        OrderService.setOrderToEdit(order);
        $location.path('/edit-order/'+order._id)
    }

    $scope.showDate = function(dateStr){
        return new Date(dateStr).toTimeString().split(' ')[0];
    }

    $scope.payBal = false;

    $scope.makeBalPayment = function(e){

        $scope.payBal = true;
        var target = event.target;

        $timeout(function(){
            angular.element(target).closest('.sol-additional-opts').find('.paid-amt')[0].focus();
        },30)
    }

    $scope.makeBalPaymentCancel = function(){
        $scope.payBal = false;
    }

    $scope.makeBalPaymentPay = function(){
        //add service call here
        $scope.payBal = false;
    }

    $scope.payOrderChange = function(order){
        order.bal_amt = (order.paid_amt <= order.order_total_amt) ? order.order_total_amt - order.paid_amt : 0;
    }

    $scope.changeStatus = function(item){
        item.status++;
        item.status = item.status % 4;
        if(item.status==0){
            item.status++;
        }
    }

    $scope.getStatus = function (item) {
        var classes = '';
        if(!$scope.isShowingParcel){
            classes = 'hide-bg '
        }

        return classes + statusClasses[item.status]
    }

    $scope.getRowStyle = function(item) {
        var color = (item.isParcel) ? '#FF0' : '#F0F';

        return {
            'background-color': color
        }
    }
    $scope.isFooterExpanded = function(order){
        return (isFooterOpen) ? 'isOpen' : '';
    }

    $scope.isShowingParcel = false;

    $scope.showParcel = function() {
        $scope.isShowingParcel = !$scope.isShowingParcel;
    }

    $scope.orderStatus = '1';

    $scope.expandFooter = function (){
        isFooterOpen = !isFooterOpen;
    }

}])

.controller('OrderListCtrl', ['$scope', '$location', '$stateParams', '$timeout', 'OrderService', 'Product', function($scope, $location, $stateParams, $timeout, OrderService, Product) {

    $scope.orderlist = [];
    $scope.orderlistFiltered = [];
    $scope.includeCompletedOrders = false;
    $scope.displayItemGrouped = true;

    $scope.products = [];
    $scope.categories = [];

    $scope.isFilterSelected = false;
    $scope.selectedFilterCat = '';
    $scope.selectedFilterOpt = 'OR';
    $scope.selectedFilteritem = '';

    var timeSlotMins = 5;
    var getOrderWithStatus = '1';
    var socket = io();
    var wasFiltered = false;

    $scope.filterItems = function(item){
        return item
    }

    $scope.orderEdit = false;
    $scope.alert = null;

    var alerts = [
        { type: 'danger', msg: 'Oh snap! unable to update order, Please try again.' },
        { type: 'success', msg: 'Well done! You successfully updated the Order' }
    ];

    $scope.playOrderSound = function(type){
        var audio ;
        switch(type.toLowerCase())
        {
            case 'order_created':
                audio = document.getElementById("order_created_audio");
            break;
            case 'error':
                audio = document.getElementById("error_audio");
            break;
        }
        audio.play();
    }

    function init() {

        loadOrders();
        loadProducts();
        loadCategory();

        if($location.search().hasOwnProperty('orderEditSuccess')) {
            console.log($location.search().orderEditSuccess)
            $scope.orderEdit = true;
            debugger;
            if($location.search().orderEditSuccess == true){
                $scope.alert = alerts[1]
            }else{
                $scope.alert = alerts[0]
            }
            initiateAutoHide()
        }else{
            $scope.orderEdit = false;
        }

        socket.on('order-created', function(){
            $scope.playOrderSound('order_created');
            loadOrders();
        });
    }

    var intervalID;

    function initiateAutoHide(){
        $timeout(function(){
            $scope.orderEdit = false;
            $scope.alert = null
        }, 5000)
    }

    function loadCategory(){
        Product.getCategories().then(function(data){
            $timeout(function(){
                $scope.categories = data;
            },10)

        })
    }

    $scope.getFilterOptionsArray = function(){
        return ($scope.selectedFilterCat == '') ? ['OR'] : ['OR','AND'];

    }

    function loadProducts(){
        Product.getProducts().then(function(data){
            $scope.products = data;
        })
    }

    $scope.filterSelectionChange = function(){
        if($scope.isFilterSelected){
            localFilter()
        }else{
            if(wasFiltered){

                loadOrders();
            }

        }
    }

    $scope.filterCatChanged = function(){
        $scope.isFilterSelected = true;
        localFilter()
    }

    $scope.filterOptionChanged = function(){
        $scope.isFilterSelected = true;

        for (var i = 0; i < Things.length; i++) {
            Things[i]
        }
        localFilter()

    }

    $scope.filterItemChanged = function(){
        $scope.isFilterSelected = true;
        localFilter()
    }

    function localFilter(){

        if ($scope.selectedFilterCat == '') return;

        $scope.orderlist = []
        for(var i=0;i<orderBackBuffer.length;i++)
        {
            var order = orderBackBuffer[i];
            var exists = false;
            for (var j = 0; j < order.itemsInOrder.length; j++) {
                if($scope.selectedFilterOpt.trim() == 'OR') {
                    if(order.itemsInOrder[j].prod_category_id == $scope.selectedFilterCat.category_id ||
                    order.itemsInOrder[j].prod_id == $scope.selectedFilteritem.prod_id){
                        exists = true;
                    }
                }else if($scope.selectedFilterOpt.trim() == 'AND') {
                    if(order.itemsInOrder[j].prod_category_id == $scope.selectedFilterCat.category_id &&
                    order.itemsInOrder[j].prod_id == $scope.selectedFilteritem.prod_id){
                        exists = true;
                    }
                }
            }
            if(exists){
                wasFiltered = true;
                $scope.orderlist.push(angular.copy(order))
            }
        }
    }

    function createTimer() {

        var now = new Date();
        var newTime = new Date();
        var ahead = (Math.ceil(now.getMinutes()/timeSlotMins) * timeSlotMins) % 60;

        if(ahead == 0){
            newTime.setHours(newTime.getHours()+1)
            newTime.setMinutes(0);
        }else{
            newTime.setMinutes(ahead);
        }

        var diff = newTime - now;

        setTimeout(function(){
            if(intervalID){
                clearInterval(intervalID)
            }

            intervalID = setInterval(scheduleReloader, timeSlotMins*60*1000)
            scheduleReloader();
        }, diff)
    }

    function scheduleReloader(){
        $scope.$apply(function(){
            $scope.orderlist = filterScheduledOrder(orderBackBuffer);
        })

    }

    $scope.showCompletedChange = function(){

        getOrderWithStatus = ($scope.includeCompletedOrders) ? '1' : '1';
        loadOrders();
    }

    var orderBackBuffer = [];

    function loadOrders(){
        OrderService.getLatestOrder(getOrderWithStatus).then(function(response) {

            if($scope.displayItemGrouped)
            {
                orderBackBuffer = response;
                for(var i=0;i<orderBackBuffer.length;i++)
                {
                    var order = orderBackBuffer[i];
                    order.itemsInOrderMap = groupByItemID_Abbr(order.itemsInOrder);
                }
                $scope.orderlist = filterScheduledOrder(orderBackBuffer);
            }else
            {
                $scope.orderlist = response;
            }
            createTimer()
        })
    }

    function filterScheduledOrder(orders){
        return orders.filter(function(order){

            if(order.is_scheduled==1){
                var now = new Date()
                now.setSeconds(59);
                var sdt = new Date(order.scheduled_date_time)
                return ((sdt - now) <= (timeSlotMins*60*1000))
            }

            return true;
        })
    }

    function groupByItemID_Abbr(itemsInOrder){
        var map={};
        for(var i=0;i<itemsInOrder.length;i++)
        {
            var product = itemsInOrder[i];
            var key = product.prod_id + JSON.stringify(product.ingredients).replace(/[{}"]/g, '');

            if(map.hasOwnProperty(key)){
                product.prod_qty++;
            }

            map[key] = product;
        }
        return map;
    }

    init()

}])
<commit_msg>newly added order at last position in order list #40<commit_after>angular.module('hotelApp')

.filter('itemsFilter', function(){

    return function(order, status){
        //order.status = stat
        //console.log(order);
        return order;
    }
})

.controller('OrderGridCtrl', ['$scope', '$location', '$timeout', 'OrderService', function($scope, $location, $timeout, OrderService) {

    var isFooterOpen = false;
    var statusClasses = [
        '',
        'pending',
        'process',
        'completed',
        'delivered',
        'cancelled'
    ];

    $scope.editOrder = function(order){
        OrderService.setOrderToEdit(order);
        $location.path('/edit-order/'+order._id)
    }

    $scope.showDate = function(dateStr){
        return new Date(dateStr).toTimeString().split(' ')[0];
    }

    $scope.payBal = false;

    $scope.makeBalPayment = function(e){

        $scope.payBal = true;
        var target = event.target;

        $timeout(function(){
            angular.element(target).closest('.sol-additional-opts').find('.paid-amt')[0].focus();
        },30)
    }

    $scope.makeBalPaymentCancel = function(){
        $scope.payBal = false;
    }

    $scope.makeBalPaymentPay = function(){
        //add service call here
        $scope.payBal = false;
    }

    $scope.payOrderChange = function(order){
        order.bal_amt = (order.paid_amt <= order.order_total_amt) ? order.order_total_amt - order.paid_amt : 0;
    }

    $scope.changeStatus = function(item){
        item.status++;
        item.status = item.status % 4;
        if(item.status==0){
            item.status++;
        }
    }

    $scope.getStatus = function (item) {
        var classes = '';
        if(!$scope.isShowingParcel){
            classes = 'hide-bg '
        }

        return classes + statusClasses[item.status]
    }

    $scope.getRowStyle = function(item) {
        var color = (item.isParcel) ? '#FF0' : '#F0F';

        return {
            'background-color': color
        }
    }
    $scope.isFooterExpanded = function(order){
        return (isFooterOpen) ? 'isOpen' : '';
    }

    $scope.isShowingParcel = false;

    $scope.showParcel = function() {
        $scope.isShowingParcel = !$scope.isShowingParcel;
    }

    $scope.orderStatus = '1';

    $scope.expandFooter = function (){
        isFooterOpen = !isFooterOpen;
    }

}])

.controller('OrderListCtrl', ['$scope', '$location', '$stateParams', '$timeout', 'OrderService', 'Product', function($scope, $location, $stateParams, $timeout, OrderService, Product) {

    $scope.orderlist = [];
    $scope.orderlistFiltered = [];
    $scope.includeCompletedOrders = false;
    $scope.displayItemGrouped = true;

    $scope.products = [];
    $scope.categories = [];

    $scope.isFilterSelected = false;
    $scope.selectedFilterCat = '';
    $scope.selectedFilterOpt = 'OR';
    $scope.selectedFilteritem = '';

    var timeSlotMins = 5;
    var getOrderWithStatus = '1';
    var socket = io();
    var wasFiltered = false;

    $scope.filterItems = function(item){
        return item
    }

    $scope.orderEdit = false;
    $scope.alert = null;

    var alerts = [
        { type: 'danger', msg: 'Oh snap! unable to update order, Please try again.' },
        { type: 'success', msg: 'Well done! You successfully updated the Order' }
    ];

    $scope.playOrderSound = function(type){
        var audio ;
        switch(type.toLowerCase())
        {
            case 'order_created':
                audio = document.getElementById("order_created_audio");
            break;
            case 'error':
                audio = document.getElementById("error_audio");
            break;
        }
        audio.play();
    }

    function init() {

        loadOrders();
        loadProducts();
        loadCategory();

        if($location.search().hasOwnProperty('orderEditSuccess')) {
            $scope.orderEdit = true;
            if($location.search().orderEditSuccess == true){
                $scope.alert = alerts[1]
            }else{
                $scope.alert = alerts[0]
            }
            initiateAutoHide()
        }else{
            $scope.orderEdit = false;
        }

        socket.on('order-created', function(){
            $scope.playOrderSound('order_created');
            loadOrders();
        });
    }

    var intervalID;

    function initiateAutoHide(){
        $timeout(function(){
            $scope.orderEdit = false;
            $scope.alert = null
        }, 5000)
    }

    function loadCategory(){
        Product.getCategories().then(function(data){
            $timeout(function(){
                $scope.categories = data;
            },10)

        })
    }

    $scope.getFilterOptionsArray = function(){
        return ($scope.selectedFilterCat == '') ? ['OR'] : ['OR','AND'];

    }

    function loadProducts(){
        Product.getProducts().then(function(data){
            $scope.products = data;
        })
    }

    $scope.filterSelectionChange = function(){
        if($scope.isFilterSelected){
            localFilter()
        }else{
            if(wasFiltered){

                loadOrders();
            }

        }
    }

    $scope.filterCatChanged = function(){
        $scope.isFilterSelected = true;
        localFilter()
    }

    $scope.filterOptionChanged = function(){
        $scope.isFilterSelected = true;

        for (var i = 0; i < Things.length; i++) {
            Things[i]
        }
        localFilter()

    }

    $scope.filterItemChanged = function(){
        $scope.isFilterSelected = true;
        localFilter()
    }

    function localFilter(){

        if ($scope.selectedFilterCat == '') return;

        $scope.orderlist = []
        for(var i=0;i<orderBackBuffer.length;i++)
        {
            var order = orderBackBuffer[i];
            var exists = false;
            for (var j = 0; j < order.itemsInOrder.length; j++) {
                if($scope.selectedFilterOpt.trim() == 'OR') {
                    if(order.itemsInOrder[j].prod_category_id == $scope.selectedFilterCat.category_id ||
                    order.itemsInOrder[j].prod_id == $scope.selectedFilteritem.prod_id){
                        exists = true;
                    }
                }else if($scope.selectedFilterOpt.trim() == 'AND') {
                    if(order.itemsInOrder[j].prod_category_id == $scope.selectedFilterCat.category_id &&
                    order.itemsInOrder[j].prod_id == $scope.selectedFilteritem.prod_id){
                        exists = true;
                    }
                }
            }
            if(exists){
                wasFiltered = true;
                $scope.orderlist.push(angular.copy(order))
            }
        }
    }

    function createTimer() {

        var now = new Date();
        var newTime = new Date();
        var ahead = (Math.ceil(now.getMinutes()/timeSlotMins) * timeSlotMins) % 60;

        if(ahead == 0){
            newTime.setHours(newTime.getHours()+1)
            newTime.setMinutes(0);
        }else{
            newTime.setMinutes(ahead);
        }

        var diff = newTime - now;

        setTimeout(function(){
            if(intervalID){
                clearInterval(intervalID)
            }

            intervalID = setInterval(scheduleReloader, timeSlotMins*60*1000)
            scheduleReloader();
        }, diff)
    }

    function scheduleReloader(){
        $scope.$apply(function(){
            $scope.orderlist = filterScheduledOrder(orderBackBuffer);
        })

    }

    $scope.showCompletedChange = function(){

        getOrderWithStatus = ($scope.includeCompletedOrders) ? '1' : '1';
        loadOrders();
    }

    var orderBackBuffer = [];

    function loadOrders(){
        OrderService.getLatestOrder(getOrderWithStatus).then(function(response) {

            if($scope.displayItemGrouped)
            {
                orderBackBuffer = response;
                for(var i=0;i<orderBackBuffer.length;i++)
                {
                    var order = orderBackBuffer[i];
                    order.itemsInOrderMap = groupByItemID_Abbr(order.itemsInOrder);
                }
                $scope.orderlist = filterScheduledOrder(orderBackBuffer);
            }else
            {
                $scope.orderlist = response;
            }
            createTimer()
        })
    }

    function filterScheduledOrder(orders){
        return orders.filter(function(order){

            if(order.is_scheduled==1){
                var now = new Date()
                now.setSeconds(59);
                var sdt = new Date(order.scheduled_date_time)
                return ((sdt - now) <= (timeSlotMins*60*1000))
            }

            return true;
        })
    }

    function groupByItemID_Abbr(itemsInOrder){
        var map={};
        for(var i=0;i<itemsInOrder.length;i++)
        {
            var product = itemsInOrder[i];
            var key = product.prod_id + JSON.stringify(product.ingredients).replace(/[{}"]/g, '');

            if(map.hasOwnProperty(key)){
                product.prod_qty++;
            }

            map[key] = product;
        }
        return map;
    }

    init()

}])
<|endoftext|>


<commit_before>(function() {
    'use strict';
    LiveblogSettingsController.$inject = ['$scope', 'api', '$location', 'notify', 'gettext', '$q'];
    function LiveblogSettingsController($scope, api, $location, notify, gettext, $q) {
        //prep the settings
        $scope.liveblogSettings = {'language': {}, 'theme': {}};
        $scope.settingsLoading = true;
        api.languages.query().then(function(data) {
            $scope.languages = [];
            _.map(data._items, function(language) {
                $scope.languages.push(language);
            });
        });
        api.themes.query().then(function(data) {
            $scope.themes = [];
            _.map(data._items, function(theme) {
                $scope.themes.push(theme);
            });
        });
        $scope.settingsLoading = true;
        api.global_preferences.query().then(function(data) {
            _.map(data._items, function(setting) {
                $scope.liveblogSettings[setting.key] = setting;
            });
            $scope.settingsLoading = false;
        });

        $scope.saveSettings = function() {
            notify.pop();
            notify.info(gettext('Saving settings'));
            var patch = {}, reqArr = [];
            _.map($scope.liveblogSettings, function(item, key) {
                patch = {
                    key: key,
                    value: item.value
                };
                reqArr.push(api('global_preferences').save(item, patch));
            });
            $q.all(reqArr).then(function() {
                notify.pop();
                notify.info(gettext('Settings saved'));
            }, function() {
                notify.pop();
                notify.error(gettext('Saving settings failed. Please try again later'));
            });
        };
        $scope.close = function() {
            //return to blog list page
            $location.path('/liveblog/');
        };
    }
    var liveblogSettingsModule = angular.module('liveblog.settings', [])
    .config(['superdeskProvider', function(superdesk) {
        superdesk
            .activity('/settings/liveblog', {
                label: gettext('Liveblog'),
                controller: LiveblogSettingsController,
                templateUrl: 'scripts/liveblog-settings/views/general.html',
                category: superdesk.MENU_SETTINGS
            });
    }])
    .config(['apiProvider', function(apiProvider) {
        apiProvider.api('themes', {
            type: 'http',
            backend: {rel: 'themes'}
        });
        apiProvider.api('languages', {
            type: 'http',
            backend: {rel: 'languages'}
        });
        apiProvider.api('global_preferences', {
            type: 'http',
            backend: {rel: 'global_preferences'}
        });
    }]);
    return liveblogSettingsModule;
})();
<commit_msg>LBSD-384 fixes after review<commit_after>(function() {
    'use strict';
    LiveblogSettingsController.$inject = ['$scope', 'api', '$location', 'notify', 'gettext', '$q'];
    function LiveblogSettingsController($scope, api, $location, notify, gettext, $q) {
        //prep the settings
        $scope.liveblogSettings = {'language': {}, 'theme': {}};
        api.languages.query().then(function(data) {
            $scope.languages = data._items;
        });
        api.themes.query().then(function(data) {
            $scope.themes = data._items;
        });
        $scope.settingsLoading = true;
        api.global_preferences.query().then(function(data) {
            _.map(data._items, function(setting) {
                $scope.liveblogSettings[setting.key] = setting;
            });
            $scope.settingsLoading = false;
        });

        $scope.saveSettings = function() {
            notify.pop();
            notify.info(gettext('Saving settings'));
            var patch = {}, reqArr = [];
            _.map($scope.liveblogSettings, function(item, key) {
                patch = {
                    key: key,
                    value: item.value
                };
                reqArr.push(api('global_preferences').save(item, patch));
            });
            $q.all(reqArr).then(function() {
                notify.pop();
                notify.info(gettext('Settings saved'));
            }, function() {
                notify.pop();
                notify.error(gettext('Saving settings failed. Please try again later'));
            });
        };
        $scope.close = function() {
            //return to blog list page
            $location.path('/liveblog/');
        };
    }
    var liveblogSettingsModule = angular.module('liveblog.settings', [])
    .config(['superdeskProvider', function(superdesk) {
        superdesk
            .activity('/settings/liveblog', {
                label: gettext('Liveblog'),
                controller: LiveblogSettingsController,
                templateUrl: 'scripts/liveblog-settings/views/general.html',
                category: superdesk.MENU_SETTINGS
            });
    }])
    .config(['apiProvider', function(apiProvider) {
        apiProvider.api('themes', {
            type: 'http',
            backend: {rel: 'themes'}
        });
        apiProvider.api('languages', {
            type: 'http',
            backend: {rel: 'languages'}
        });
        apiProvider.api('global_preferences', {
            type: 'http',
            backend: {rel: 'global_preferences'}
        });
    }]);
    return liveblogSettingsModule;
})();
<|endoftext|>


<commit_before>/*globals define, _*/

/**
 * @author rkereskenyi / https://github.com/rkereskenyi
 */

define(['js/Decorators/DecoratorBase',
    './SnapEditor/SVGDecorator.SnapEditorWidget',
    './DiagramDesigner/SVGDecorator.DiagramDesignerWidget',
    './PartBrowser/SVGDecorator.PartBrowserWidget'], function (
                                                           DecoratorBase,
                                                           SVGDecoratorSnapEditorWidget,
                                                           SVGDecoratorDiagramDesignerWidget,
                                                           SVGDecoratorPartBrowserWidget) {

    "use strict";

    var SVGDecorator,
        DECORATOR_ID = "SVGDecorator";

    SVGDecorator = function (params) {
        var opts = _.extend( {"loggerName": this.DECORATORID}, params);

        DecoratorBase.apply(this, [opts]);

        this.logger.debug("SVGDecorator ctor");
    };

    _.extend(SVGDecorator.prototype, DecoratorBase.prototype);
    SVGDecorator.prototype.DECORATORID = DECORATOR_ID;

    /*********************** OVERRIDE DecoratorBase MEMBERS **************************/

    SVGDecorator.prototype.initializeSupportedWidgetMap = function () {

        this.supportedWidgetMap = {
            'DiagramDesigner': SVGDecoratorDiagramDesignerWidget,
            'PartBrowser': SVGDecoratorPartBrowserWidget};

    };

    return SVGDecorator;
});
<commit_msg>Re-added the SnapEditor<commit_after>/*globals define, _*/

/**
 * @author rkereskenyi / https://github.com/rkereskenyi
 */

define(['js/Decorators/DecoratorBase',
    './SnapEditor/SVGDecorator.SnapEditorWidget',
    './DiagramDesigner/SVGDecorator.DiagramDesignerWidget',
    './PartBrowser/SVGDecorator.PartBrowserWidget'], function (
                                                           DecoratorBase,
                                                           SVGDecoratorSnapEditorWidget,
                                                           SVGDecoratorDiagramDesignerWidget,
                                                           SVGDecoratorPartBrowserWidget) {

    "use strict";

    var SVGDecorator,
        DECORATOR_ID = "SVGDecorator";

    SVGDecorator = function (params) {
        var opts = _.extend( {"loggerName": this.DECORATORID}, params);

        DecoratorBase.apply(this, [opts]);

        this.logger.debug("SVGDecorator ctor");
    };

    _.extend(SVGDecorator.prototype, DecoratorBase.prototype);
    SVGDecorator.prototype.DECORATORID = DECORATOR_ID;

    /*********************** OVERRIDE DecoratorBase MEMBERS **************************/

    SVGDecorator.prototype.initializeSupportedWidgetMap = function () {

        this.supportedWidgetMap = {
            'DiagramDesigner': SVGDecoratorDiagramDesignerWidget,
            'PartBrowser': SVGDecoratorPartBrowserWidget,
            'SnapEditor': SVGDecoratorSnapEditorWidget};

    };

    return SVGDecorator;
});
<|endoftext|>


<commit_before><commit_msg>Update Benchmark.js submodule.<commit_after><|endoftext|>


<commit_before>var app = angular.module('app');

app.controller('LoadModalController',
  function($scope, $uibModalInstance, localStorageService)
  {

    $scope.cancel = function() {
      $uibModalInstance.dismiss('cancel');
    };

    $scope.btnLoadClicked = function() {
      $uibModalInstance.close($scope.projects[$scope.input.name]);
    };

    (function constructor() {
      $scope.projects = localStorageService.get('names') || {};
      $scope.names = _.chain($scope.projects).keys().sortBy().value();
      $scope.input = {};
      $scope.input.name = $scope.names[0];
    })();

  }
);
<commit_msg>load modal return name & data<commit_after>var app = angular.module('app');

app.controller('LoadModalController',
  function($scope, $uibModalInstance, localStorageService)
  {

    $scope.cancel = function() {
      $uibModalInstance.dismiss('cancel');
    };

    $scope.btnLoadClicked = function() {
      var result = {};
      result.projectName = $scope.input.name;
      result.data = $scope.projects[$scope.input.name];
      $uibModalInstance.close(result);
    };

    (function constructor() {
      $scope.projects = localStorageService.get('names') || {};
      $scope.names = _.chain($scope.projects).keys().sortBy().value();
      $scope.input = {};
      $scope.input.name = $scope.names[0];
    })();

  }
);
<|endoftext|>


<commit_before>'use strict';

/**
 * Class counting occurrences of values.
 */
class Histogram {
	constructor() {
		this.frequencies = {};
	}

	clear() {
		for (let i in this.frequencies) {
			this.frequencies[i] = 0;
		}
	}

	/**
	 * Increase frequency of val in the histogram.
	 *
	 * @param {any} val – Value to be added to a histogram.
	 */
	addValue(val) {
		if (this.frequencies.hasOwnProperty(val)) {
			this.frequencies[val]++;
		} else {
			this.frequencies[val] = 1;
		}
	}

	/**
	 * Produce the list of categories counted by the histogram.
	 *
	 * @return {Array[any]} List of categories.
	 */
	get labels() {
		let labels = [];
		for (let i in this.frequencies) {
			labels.push(i);
		}
		return labels;
	}

	/**
	 * Produce the list of frequencies of the categories as counted by the histogram.
	 *
	 * @return {Array[Int]} List of frequencies.
	 */
	get occurrences() {
		let occurrences = [];
		for (let i in this.frequencies) {
			occurrences.push(this.frequencies[i]);
		}
		return occurrences;
	}
}

/**
 * Class counting occurrences of numerical values in specified intervals.
 */
class PartitionedHistogram extends Histogram {
	/**
	 * Create a histogram of number intervals.
	 *
	 * @param {array[number]} partitions – List of numbers that will partition the numbers.
	 */
	constructor(partitions) {
		super();
		this.partitions = partitions.slice().sort();
	}

	/**
	 * Find an interval to add a value to and increase its frequency.
	 *
	 * @param {number} val – Value to be added to a histogram.
	 */
	addValue(val) {
		let partition = this.partitions.length;
		for (let i = 0; i < this.partitions.length; i++) {
			if (val <= this.partitions[i]) {
				partition = i;
				break;
			}
		}
		super.addValue(partition);
	}

	/**
	 * Produce the list of intervals counted by the histogram.
	 *
	 * @return {Array[any]} List of intervals.
	 */
	get labels() {
		if (this.partitions.length == 0) {
			return ['*'];
		}

		let labels = [];
		for (var i = 0; i < this.partitions.length; i++) {
			if (i == 0) {
				labels.push('≤' + this.partitions[i]);
			} else {
				labels.push((this.partitions[i - 1] + 1).toString() + '–' + this.partitions[i]);
			}
		}
		labels.push('>' + this.partitions[i - 1]);

		return labels;
	}

	/**
	 * Produce the list of frequencies of the intervals as counted by the histogram.
	 *
	 * @return {Array[Int]} List of frequencies.
	 */
	get occurrences() {
		let occurrences = [];
		for (var i = 0; i <= this.partitions.length; i++) {
			occurrences.push(this.frequencies[i] || 0);
		}
		return occurrences;
	}
}

/**
 * Class representing a menu with a list of available visualisations.
 */
class SideBar {
	/**
	 * Create a sidebar
	 *
	 * @param {Element} sideBarElement – An element to which the menu should be placed.
	 */
	constructor(sideBarElement) {
		this.sideBarElement = sideBarElement;
		const header = document.createElement('h1');
		header.textContent = 'Available visualisations';
		sideBarElement.appendChild(header);

		const visList = document.createElement('ul');
		sideBarElement.appendChild(visList);

		visualisations.forEach((item, key) => {
			const li = document.createElement('li');

			const a = document.createElement('a');
			a.href = '?' + key;
			a.textContent = item.name;
			li.appendChild(a);

			visList.appendChild(li);
		});

		const sideBarToggle = document.createElement('span');
		sideBarToggle.setAttribute('role', 'button');
		sideBarToggle.setAttribute('tabindex', '0');
		sideBarToggle.classList.add('sidebar-toggle')
		sideBarToggle.addEventListener('click', () => sideBarElement.classList.toggle('collapsed'));
		sideBarToggle.textContent = 'Menu';
		sideBarElement.appendChild(sideBarToggle);
	}

	/**
	 * Collapse the sidebar to save space on the screen.
	 */
	collapse() {
		this.sideBarElement.classList.add('collapsed');
	}

	/**
	 * Expand the sidebar so the user could change the visualisation.
	 */
	expand() {
		this.sideBarElement.classList.remove('collapsed');
	}
}

/**
 * Class representing a sidebar where a list of currently visible features,
 * as well as informations about the selected feature are displayed.
 */
class InfoBar {
	/**
	 * Create an info sidebar.
	 *
	 * @param {Element} infoBar – An element to which the sidebar should be placed.
	 * @param {Object} visualisation – Currently active visualisation.
	 */
	constructor(infoBar, visualisation) {
		this.visualisation = visualisation;

		this.infoHolder = document.createElement('div');
		this.infoHolder.classList.add('info');
		infoBar.appendChild(this.infoHolder);

		const featureHolder = document.createElement('div');
		infoBar.appendChild(featureHolder);

		this.header = document.createElement('h1');
		this.header.textContent = 'Visible features';
		featureHolder.classList.add('features');
		featureHolder.appendChild(this.header);

		this.emptyMessage = document.createElement('p');
		this.emptyMessage.textContent = 'There are no features in the visible area. Try zooming out or moving the map view.';
		this.emptyMessage.classList.add('hidden');
		featureHolder.appendChild(this.emptyMessage);

		this.featList = document.createElement('ul');
		featureHolder.appendChild(this.featList);

		this.chartEnabled = visualisation.histogram;
		this.histogram = null;
		this.plottedCategory = null;
		this.chart = null;
		this.chartHolder = null;

		this.chartStyle = {
			backgroundColor: 'rgba(255,99,132,0.2)',
			borderColor: 'rgba(255,99,132,1)',
			borderWidth: 1,
			hoverBackgroundColor: 'rgba(255,99,132,0.4)',
			hoverBorderColor: 'rgba(255,99,132,1)'
		};

		if (this.chartEnabled) {
			if (visualisation.histogram.partitions) {
				this.histogram = new PartitionedHistogram(visualisation.histogram.partitions);
			} else {
				this.histogram = new Histogram;
			}

			let styleProperties = visualisation.histogram.style;
			if (styleProperties) {
				for (let prop in this.chartStyle) {
					if (styleProperties.hasOwnProperty(prop)) {
						this.chartStyle[prop] = styleProperties[prop];
					}
				}
			}

			this.plottedCategory = visualisation.histogram.plottedCategory || (feature => feature.get('measured'));

			this.chartHolder = document.createElement('canvas');
			this.chartHolder.classList.add('chart');
			this.chartHolder.width = infoBar.offsetWidth;
			this.chartHolder.height = Math.floor(infoBar.offsetWidth / 16 * 9);

			infoBar.appendChild(this.chartHolder);
		}
	}

	/**
	 * Set the information about the selected feature.
	 *
	 * @param {String} info – Feature information.
	 */
	setInfo(info) {
		this.infoHolder.innerHTML = info;
	}

	/**
	 * Clear the information after the feature was deselected.
	 */
	clearInfo() {
		this.infoHolder.innerHTML = 'Hover a feature on the map or click on one in the list below to show information about the feature.';
	}

	/**
	 * Update the list of features.
	 *
	 * @param {Array[ol.Feature]} features – List of features to be listed.
	 * @param {Function} highlightFeature – A function that will highlight the feature selected in the list.
	 */
	setFeatures(features, highlightFeature) {
		this.featList.textContent = '';

		if (this.chartEnabled) {
			this.histogram.clear();
		}

		if (features.length == 0) {
			this.header.classList.add('hidden');
			this.emptyMessage.classList.remove('hidden');
		} else {
			this.header.classList.remove('hidden');
			this.emptyMessage.classList.add('hidden');
		}

		features.forEach((feature) => {
			const li = document.createElement('li');
			const highlight = () => {
				highlightFeature(feature);
			};

			li.setAttribute('role', 'button');
			li.setAttribute('tabindex', '0');
			li.feature = feature;
			li.addEventListener('click', highlight);
			li.addEventListener('keypress', (e) => {
				if (e.key == 'Spacebar' || e.key == ' ' || e.key == 'Enter') {
					e.preventDefault();
					highlight();
				}
			});

			if (this.chartEnabled) {
				this.histogram.addValue(this.plottedCategory(feature));
			}

			li.textContent = this.visualisation.listInfo(feature);
			this.featList.appendChild(li);
		});

		if (this.chartEnabled) {
			if (!this.chart) {
				this.chart = new Chart(this.chartHolder, {
					type: 'bar',
					data: {
						labels: this.histogram.labels,
						datasets: [
							{
								backgroundColor: this.chartStyle.backgroundColor,
								borderColor: this.chartStyle.borderColor,
								borderWidth: this.chartStyle.borderWidth,
								hoverBackgroundColor: this.chartStyle.hoverBackgroundColor,
								hoverBorderColor: this.chartStyle.hoverBorderColor,
								data: this.histogram.occurrences,
							}
						]
					},
					options: {
						legend: {
							display: false
						},
						scales: {
							yAxes: [{
								ticks: {
									beginAtZero: true
								}
							}]
						}
					}
				});
			} else {
				for (let i in this.chart.data.datasets[0].data) {
					this.chart.data.datasets[0].data[i] = this.histogram.occurrences[i];
				}
				this.chart.update();
			}
		}
	}
}

window.onload = function() {
	const fileName = window.location.search.toString().replace(/^\?/, '');
	const sideBar = new SideBar(document.getElementById('sidebar'));

	if (!fileName) {
		sideBar.expand();
	} else if (!visualisations.has(fileName)) {
		alert('Unknown visualisation.');
		window.location.search = '';
	} else {
		const currentVisualiser = visualisations.get(fileName);
		const infoBar = new InfoBar(document.getElementById('infobar'), currentVisualiser);

		document.title = 'Address visualisation – ' + currentVisualiser.name;
		sideBar.collapse();
		setTimeout(() => document.body.classList.remove('preload'), 250);

		proj4.defs('urn:ogc:def:crs:EPSG::5514','+proj=krovak +lat_0=49.5 +lon_0=24.83333333333333 +alpha=30.28813972222222 +k=0.9999 +x_0=0 +y_0=0 +ellps=bessel +towgs84=589,76,480,0,0,0,0 +units=m +no_defs');

		function styleFunction(feature) {
			let style;
			let fillColor = 'rgba(240, 0, 0, 0.3)';
			let strokeColor = 'rgba(240, 0, 0, 1)';

			let styleProperty = feature.get('style');
			if (styleProperty) {
				if (styleProperty.fill) {
					fillColor = styleProperty.fill;
				}
				if (styleProperty.stroke) {
					strokeColor = styleProperty.stroke;
				}
			}

			if (feature.getGeometry().getType() === 'Point') {
				style = new ol.style.Style({
					image: new ol.style.Circle({
						radius: 5,
						fill: new ol.style.Fill({
							color: fillColor,
						}),
						stroke: new ol.style.Stroke({
							color: strokeColor,
							width: 10
						})
					})
				});
			} else {
				style = new ol.style.Style({
					fill: new ol.style.Fill({
						color: fillColor,
					}),
					stroke: new ol.style.Stroke({
						color: strokeColor,
						width: 10
					})
				});
			}

			return style;
		}

		const geoJsonSource = new ol.source.Vector({
			url: '../vf_resources/visualisations/' + fileName + '.json',
			format: new ol.format.GeoJSON()
		});

		const map = new ol.Map({
			layers: [
				new ol.layer.Tile({
					source: new ol.source.OSM()
				}),
				new ol.layer.Image({
					source: new ol.source.ImageVector({
						source: geoJsonSource,
						style: styleFunction
					})
				})
			],
			target: 'view',
			view: new ol.View({
				center: ol.proj.fromLonLat([15.5, 49.75]),
				zoom: 8,
				minZoom: 8
			})
		});

		const featureOverlay = new ol.layer.Vector({
			source: new ol.source.Vector(),
			map: map,
			style: new ol.style.Style({
				stroke: new ol.style.Stroke({
					color: '#f0f000',
					width: 10
				}),
				fill: new ol.style.Fill({
					color: 'rgba(240,240,0,0.3)'
				})
			})
		});

		function getFeatureUnderCursor(pixel) {
			return map.forEachFeatureAtPixel(pixel, function(feature) {
				return feature;
			});
		}

		let highlight;
		function highlightFeature(feature) {
			if (feature) {
				infoBar.setInfo(currentVisualiser.info(feature))
			} else {
				infoBar.clearInfo();
			}

			if (feature !== highlight) {
				if (highlight) {
					featureOverlay.getSource().removeFeature(highlight);
				}
				if (feature) {
					featureOverlay.getSource().addFeature(feature);
				}
				highlight = feature;
			}

		};

		map.on('pointermove', function(evt) {
			if (evt.dragging) {
				return;
			}
			const pixel = map.getEventPixel(evt.originalEvent);
			highlightFeature(getFeatureUnderCursor(pixel));
		});

		map.on('click', function(evt) {
			highlightFeature(getFeatureUnderCursor(evt.pixel));
		});

		function updateFeatureList() {
			const extent = map.getView().calculateExtent(map.getSize());
			const features = geoJsonSource.getFeaturesInExtent(extent);
			infoBar.setFeatures(features, highlightFeature);
		}

		geoJsonSource.once('change', () => map.on('postrender', updateFeatureList));
	}
};
<commit_msg>factor out the chart handling<commit_after>'use strict';

/**
 * Class counting occurrences of values.
 */
class Histogram {
	constructor() {
		this.frequencies = {};
	}

	clear() {
		for (let i in this.frequencies) {
			this.frequencies[i] = 0;
		}
	}

	/**
	 * Increase frequency of val in the histogram.
	 *
	 * @param {any} val – Value to be added to a histogram.
	 */
	addValue(val) {
		if (this.frequencies.hasOwnProperty(val)) {
			this.frequencies[val]++;
		} else {
			this.frequencies[val] = 1;
		}
	}

	/**
	 * Produce the list of categories counted by the histogram.
	 *
	 * @return {Array[any]} List of categories.
	 */
	get labels() {
		let labels = [];
		for (let i in this.frequencies) {
			labels.push(i);
		}
		return labels;
	}

	/**
	 * Produce the list of frequencies of the categories as counted by the histogram.
	 *
	 * @return {Array[Int]} List of frequencies.
	 */
	get occurrences() {
		let occurrences = [];
		for (let i in this.frequencies) {
			occurrences.push(this.frequencies[i]);
		}
		return occurrences;
	}
}

/**
 * Class counting occurrences of numerical values in specified intervals.
 */
class PartitionedHistogram extends Histogram {
	/**
	 * Create a histogram of number intervals.
	 *
	 * @param {array[number]} partitions – List of numbers that will partition the numbers.
	 */
	constructor(partitions) {
		super();
		this.partitions = partitions.slice().sort();
	}

	/**
	 * Find an interval to add a value to and increase its frequency.
	 *
	 * @param {number} val – Value to be added to a histogram.
	 */
	addValue(val) {
		let partition = this.partitions.length;
		for (let i = 0; i < this.partitions.length; i++) {
			if (val <= this.partitions[i]) {
				partition = i;
				break;
			}
		}
		super.addValue(partition);
	}

	/**
	 * Produce the list of intervals counted by the histogram.
	 *
	 * @return {Array[any]} List of intervals.
	 */
	get labels() {
		if (this.partitions.length == 0) {
			return ['*'];
		}

		let labels = [];
		for (var i = 0; i < this.partitions.length; i++) {
			if (i == 0) {
				labels.push('≤' + this.partitions[i]);
			} else {
				labels.push((this.partitions[i - 1] + 1).toString() + '–' + this.partitions[i]);
			}
		}
		labels.push('>' + this.partitions[i - 1]);

		return labels;
	}

	/**
	 * Produce the list of frequencies of the intervals as counted by the histogram.
	 *
	 * @return {Array[Int]} List of frequencies.
	 */
	get occurrences() {
		let occurrences = [];
		for (var i = 0; i <= this.partitions.length; i++) {
			occurrences.push(this.frequencies[i] || 0);
		}
		return occurrences;
	}
}

/**
 * Class representing a menu with a list of available visualisations.
 */
class SideBar {
	/**
	 * Create a sidebar
	 *
	 * @param {Element} sideBarElement – An element to which the menu should be placed.
	 */
	constructor(sideBarElement) {
		this.sideBarElement = sideBarElement;
		const header = document.createElement('h1');
		header.textContent = 'Available visualisations';
		sideBarElement.appendChild(header);

		const visList = document.createElement('ul');
		sideBarElement.appendChild(visList);

		visualisations.forEach((item, key) => {
			const li = document.createElement('li');

			const a = document.createElement('a');
			a.href = '?' + key;
			a.textContent = item.name;
			li.appendChild(a);

			visList.appendChild(li);
		});

		const sideBarToggle = document.createElement('span');
		sideBarToggle.setAttribute('role', 'button');
		sideBarToggle.setAttribute('tabindex', '0');
		sideBarToggle.classList.add('sidebar-toggle')
		sideBarToggle.addEventListener('click', () => sideBarElement.classList.toggle('collapsed'));
		sideBarToggle.textContent = 'Menu';
		sideBarElement.appendChild(sideBarToggle);
	}

	/**
	 * Collapse the sidebar to save space on the screen.
	 */
	collapse() {
		this.sideBarElement.classList.add('collapsed');
	}

	/**
	 * Expand the sidebar so the user could change the visualisation.
	 */
	expand() {
		this.sideBarElement.classList.remove('collapsed');
	}
}

/**
 * A graphical representation of a Histogram using Chart.js.
 */
class HistoChart {
	/**
	 * Create a chart.
	 *
	 * @param {Canvas} canvas – A canvas element used for painting the chart.
	 */
	constructor(canvas, style) {
		this.chart = null;
		this.chartHolder = canvas;

		this.initStyle(style);
	}

	/**
	 * Initialise a chart style with specified properties.
	 *
	 * Following properties are supported:
	 * - backgroundColor
	 * - borderColor
	 * - borderWidth
	 * - hoverBackgroundColor
	 * - hoverBorderColor
	 *
	 * @param {Object} style – A dictionary defining values of the properties.
	 */
	initStyle(style = {}) {
		this.style = {
			backgroundColor: 'rgba(255,99,132,0.2)',
			borderColor: 'rgba(255,99,132,1)',
			borderWidth: 1,
			hoverBackgroundColor: 'rgba(255,99,132,0.4)',
			hoverBorderColor: 'rgba(255,99,132,1)'
		};

		for (let prop in this.style) {
			if (style.hasOwnProperty(prop)) {
				this.style[prop] = style[prop];
			}
		}
	}

	/**
	 * Update the chart data.
	 *
	 * @param {Histogram} histogram – A histogram that should be visualised.
	 */
	update(histogram) {
		if (!this.chart) {
			this.chart = new Chart(this.chartHolder, {
				type: 'bar',
				data: {
					labels: histogram.labels,
					datasets: [
						{
							backgroundColor: this.style.backgroundColor,
							borderColor: this.style.borderColor,
							borderWidth: this.style.borderWidth,
							hoverBackgroundColor: this.style.hoverBackgroundColor,
							hoverBorderColor: this.style.hoverBorderColor,
							data: histogram.occurrences,
						}
					]
				},
				options: {
					legend: {
						display: false
					},
					scales: {
						yAxes: [{
							ticks: {
								beginAtZero: true
							}
						}]
					}
				}
			});
		} else {
			for (let i in this.chart.data.datasets[0].data) {
				this.chart.data.datasets[0].data[i] = histogram.occurrences[i];
			}
			this.chart.update();
		}
	}
}

/**
 * Class representing a sidebar where a list of currently visible features,
 * as well as informations about the selected feature are displayed.
 */
class InfoBar {
	/**
	 * Create an info sidebar.
	 *
	 * @param {Element} infoBar – An element to which the sidebar should be placed.
	 * @param {Object} visualisation – Currently active visualisation.
	 */
	constructor(infoBar, visualisation) {
		this.visualisation = visualisation;

		this.infoHolder = document.createElement('div');
		this.infoHolder.classList.add('info');
		infoBar.appendChild(this.infoHolder);

		const featureHolder = document.createElement('div');
		infoBar.appendChild(featureHolder);

		this.header = document.createElement('h1');
		this.header.textContent = 'Visible features';
		featureHolder.classList.add('features');
		featureHolder.appendChild(this.header);

		this.emptyMessage = document.createElement('p');
		this.emptyMessage.textContent = 'There are no features in the visible area. Try zooming out or moving the map view.';
		this.emptyMessage.classList.add('hidden');
		featureHolder.appendChild(this.emptyMessage);

		this.featList = document.createElement('ul');
		featureHolder.appendChild(this.featList);

		this.chartEnabled = visualisation.histogram;
		this.histogram = null;

		if (this.chartEnabled) {
			if (visualisation.histogram.partitions) {
				this.histogram = new PartitionedHistogram(visualisation.histogram.partitions);
			} else {
				this.histogram = new Histogram;
			}

			this.plottedCategory = visualisation.histogram.plottedCategory || (feature => feature.get('measured'));

			const chartHolder = document.createElement('canvas');
			chartHolder.classList.add('chart');
			chartHolder.width = infoBar.offsetWidth;
			chartHolder.height = Math.floor(infoBar.offsetWidth / 16 * 9); // Chart will follow 16:9 ratio

			this.chart = new HistoChart(chartHolder, visualisation.histogram.style);

			infoBar.appendChild(chartHolder);
		}
	}

	/**
	 * Set the information about the selected feature.
	 *
	 * @param {String} info – Feature information.
	 */
	setInfo(info) {
		this.infoHolder.innerHTML = info;
	}

	/**
	 * Clear the information after the feature was deselected.
	 */
	clearInfo() {
		this.infoHolder.innerHTML = 'Hover a feature on the map or click on one in the list below to show information about the feature.';
	}

	/**
	 * Update the list of features.
	 *
	 * @param {Array[ol.Feature]} features – List of features to be listed.
	 * @param {Function} highlightFeature – A function that will highlight the feature selected in the list.
	 */
	setFeatures(features, highlightFeature) {
		this.featList.textContent = '';

		if (this.chartEnabled) {
			this.histogram.clear();
		}

		if (features.length == 0) {
			this.header.classList.add('hidden');
			this.emptyMessage.classList.remove('hidden');
		} else {
			this.header.classList.remove('hidden');
			this.emptyMessage.classList.add('hidden');
		}

		features.forEach((feature) => {
			const li = document.createElement('li');
			const highlight = () => {
				highlightFeature(feature);
			};

			li.setAttribute('role', 'button');
			li.setAttribute('tabindex', '0');
			li.feature = feature;
			li.addEventListener('click', highlight);
			li.addEventListener('keypress', (e) => {
				if (e.key == 'Spacebar' || e.key == ' ' || e.key == 'Enter') {
					e.preventDefault();
					highlight();
				}
			});

			if (this.chartEnabled) {
				this.histogram.addValue(this.plottedCategory(feature));
			}

			li.textContent = this.visualisation.listInfo(feature);
			this.featList.appendChild(li);
		});

		if (this.chartEnabled) {
			this.chart.update(this.histogram);
		}
	}
}

window.onload = function() {
	const fileName = window.location.search.toString().replace(/^\?/, '');
	const sideBar = new SideBar(document.getElementById('sidebar'));

	if (!fileName) {
		sideBar.expand();
	} else if (!visualisations.has(fileName)) {
		alert('Unknown visualisation.');
		window.location.search = '';
	} else {
		const currentVisualiser = visualisations.get(fileName);
		const infoBar = new InfoBar(document.getElementById('infobar'), currentVisualiser);

		document.title = 'Address visualisation – ' + currentVisualiser.name;
		sideBar.collapse();
		setTimeout(() => document.body.classList.remove('preload'), 250);

		proj4.defs('urn:ogc:def:crs:EPSG::5514','+proj=krovak +lat_0=49.5 +lon_0=24.83333333333333 +alpha=30.28813972222222 +k=0.9999 +x_0=0 +y_0=0 +ellps=bessel +towgs84=589,76,480,0,0,0,0 +units=m +no_defs');

		function styleFunction(feature) {
			let style;
			let fillColor = 'rgba(240, 0, 0, 0.3)';
			let strokeColor = 'rgba(240, 0, 0, 1)';

			let styleProperty = feature.get('style');
			if (styleProperty) {
				if (styleProperty.fill) {
					fillColor = styleProperty.fill;
				}
				if (styleProperty.stroke) {
					strokeColor = styleProperty.stroke;
				}
			}

			if (feature.getGeometry().getType() === 'Point') {
				style = new ol.style.Style({
					image: new ol.style.Circle({
						radius: 5,
						fill: new ol.style.Fill({
							color: fillColor,
						}),
						stroke: new ol.style.Stroke({
							color: strokeColor,
							width: 10
						})
					})
				});
			} else {
				style = new ol.style.Style({
					fill: new ol.style.Fill({
						color: fillColor,
					}),
					stroke: new ol.style.Stroke({
						color: strokeColor,
						width: 10
					})
				});
			}

			return style;
		}

		const geoJsonSource = new ol.source.Vector({
			url: '../vf_resources/visualisations/' + fileName + '.json',
			format: new ol.format.GeoJSON()
		});

		const map = new ol.Map({
			layers: [
				new ol.layer.Tile({
					source: new ol.source.OSM()
				}),
				new ol.layer.Image({
					source: new ol.source.ImageVector({
						source: geoJsonSource,
						style: styleFunction
					})
				})
			],
			target: 'view',
			view: new ol.View({
				center: ol.proj.fromLonLat([15.5, 49.75]),
				zoom: 8,
				minZoom: 8
			})
		});

		const featureOverlay = new ol.layer.Vector({
			source: new ol.source.Vector(),
			map: map,
			style: new ol.style.Style({
				stroke: new ol.style.Stroke({
					color: '#f0f000',
					width: 10
				}),
				fill: new ol.style.Fill({
					color: 'rgba(240,240,0,0.3)'
				})
			})
		});

		function getFeatureUnderCursor(pixel) {
			return map.forEachFeatureAtPixel(pixel, function(feature) {
				return feature;
			});
		}

		let highlight;
		function highlightFeature(feature) {
			if (feature) {
				infoBar.setInfo(currentVisualiser.info(feature))
			} else {
				infoBar.clearInfo();
			}

			if (feature !== highlight) {
				if (highlight) {
					featureOverlay.getSource().removeFeature(highlight);
				}
				if (feature) {
					featureOverlay.getSource().addFeature(feature);
				}
				highlight = feature;
			}

		};

		map.on('pointermove', function(evt) {
			if (evt.dragging) {
				return;
			}
			const pixel = map.getEventPixel(evt.originalEvent);
			highlightFeature(getFeatureUnderCursor(pixel));
		});

		map.on('click', function(evt) {
			highlightFeature(getFeatureUnderCursor(evt.pixel));
		});

		function updateFeatureList() {
			const extent = map.getView().calculateExtent(map.getSize());
			const features = geoJsonSource.getFeaturesInExtent(extent);
			infoBar.setFeatures(features, highlightFeature);
		}

		geoJsonSource.once('change', () => map.on('postrender', updateFeatureList));
	}
};
<|endoftext|>