<commit_before>var GestureViewController = JSB.defineClass('GestureViewController : UIViewController', {
  viewDidLoad: function() {
    self.navigationItem.title = 'Gesture';

    var swipeView = UIView.alloc().initWithFrame({x: 10, y: 300, width: 300, height: 120});
    swipeView.backgroundColor = UIColor.greenColor();

    var swipeLabel = UILabel.alloc().initWithFrame(swipeView.bounds);
    swipeLabel.text = 'Swipe';
    swipeLabel.textAlignment = 1;
    swipeLabel.textColor = UIColor.whiteColor();
    swipeView.addSubview(swipeLabel);

    self.view.addSubview(swipeView);

    var swipeGestureRecognizer = UISwipeGestureRecognizer.alloc().initWithTargetAction(self, 'handleSwipe:');
    swipeGestureRecognizer.direction = 1 << 0 | 1 << 1;
    swipeView.addGestureRecognizer(swipeGestureRecognizer);

    var tapView = UIView.alloc().initWithFrame({x: 80, y: 100, width: 80, height: 80});
    tapView.backgroundColor = UIColor.redColor();

    var tapLabel = UILabel.alloc().initWithFrame(tapView.bounds);
    tapLabel.text = 'Tap';
    tapLabel.textAlignment = 1;
    tapLabel.textColor = UIColor.whiteColor();
    tapView.addSubview(tapLabel);

    self.view.addSubview(tapView);

    var tapGestureRecognizer = UITapGestureRecognizer.alloc().initWithTargetAction(self, 'handleTap:');
    tapView.addGestureRecognizer(tapGestureRecognizer);

    var panView = UIView.alloc().initWithFrame({x: 180, y: 200, width: 80, height: 80});
    panView.backgroundColor = UIColor.blueColor();

    var panLabel = UILabel.alloc().initWithFrame(panView.bounds);
    panLabel.text = 'Pan';
    panLabel.textAlignment = 1;
    panLabel.textColor = UIColor.whiteColor();
    panView.addSubview(panLabel);

    self.view.addSubview(panView);

    var panGestureRecognizer = UIPanGestureRecognizer.alloc().initWithTargetAction(self, 'handlePan:');
    panView.addGestureRecognizer(panGestureRecognizer);
  },
  handleTap: function(sender) {
    var alertView = UIAlertView.new();
    alertView.message = 'Tapped.';
    alertView.addButtonWithTitle('OK');
    alertView.show();
  },
  handlePan: function(sender) {
    var point = sender.translationInView(sender.view);
    var center = sender.view.center;
    sender.view.center = {x: center.x + point.x, y: center.y + point.y};

    sender.setTranslationInView({x: 0, y: 0}, sender.view);
  },
  handleSwipe: function(sender) {
    var label = UILabel.alloc().initWithFrame({x: 0, y: 0, width: 180, height: 60});
    label.backgroundColor = UIColor.colorWithWhiteAlpha(0.0, 0.5);
    label.center = self.view.center;
    label.alpha = 0.0;
    label.text = 'Swiped';
    label.textAlignment = 1;
    label.textColor = UIColor.whiteColor();
    self.view.addSubview(label);

    UIView.animateWithDurationDelayOptionsAnimationsCompletion(0.3, 0.0, 0 << 16, function() {
      label.alpha = 1.0;
    }, function(finished) {
      UIView.animateWithDurationDelayOptionsAnimationsCompletion(0.3, 0.5, 0 << 16, function() {
        label.alpha = 0.0;
      }, function(finished) {
        label.removeFromSuperView();
      });
    });
  }
});

JSB.exports = GestureViewController;
<commit_msg>Fix typo.<commit_after>var GestureViewController = JSB.defineClass('GestureViewController : UIViewController', {
  viewDidLoad: function() {
    self.navigationItem.title = 'Gesture';

    var swipeView = UIView.alloc().initWithFrame({x: 10, y: 300, width: 300, height: 120});
    swipeView.backgroundColor = UIColor.greenColor();

    var swipeLabel = UILabel.alloc().initWithFrame(swipeView.bounds);
    swipeLabel.text = 'Swipe';
    swipeLabel.textAlignment = 1;
    swipeLabel.textColor = UIColor.whiteColor();
    swipeView.addSubview(swipeLabel);

    self.view.addSubview(swipeView);

    var swipeGestureRecognizer = UISwipeGestureRecognizer.alloc().initWithTargetAction(self, 'handleSwipe:');
    swipeGestureRecognizer.direction = 1 << 0 | 1 << 1;
    swipeView.addGestureRecognizer(swipeGestureRecognizer);

    var tapView = UIView.alloc().initWithFrame({x: 80, y: 100, width: 80, height: 80});
    tapView.backgroundColor = UIColor.redColor();

    var tapLabel = UILabel.alloc().initWithFrame(tapView.bounds);
    tapLabel.text = 'Tap';
    tapLabel.textAlignment = 1;
    tapLabel.textColor = UIColor.whiteColor();
    tapView.addSubview(tapLabel);

    self.view.addSubview(tapView);

    var tapGestureRecognizer = UITapGestureRecognizer.alloc().initWithTargetAction(self, 'handleTap:');
    tapView.addGestureRecognizer(tapGestureRecognizer);

    var panView = UIView.alloc().initWithFrame({x: 180, y: 200, width: 80, height: 80});
    panView.backgroundColor = UIColor.blueColor();

    var panLabel = UILabel.alloc().initWithFrame(panView.bounds);
    panLabel.text = 'Pan';
    panLabel.textAlignment = 1;
    panLabel.textColor = UIColor.whiteColor();
    panView.addSubview(panLabel);

    self.view.addSubview(panView);

    var panGestureRecognizer = UIPanGestureRecognizer.alloc().initWithTargetAction(self, 'handlePan:');
    panView.addGestureRecognizer(panGestureRecognizer);
  },
  handleTap: function(sender) {
    var alertView = UIAlertView.new();
    alertView.message = 'Tapped.';
    alertView.addButtonWithTitle('OK');
    alertView.show();
  },
  handlePan: function(sender) {
    var point = sender.translationInView(sender.view);
    var center = sender.view.center;
    sender.view.center = {x: center.x + point.x, y: center.y + point.y};

    sender.setTranslationInView({x: 0, y: 0}, sender.view);
  },
  handleSwipe: function(sender) {
    var label = UILabel.alloc().initWithFrame({x: 0, y: 0, width: 180, height: 60});
    label.backgroundColor = UIColor.colorWithWhiteAlpha(0.0, 0.5);
    label.center = self.view.center;
    label.alpha = 0.0;
    label.text = 'Swiped';
    label.textAlignment = 1;
    label.textColor = UIColor.whiteColor();
    self.view.addSubview(label);

    UIView.animateWithDurationDelayOptionsAnimationsCompletion(0.3, 0.0, 0 << 16, function() {
      label.alpha = 1.0;
    }, function(finished) {
      UIView.animateWithDurationDelayOptionsAnimationsCompletion(0.3, 0.5, 0 << 16, function() {
        label.alpha = 0.0;
      }, function(finished) {
        label.removeFromSuperview();
      });
    });
  }
});

JSB.exports = GestureViewController;
<|endoftext|>


<commit_before>import {createActions, handleActions} from 'redux-actions'
import debounce from 'debounce'

const actions = createActions({
  SEARCH: undefined,
  GENRE: undefined,
  SORTER: undefined
})

const mapToState = (key) => (state, {payload}) => Object.assign({}, state, {[key]: payload})

const reducer = handleActions({
  SEARCH: mapToState('search'),
  GENRE: mapToState('genre'),
  SORTER: mapToState('sorter'),
}, {search: null, genre: null, sorter: 'trending'})

const bindFiltersActions = (dispatch) => ({
  search: debounce((term) => dispatch(actions.search(term)), 250),
  genre: (genre) => dispatch(actions.genre(genre)),
  sorter: (sorter) => dispatch(actions.sorter(sorter))
})

const filters = {
  reducer,
  actions
}

export {filters as default, bindFiltersActions}
<commit_msg>filters reduxers: order<commit_after>import {createActions, handleActions} from 'redux-actions'
import debounce from 'debounce'

const actions = createActions({
  SEARCH: undefined,
  GENRE: undefined,
  SORTER: undefined,
  ORDER: undefined,
})

const mapToState = (key) => (state, {payload}) => Object.assign({}, state, {[key]: payload})

const reducer = handleActions({
  SEARCH: mapToState('search'),
  GENRE: mapToState('genre'),
  SORTER: mapToState('sorter'),
  ORDER: mapToState('order'),
}, {search: null, genre: null, sorter: 'trending', order: 'desc'})

const bindFiltersActions = (dispatch) => ({
  search: debounce((term) => dispatch(actions.search(term)), 250),
  genre: (genre) => dispatch(actions.genre(genre)),
  sorter: (sorter) => dispatch(actions.sorter(sorter)),
  order: (order) => dispatch(actions.order(order))
})

const filters = {
  reducer,
  actions
}

export {filters as default, bindFiltersActions}
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc directive
 * @name reg.threeSixty:regThreesixty
 * @description
 * # regThreesixty
 */
angular.module('reg.threesixty', [])
  .directive('threesixty', ['$document', '$window',function ($document, $window) {
    return {
      template: '

',
      restrict: 'E',
      replace:true,
      scope:{
        images: '=',
        reverse: '=',
        animateAfterLoading: '=',
        speedMultiplier: '='
      },
      link: function(scope, element, attrs) {

        var img;
        var currentFrame = 0;
        var endFrame;
        var ticker = 0;
        var totalFrames;
        var loadedImages;
        var frames = [];
        var ready = false;
        var dragging;
        var pointerEndPosX;
        var pointerStartPosX;
        var pointerDistance;
        var monitorStartTime = 0;
        var monitorInt = 0;
        var speedMultiplier = scope.speedMultiplier ? parseInt(scope.speedMultiplier) : 20;
        var ROTATION_EVENT = 'threesixty-animate';

        var adjustHeight = function(){
          if( loadedImages > 0 ){
            var elementW = element[0].offsetWidth;
            var imageW = frames[0].width;
            var h = frames[0].height * ( elementW / imageW );
            element.css( 'height' , h + 'px' );
          }
        };

        angular.element($window).on('resize', adjustHeight );

        var load360Images = function(){

          for( var i = 1 ; i < scope.images.length ; i++ ){
            img = new Image();
            img.onload = imageReady;
            element.append( img );
            frames[i] = img;
            img.src = scope.images[ i ];
          }

        };

        var imageReady = function( event ){
          loadedImages ++;
          if( loadedImages === totalFrames ){
            ready = true;
            // start
            endFrame = totalFrames;

            if (scope.animateAfterLoading) {
              refresh();
            }
          }
        };

        var firstImageReady = function(){
          // Remove previous images.
          element.find('img').remove();
          loadedImages ++;
          var firstImage = frames[0];
          firstImage.className = 'current';
          adjustHeight();
          element.append( firstImage );
          element.removeClass('loading-first');
          load360Images();
        };

        var initImages = function(){

          element.addClass('loading-first');

          frames = [];
          totalFrames = scope.images.length;
          loadedImages = 0;

          if( totalFrames > 0 ){
            // Load first image
            img = new Image();
            img.onload = firstImageReady;
            img.src = scope.images[ 0 ];
            frames.push(img);
          }

        };

        initImages();

        // Update images on model change
        // only if image list changes
        scope.$watchCollection('images', function( newImageList, oldImageList){

          if( newImageList.length != oldImageList.length ){
            initImages();
          }else{
            for (var i = 0; i < oldImageList.length; i++) {
              if( newImageList[ i ] !== oldImageList[ i ] ){
                initImages();
                break;
              }
            }
          }

        } );

        var refresh = function (animationSpeed) {

          if (ticker === 0) {
            ticker = setInterval(render, animationSpeed || Math.round(1000 / 30));
          }
        };

        var getNormalizedCurrentFrame = function() {
          var c = -Math.ceil(currentFrame % totalFrames);
          if (c < 0) {
            c += (totalFrames - 1);
          }
          return c;
        };

        var hidePreviousFrame = function() {
          frames[getNormalizedCurrentFrame()].className = '';
        };

        var showCurrentFrame = function() {
          frames[getNormalizedCurrentFrame()].className = 'current';
        };

        var render = function() {
          if( frames.length >0 && currentFrame !== endFrame){
            var frameEasing = endFrame < currentFrame ?
                Math.floor((endFrame - currentFrame) * 0.1) :
                Math.ceil((endFrame - currentFrame) * 0.1);
            hidePreviousFrame();
            currentFrame += frameEasing;
            showCurrentFrame();
          } else {
            $window.clearInterval(ticker);
            ticker = 0;
          }
        };

        // Touch and Click events

        var getPointerEvent = function(event) {
          return event.targetTouches ? event.targetTouches[0] : event;
        };

        element.on('touchstart mousedown', mousedown);

        function mousedown (event) {
          event.preventDefault();
          pointerStartPosX = getPointerEvent(event).pageX;
          dragging = true;

          $document.on('touchmove mousemove', mousemove);
          $document.on('touchend mouseup', mouseup);
        }

        function trackPointer(event){
          if (ready && dragging) {

            pointerEndPosX = getPointerEvent(event).pageX;

            if(monitorStartTime < new Date().getTime() - monitorInt) {
              var frameDiff = 0,
                direction = scope.reverse? -1 : 1 ;

              pointerDistance = pointerEndPosX - pointerStartPosX;

              if(pointerDistance > 0){
                frameDiff = Math.ceil((totalFrames - 1) * speedMultiplier * (pointerDistance / element[0].clientWidth));
              }else{
                frameDiff = Math.floor((totalFrames - 1) * speedMultiplier * (pointerDistance / element[0].clientWidth));
              }

              endFrame = currentFrame + (direction * frameDiff);

              refresh();
              monitorStartTime = new Date().getTime();
              pointerStartPosX = getPointerEvent(event).pageX;
            }
          }
        }

        function mouseup(event){
          event.preventDefault();
          dragging = false;
          $document.off('touchmove mousemove', mousemove);
          $document.off('touchend mouseup', mouseup);
        }

        function mousemove(event){
          event.preventDefault();
          trackPointer(event);
        }

        scope.$on(ROTATION_EVENT, function(event, animationSpeed) {
          ticker = 0;
          endFrame = currentFrame + totalFrames;
          refresh(animationSpeed);
        });

        scope.$on( '$destroy', function() {
          $document.off('touchmove mousemove', mousemove);
          $document.off('touchend mouseup', mouseup);
          angular.element($window).off('resize', adjustHeight );
        });

      }
    };
  }]);
<commit_msg>fix calculation of the image-height for IE using the `naturalHeight`<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name reg.threeSixty:regThreesixty
 * @description
 * # regThreesixty
 */
angular.module('reg.threesixty', [])
  .directive('threesixty', ['$document', '$window',function ($document, $window) {
    return {
      template: '

',
      restrict: 'E',
      replace:true,
      scope:{
        images: '=',
        reverse: '=',
        animateAfterLoading: '=',
        speedMultiplier: '='
      },
      link: function(scope, element, attrs) {

        var img;
        var currentFrame = 0;
        var endFrame;
        var ticker = 0;
        var totalFrames;
        var loadedImages;
        var frames = [];
        var ready = false;
        var dragging;
        var pointerEndPosX;
        var pointerStartPosX;
        var pointerDistance;
        var monitorStartTime = 0;
        var monitorInt = 0;
        var speedMultiplier = scope.speedMultiplier ? parseInt(scope.speedMultiplier) : 20;
        var ROTATION_EVENT = 'threesixty-animate';

        var adjustHeight = function(){
          if( loadedImages > 0 ){
            var elementW = element[0].offsetWidth;
            var imageW = frames[0].width;
            var h = frames[0].naturalHeight * ( elementW / imageW );
            element.css( 'height' , h + 'px' );
          }
        };

        angular.element($window).on('resize', adjustHeight );

        var load360Images = function(){

          for( var i = 1 ; i < scope.images.length ; i++ ){
            img = new Image();
            img.onload = imageReady;
            element.append( img );
            frames[i] = img;
            img.src = scope.images[ i ];
          }

        };

        var imageReady = function( event ){
          loadedImages ++;
          if( loadedImages === totalFrames ){
            ready = true;
            // start
            endFrame = totalFrames;

            if (scope.animateAfterLoading) {
              refresh();
            }
          }
        };

        var firstImageReady = function(){
          // Remove previous images.
          element.find('img').remove();
          loadedImages ++;
          var firstImage = frames[0];
          firstImage.className = 'current';
          adjustHeight();
          element.append( firstImage );
          element.removeClass('loading-first');
          load360Images();
        };

        var initImages = function(){

          element.addClass('loading-first');

          frames = [];
          totalFrames = scope.images.length;
          loadedImages = 0;

          if( totalFrames > 0 ){
            // Load first image
            img = new Image();
            img.onload = firstImageReady;
            img.src = scope.images[ 0 ];
            frames.push(img);
          }

        };

        initImages();

        // Update images on model change
        // only if image list changes
        scope.$watchCollection('images', function( newImageList, oldImageList){

          if( newImageList.length != oldImageList.length ){
            initImages();
          }else{
            for (var i = 0; i < oldImageList.length; i++) {
              if( newImageList[ i ] !== oldImageList[ i ] ){
                initImages();
                break;
              }
            }
          }

        } );

        var refresh = function (animationSpeed) {

          if (ticker === 0) {
            ticker = setInterval(render, animationSpeed || Math.round(1000 / 30));
          }
        };

        var getNormalizedCurrentFrame = function() {
          var c = -Math.ceil(currentFrame % totalFrames);
          if (c < 0) {
            c += (totalFrames - 1);
          }
          return c;
        };

        var hidePreviousFrame = function() {
          frames[getNormalizedCurrentFrame()].className = '';
        };

        var showCurrentFrame = function() {
          frames[getNormalizedCurrentFrame()].className = 'current';
        };

        var render = function() {
          if( frames.length >0 && currentFrame !== endFrame){
            var frameEasing = endFrame < currentFrame ?
                Math.floor((endFrame - currentFrame) * 0.1) :
                Math.ceil((endFrame - currentFrame) * 0.1);
            hidePreviousFrame();
            currentFrame += frameEasing;
            showCurrentFrame();
          } else {
            $window.clearInterval(ticker);
            ticker = 0;
          }
        };

        // Touch and Click events

        var getPointerEvent = function(event) {
          return event.targetTouches ? event.targetTouches[0] : event;
        };

        element.on('touchstart mousedown', mousedown);

        function mousedown (event) {
          event.preventDefault();
          pointerStartPosX = getPointerEvent(event).pageX;
          dragging = true;

          $document.on('touchmove mousemove', mousemove);
          $document.on('touchend mouseup', mouseup);
        }

        function trackPointer(event){
          if (ready && dragging) {

            pointerEndPosX = getPointerEvent(event).pageX;

            if(monitorStartTime < new Date().getTime() - monitorInt) {
              var frameDiff = 0,
                direction = scope.reverse? -1 : 1 ;

              pointerDistance = pointerEndPosX - pointerStartPosX;

              if(pointerDistance > 0){
                frameDiff = Math.ceil((totalFrames - 1) * speedMultiplier * (pointerDistance / element[0].clientWidth));
              }else{
                frameDiff = Math.floor((totalFrames - 1) * speedMultiplier * (pointerDistance / element[0].clientWidth));
              }

              endFrame = currentFrame + (direction * frameDiff);

              refresh();
              monitorStartTime = new Date().getTime();
              pointerStartPosX = getPointerEvent(event).pageX;
            }
          }
        }

        function mouseup(event){
          event.preventDefault();
          dragging = false;
          $document.off('touchmove mousemove', mousemove);
          $document.off('touchend mouseup', mouseup);
        }

        function mousemove(event){
          event.preventDefault();
          trackPointer(event);
        }

        scope.$on(ROTATION_EVENT, function(event, animationSpeed) {
          ticker = 0;
          endFrame = currentFrame + totalFrames;
          refresh(animationSpeed);
        });

        scope.$on( '$destroy', function() {
          $document.off('touchmove mousemove', mousemove);
          $document.off('touchend mouseup', mouseup);
          angular.element($window).off('resize', adjustHeight );
        });

      }
    };
  }]);
<|endoftext|>


<commit_before>import {createHashMap} from "../hashmap.js";
import {colors} from "../color.js";
import {clamp, divisors, nthParse, nthCheck} from "../math.js";
import {toArray, isObject, isValid} from "../util.js";

//Default panels props
let defaultProps = {
    "background": "transparent",
    "border": false,
    "borderColor": colors.black,
    "borderWidth": "1px"
};

//Default panels layout
let defaultPanelsLayout = {"rows": 1, "cols": 1, "spacing": 0};

//Calculate panels layout from value
// For example, if a value of 20 is provided, we calculate all divisors and calculate a score
// We will get the divisors pair with the lowest score. If there are more than one with the same score,
// we will get the one with the lowest first divisor (rows).
// 2, 10 ---> score = 12
// 4, 5  ---> score = 9  <<<< this is the best layout
// 5, 4  ---> score = 9
// 10, 2 ---> score = 12
// Result >>> rows=4, cols=5
let buildPanelsLayout = function (value) {
    //Check for negative, 0 or 1 values
    if (value < 2) {
        return {"rows": 1, "cols": 1}; //Return at least one panel
    }
    else if (value === 2 || value === 3) {
        return {"rows": 1, "cols": value}; //Return only one row
    }
    let allDivisors = divisors(value); //Get all divisors of the provided value
    //Check if the value is a primer number (only one divisor)
    if (allDivisors.length === 1) {
        allDivisors = divisors(value + 1); //Convert to even number
    }
    //Calculate a score of each divisor
    let scores = allDivisors.map(function (d) {
        return d[0] + d[1];
    });
    let besScore = Math.min.apply(null, scores); //Get the best score
    let filteredDivisors = allDivisors.filter(function (d, index) {
        return bestScore === scores[index]; //Get only the divisors of the best score
    });
    //Get the best divisor --> the one with the lowest first divisor
    let bestDivisor = filteredDivisors[0]; //Get the first divisor
    //The divisors list are sorted, so the first one has the lowest row value
    //for (let i = 1; i < filteredDivisors.length; i++) {
    //    if (bestDivisor[0] > filteredDivisors[i][0]) {
    //        bestDivisor = filteredDivisors[i]; //Update the best divisor
    //    }
    //}
    //Return the best panels layout
    return {"rows": bestDivisor[0], "cols": bestDivisor[1]};
};

//Get panels layout
let getPanelsLayout = function (context, props) {
    let newLayout = Object.assign({}, defaultPanelsLayout); //Initialize layout
    //Check for object ---> merge with default props
    if (isObject(props) === true) {
        //Check for row and column values
        if (isValid(props.rows) && isValid(props.cols)) {
            Object.assign(newLayout, {
                "rows": context.value(props.rows, 1, 1),
                "cols": context.value(props.cols, 1, 1)
            });
        }
        //Check for size value --> calculate from
        else if (isValid(props.size)) {
            Object.assign(newLayout, buildPanelsLayout(context.value(props.size, 1, 1)));
        }
        //Check for spacing value
        if (isValid(props.spacing)) {
            Object.assign(newLayout, {"spacing": context.value(props.spacing, 0, 0)});
        }
    }
    //Check for number --> calculate the number of rows and columns
    else if (typeof props === "number") {
        Object.assign(newLayout, buildPanelsLayout(props)); //Build the layout
    }
    //Return the layout
    return newLayout;
};

//Get panels elements
export function getPanelsElements (context, props) {
    if (typeof props === "undefined" || props === null || props == "") {
        return [context.panels.elements[0]]; //Return a single panel element
    }
    else if (typeof props === "string" && props === "*") {
        return context.panels.elements; //Return all panels
    }
    //let panels = context.panels;
    //Generate a list with all wanted panels indexes
    let indexes = {};
    toArray(props).forEach(function (value) {
        //Check for number --> as a single index
        if (typeof value === "number") {
            let index = clamp(value, 0, context.panels.elements.length - 1);
            indexes[index] = 1; //Save this index
        }
        //Check for object --> TODO
        else if (isObject(value) && props !== null) {
            //TODO
        }
        //Check for string ---> parse as a nth expression
        else if (typeof value === "string") {
            let nthExpression = nthParse(value); //Parse as an nth expression
            for (let i = 0; i < context.panels.value.length; i++) {
                if (nthCheck(i, nthExpression) === true) {
                    indexes[i] = 1; //Save this index
                }
            }
        }
        //Ignore other value types
    });
    //Return an array with all panels
    return Object.keys(indexes).map(function (index) {
        return context.panels.elements[index];
    });
}

//Create a panels node
export function createPanelsNode (context, props) {
    let node = context.addNode({
        "id": "panels",
        "type": "panels",
        "value": {"width": 0, "height": 0, "length": 0, "rows": 0, "cols": 0},
        "targets": createHashMap(),
        "elements": [],
        "props": props //parsePanelsProps(props)
    });
    //Add this node as a dependency of draw nodes
    context.draw.width.targets.add(node.id, node);
    context.draw.height.targets.add(node.id, node);
    context.draw.margin.targets.add(node.id, node);
    context.draw.outerMargin.targets.add(node.id, node);
    //Save the reference to the panels node
    context.panels = node;
}

//Update a panels node
export function updatePanelsNode (context, node) {
    let props = (isObject(node.props)) ? node.props : {};
    let layout = getPanelsLayout(context, node.props); //Build layout
    layout.length = layout.rows * layout.cols; //Get layout total size
    //Check if we need to rebuild all panels groups
    if (node.value.rows !== layout.rows || node.value.cols !== layout.cols) {
        context.target.selectAll("g[data-type='panel']").remove(); //Remove all panels
        node.elements = []; //Reset nodes list
        for (let i = 0; i < layout.length; i++) {
            let row = Math.floor(i / layout.cols); //Get row index
            let col = i % layout.cols; //Get column index
            let element = context.target.append("g");
            element.attr("data-type", "panel"); //Set the group type ---> panel
            element.attr("data-row", row + ""); //Set the panel row
            element.attr("data-col", col + ""); //Set the panel col
            //Create a rectangle for displaying panel background
            let bg = element.append("rect");
            bg.attr("id", "panel-background").attr("x", "0").attr("y", "0").attr("fill", "none");
            //Save the panel group element
            node.elements.push(element);
        }
    }
    //Get the new sizes
    let margin = context.draw.margin.value;
    let outerMargin = context.draw.outerMargin.value;
    layout.width = ((context.draw.width.value - outerMargin.left - outerMargin.right) / layout.cols) - margin.left - margin.right;
    layout.height = ((context.draw.height.value - outerMargin.top - outerMargin.bottom) / layout.rows) - margin.top - margin.bottom;
    //Update the groups positions
    node.elements.forEach(function (element) {
        let row = parseInt(element.attr("data-row")); //Get row index
        let col = parseInt(element.attr("data-col")); //Get column index
        let left = (col * layout.width) + (col + 1) * margin.left + col * margin.right; //Calculate the left translation
        let top = (row * layout.height) + (row + 1) * margin.top + row * margin.bottom; //Calculate the top translation
        element.attr("transform", `translate(${left},${top})`); //Translate the panel group
        //Update the panel background
        let bg = element.selectAll("#panel-background");
        bg.attr("width", layout.width).attr("height", layout.height); //Set background size
        bg.attr("fill", context.value(props.background, null, context.theme.panelBackground));
    });
    //Update the layout config
    node.value = layout;
}

<commit_msg>render: improve panels selector and allow selection by row or column<commit_after>import {createHashMap} from "../hashmap.js";
import {colors} from "../color.js";
import {clamp, divisors, nthParse, nthCheck} from "../math.js";
import {toArray, isObject, isValid} from "../util.js";

//Default panels props
let defaultProps = {
    "background": "transparent",
    "border": false,
    "borderColor": colors.black,
    "borderWidth": "1px"
};

//Default panels layout
let defaultPanelsLayout = {"rows": 1, "cols": 1, "spacing": 0};

//Calculate panels layout from value
// For example, if a value of 20 is provided, we calculate all divisors and calculate a score
// We will get the divisors pair with the lowest score. If there are more than one with the same score,
// we will get the one with the lowest first divisor (rows).
// 2, 10 ---> score = 12
// 4, 5  ---> score = 9  <<<< this is the best layout
// 5, 4  ---> score = 9
// 10, 2 ---> score = 12
// Result >>> rows=4, cols=5
let buildPanelsLayout = function (value) {
    //Check for negative, 0 or 1 values
    if (value < 2) {
        return {"rows": 1, "cols": 1}; //Return at least one panel
    }
    else if (value === 2 || value === 3) {
        return {"rows": 1, "cols": value}; //Return only one row
    }
    let allDivisors = divisors(value); //Get all divisors of the provided value
    //Check if the value is a primer number (only one divisor)
    if (allDivisors.length === 1) {
        allDivisors = divisors(value + 1); //Convert to even number
    }
    //Calculate a score of each divisor
    let scores = allDivisors.map(function (d) {
        return d[0] + d[1];
    });
    let bestScore = Math.min.apply(null, scores); //Get the best score
    let filteredDivisors = allDivisors.filter(function (d, index) {
        return bestScore === scores[index]; //Get only the divisors of the best score
    });
    //Get the best divisor --> the one with the lowest first divisor
    let bestDivisor = filteredDivisors[0]; //Get the first divisor
    //The divisors list are sorted, so the first one has the lowest row value
    //for (let i = 1; i < filteredDivisors.length; i++) {
    //    if (bestDivisor[0] > filteredDivisors[i][0]) {
    //        bestDivisor = filteredDivisors[i]; //Update the best divisor
    //    }
    //}
    //Return the best panels layout
    return {"rows": bestDivisor[0], "cols": bestDivisor[1]};
};

//Get panels layout
let getPanelsLayout = function (context, props) {
    let newLayout = Object.assign({}, defaultPanelsLayout); //Initialize layout
    //Check for object ---> merge with default props
    if (isObject(props) === true) {
        //Check for row and column values
        if (isValid(props.rows) && isValid(props.cols)) {
            Object.assign(newLayout, {
                "rows": context.value(props.rows, 1, 1),
                "cols": context.value(props.cols, 1, 1)
            });
        }
        //Check for size value --> calculate from
        else if (isValid(props.size)) {
            Object.assign(newLayout, buildPanelsLayout(context.value(props.size, 1, 1)));
        }
        //Check for spacing value
        if (isValid(props.spacing)) {
            Object.assign(newLayout, {"spacing": context.value(props.spacing, 0, 0)});
        }
    }
    //Check for number --> calculate the number of rows and columns
    else if (typeof props === "number") {
        Object.assign(newLayout, buildPanelsLayout(props)); //Build the layout
    }
    //Return the layout
    return newLayout;
};

//Get panels elements
export function getPanelsElements (context, props) {
    let layout = context.panels.value;
    if (typeof props === "undefined" || props === null || props == "") {
        return [context.panels.elements[0]]; //Return a single panel element
    }
    else if (typeof props === "string" && props === "*") {
        return context.panels.elements; //Return all panels
    }
    //let panels = context.panels;
    //Generate a list with all wanted panels indexes
    let indexes = {};
    toArray(props).forEach(function (value) {
        //Check for number --> as a single index
        if (typeof value === "number") {
            let index = clamp(value - 1, 0, context.panels.elements.length - 1);
            indexes[index] = 1; //Save this index
        }
        //Check for object --> TODO
        else if (isObject(value) && (typeof value.row === "number" || typeof value.col === "number")) {
            //Check for row and column provided
            if (typeof value.row === "number" && typeof value.col === "number") {
                let index = (value.col - 1) + (value.row - 1) * layout.cols;
                if (0 <= index && index < layout.length) {
                    indexes[index] = 1; //Save this single item
                }
            }
            //Check for only row value provided
            else if (typeof value.row === "number") {
                let row = value.row - 1;
                for (let i = 0; i < layout.cols; i++) {
                    let index = i + row * layout.cols;
                    if (0 <= index && index < layout.length) {
                        indexes[index] = 1; //Save this item
                    }
                }
            }
            //Check for only column value provided
            else {
                let col = value.col - 1; //Get column
                for (let i = 0; i < layout.rows; i++) {
                    let index = col + i * layout.cols;
                    if (0 <= index && index < layout.length) {
                        indexes[index] = 1; //Save this item
                    }
                }
            }
        }
        //Check for string ---> parse as a nth expression
        else if (typeof value === "string") {
            let nthExpression = nthParse(value); //Parse as an nth expression
            for (let i = 0; i < context.panels.value.length; i++) {
                if (nthCheck(i + 1, nthExpression) === true) {
                    indexes[i] = 1; //Save this index
                }
            }
        }
        //Ignore other value types
    });
    //Return an array with all panels
    return Object.keys(indexes).map(function (index) {
        return context.panels.elements[index];
    });
}

//Create a panels node
export function createPanelsNode (context, props) {
    let node = context.addNode({
        "id": "panels",
        "type": "panels",
        "value": {"width": 0, "height": 0, "length": 0, "rows": 0, "cols": 0},
        "targets": createHashMap(),
        "elements": [],
        "props": props //parsePanelsProps(props)
    });
    //Add this node as a dependency of draw nodes
    context.draw.width.targets.add(node.id, node);
    context.draw.height.targets.add(node.id, node);
    context.draw.margin.targets.add(node.id, node);
    context.draw.outerMargin.targets.add(node.id, node);
    //Save the reference to the panels node
    context.panels = node;
}

//Update a panels node
export function updatePanelsNode (context, node) {
    let props = (isObject(node.props)) ? node.props : {};
    let layout = getPanelsLayout(context, node.props); //Build layout
    layout.length = layout.rows * layout.cols; //Get layout total size
    //Check if we need to rebuild all panels groups
    if (node.value.rows !== layout.rows || node.value.cols !== layout.cols) {
        context.target.selectAll("g[data-type='panel']").remove(); //Remove all panels
        node.elements = []; //Reset nodes list
        for (let i = 0; i < layout.length; i++) {
            let row = Math.floor(i / layout.cols); //Get row index
            let col = i % layout.cols; //Get column index
            let element = context.target.append("g");
            element.attr("data-type", "panel"); //Set the group type ---> panel
            element.attr("data-row", row + ""); //Set the panel row
            element.attr("data-col", col + ""); //Set the panel col
            //Create a rectangle for displaying panel background
            let bg = element.append("rect");
            bg.attr("id", "panel-background").attr("x", "0").attr("y", "0").attr("fill", "none");
            //Save the panel group element
            node.elements.push(element);
        }
    }
    //Get the new sizes
    let margin = context.draw.margin.value;
    let outerMargin = context.draw.outerMargin.value;
    layout.width = ((context.draw.width.value - outerMargin.left - outerMargin.right) / layout.cols) - margin.left - margin.right;
    layout.height = ((context.draw.height.value - outerMargin.top - outerMargin.bottom) / layout.rows) - margin.top - margin.bottom;
    //Update the groups positions
    node.elements.forEach(function (element) {
        let row = parseInt(element.attr("data-row")); //Get row index
        let col = parseInt(element.attr("data-col")); //Get column index
        let left = (col * layout.width) + (col + 1) * margin.left + col * margin.right; //Calculate the left translation
        let top = (row * layout.height) + (row + 1) * margin.top + row * margin.bottom; //Calculate the top translation
        element.attr("transform", `translate(${left},${top})`); //Translate the panel group
        //Update the panel background
        let bg = element.selectAll("#panel-background");
        bg.attr("width", layout.width).attr("height", layout.height); //Set background size
        bg.attr("fill", context.value(props.background, null, context.theme.panelBackground));
    });
    //Update the layout config
    node.value = layout;
}

<|endoftext|>


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

  // prerequisites
  if ( ! ( 'querySelectorAll' in document ) ){ return; }

  var targets = [],
      $redirects = document.querySelector('meta[property="webmention:redirected_from"]'),
      redirects,
      base_url = window.location.origin,
      $script;

  targets.push( base_url + window.location.pathname );
  if ( $redirects )
  {
    redirects = $redirects.getAttribute('content').split(',');
    redirects.forEach(function( value ){
      targets.push(
        value.indexOf('//') < 0 ? base_url + value : value
      );
    });
    redirects = false;
  }

  // Load up any unpublished webmentions on load
  $script = document.createElement('script');
  $script.async = true;
  $script.src = '//webmention.io/api/mentions?' +
                'jsonp=window.JekyllWebmentionIO.processWebmentions&target[]=' +
                targets.join( '&target[]=' );
  document.querySelector('head').appendChild( $script );

}(this, this.document, this.JekyllWebmentionIO));<commit_msg>moving to https always<commit_after>(function(window, document, JekyllWebmentionIO){

  // prerequisites
  if ( ! ( 'querySelectorAll' in document ) ){ return; }

  var targets = [],
      $redirects = document.querySelector('meta[property="webmention:redirected_from"]'),
      redirects,
      base_url = window.location.origin,
      $script;

  targets.push( base_url + window.location.pathname );
  if ( $redirects )
  {
    redirects = $redirects.getAttribute('content').split(',');
    redirects.forEach(function( value ){
      targets.push(
        value.indexOf('//') < 0 ? base_url + value : value
      );
    });
    redirects = false;
  }

  // Load up any unpublished webmentions on load
  $script = document.createElement('script');
  $script.async = true;
  $script.src = 'https://webmention.io/api/mentions?' +
                'jsonp=window.JekyllWebmentionIO.processWebmentions&target[]=' +
                targets.join( '&target[]=' );
  document.querySelector('head').appendChild( $script );

}(this, this.document, this.JekyllWebmentionIO));<|endoftext|>


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

  require.config({
    // # Paths
    paths: {
      // ## Requirejs plugins
      text: '../../bower_components/requirejs-text/text',
      hbs: '../../bower_components/require-handlebars-plugin/hbs',

      // ## Core Libraries
      jquery: '../../bower_components/jquery/dist/jquery',
      underscore: '../../bower_components/lodash/lodash',
      backbone: '../../bower_components/backbone/backbone',
      'hbs/handlebars': '../../bower_components/require-handlebars-plugin/hbs/handlebars',

      // ## Backbone plugins
      'backbone-associations': '../../bower_components/backbone-associations/backbone-associations',

      // ## MathJax
      mathjax: '//cdn.mathjax.org/mathjax/2.3-latest/MathJax.js?config=MML_HTMLorMML',

      // ## Zendesk
      zendesk: '//assets.zendesk.com/external/zenbox/v2.6/zenbox',

      // Use Minified Aloha because loading files in a different requirejs context is a royal pain
      aloha: '../../bower_components/aloha-editor/target/build-profile-with-oer/rjs-output/lib/aloha',

      // ## UI Libraries and Helpers
      tooltip: 'helpers/backbone/views/attached/tooltip/tooltip',
      popover: 'helpers/backbone/views/attached/popover/popover',
      // Boostrap Plugins
      bootstrapAffix: '../../bower_components/bootstrap/js/affix',
      bootstrapAlert: '../../bower_components/bootstrap/js/alert',
      bootstrapButton: '../../bower_components/bootstrap/js/button',
      bootstrapCarousel: '../../bower_components/bootstrap/js/carousel',
      bootstrapCollapse: '../../bower_components/bootstrap/js/collapse',
      bootstrapDropdown: '../../bower_components/bootstrap/js/dropdown',
      bootstrapModal: '../../bower_components/bootstrap/js/modal',
      bootstrapPopover: '../../bower_components/bootstrap/js/popover',
      bootstrapScrollspy: '../../bower_components/bootstrap/js/scrollspy',
      bootstrapTab: '../../bower_components/bootstrap/js/tab',
      bootstrapTooltip: '../../bower_components/bootstrap/js/tooltip',
      bootstrapTransition: '../../bower_components/bootstrap/js/transition',

      // # Select2 multiselect widget
      select2: '../../bower_components/select2/select2',

      // # CoffeeScript
      cs: '../../bower_components/require-cs/cs',
      'coffee-script': '../../bower_components/coffeescript/extras/coffee-script'
    },

    // # Packages
    packages: [{
      name: 'css',
      location: '../../bower_components/require-css',
      main: 'css'
    }, {
      name: 'less',
      location: '../../bower_components/require-less',
      main: 'less'
    }],

    // # Shims
    shim: {
      // ## Aloha
      aloha: {
        // To disable MathJax comment out the `mathjax` entry in `deps` below.
        deps: ['jquery', 'mathjax', 'cs!configs/aloha', 'bootstrapModal', 'bootstrapPopover'],
        exports: 'Aloha',
        init: function () {
          return window.Aloha;
        }
      },

      // ## MathJax
      mathjax: {
        exports: 'MathJax',
        init: function () {
          // This config is copied from
          // `../../bower_components/aloha-editor/cnx/mathjax-config.coffee`
          //
          // It configures the TeX and AsciiMath inputs and the MML output
          // mostly for the Math editor.
          //
          // MathMenu and Zoom may not be needed but the `noErrors` is useful
          // for previewing as you type.
          window.MathJax.Hub.Config({
            jax: [
              'input/MathML',
              'input/TeX',
              'input/AsciiMath',
              'output/NativeMML',
              'output/HTML-CSS'
            ],
            extensions: [
              'asciimath2jax.js',
              'tex2jax.js',
              'mml2jax.js',
              'MathMenu.js',
              'MathZoom.js'
            ],
            tex2jax: {
              inlineMath: [
                ['[TEX_START]', '[TEX_END]'],
                ['\\(', '\\)']
              ]
            },
            TeX: {
              extensions: [
                'AMSmath.js',
                'AMSsymbols.js',
                'noErrors.js',
                'noUndefined.js'
              ],
              noErrors: {
                disabled: true
              }
            },
            AsciiMath: {
              noErrors: {
                disabled: true
              }
            }
          });

          return window.MathJax;
        }
      },

      zendesk: {
        deps: ['jquery', 'css!//assets.zendesk.com/external/zenbox/v2.6/zenbox'],
        exports: 'Zendesk',
        init: function ($) {
          if (typeof Zenbox !== 'undefined') {
            window.Zenbox.init({
              dropboxID: '20186520',
              url: 'https://openstaxcnx.zendesk.com',
              tabTooltip: 'Ask Us',
              tabImageURL: 'https://p2.zdassets.com/external/zenbox/images/tab_ask_us_right.png',
              tabColor: '#78b04a',
              tabPosition: 'Right'
            });

            // UGLY HACK: Remove Zenbox iframe so it doesn't cause issues in Aloha
            var $tab = $('#zenbox_tab'),
              $overlay = $('#zenbox_overlay'),
              $close = $('#zenbox_close');

            $overlay.remove();

            $tab.click(function () {
              $overlay.insertAfter($tab);
            });

            $close.click(function () {
              $overlay.remove();
            });
            // END HACK
          }

          return window.Zenbox;
        }
      },

      // ## UI Libraries
      // # Bootstrap Plugins
      bootstrapAffix: ['jquery'],
      bootstrapAlert: ['jquery'],
      bootstrapButton: ['jquery'],
      bootstrapCarousel: ['jquery'],
      bootstrapCollapse: ['jquery', 'bootstrapTransition'],
      bootstrapDropdown: ['jquery'],
      bootstrapModal: ['jquery', 'bootstrapTransition'],
      bootstrapPopover: ['jquery', 'bootstrapTooltip'],
      bootstrapScrollspy: ['jquery'],
      bootstrapTab: ['jquery'],
      bootstrapTooltip: ['jquery'],
      bootstrapTransition: ['jquery'],

      // Select2
      select2: {
        deps: ['jquery', 'css!../../bower_components/select2/select2'],
        exports: 'Select2'
      }
    },

    less: {
      logLevel: 1,

      globalVars: {
        dependencyDir: '"/bower_components"'
      }
    },

    // Handlebars Requirejs Plugin Configuration
    // Used when loading templates `'hbs!...'`.
    hbs: {
      templateExtension: 'html',
      helperPathCallback: function (name) {
        return 'cs!helpers/handlebars/' + name;
      }
    }
  });

})();
<commit_msg>Upgrade to MathJax 2.5<commit_after>(function () {
  'use strict';

  require.config({
    // # Paths
    paths: {
      // ## Requirejs plugins
      text: '../../bower_components/requirejs-text/text',
      hbs: '../../bower_components/require-handlebars-plugin/hbs',

      // ## Core Libraries
      jquery: '../../bower_components/jquery/dist/jquery',
      underscore: '../../bower_components/lodash/lodash',
      backbone: '../../bower_components/backbone/backbone',
      'hbs/handlebars': '../../bower_components/require-handlebars-plugin/hbs/handlebars',

      // ## Backbone plugins
      'backbone-associations': '../../bower_components/backbone-associations/backbone-associations',

      // ## MathJax
      mathjax: '//cdn.mathjax.org/mathjax/2.5-latest/MathJax.js?config=MML_HTMLorMML',

      // ## Zendesk
      zendesk: '//assets.zendesk.com/external/zenbox/v2.6/zenbox',

      // Use Minified Aloha because loading files in a different requirejs context is a royal pain
      aloha: '../../bower_components/aloha-editor/target/build-profile-with-oer/rjs-output/lib/aloha',

      // ## UI Libraries and Helpers
      tooltip: 'helpers/backbone/views/attached/tooltip/tooltip',
      popover: 'helpers/backbone/views/attached/popover/popover',
      // Boostrap Plugins
      bootstrapAffix: '../../bower_components/bootstrap/js/affix',
      bootstrapAlert: '../../bower_components/bootstrap/js/alert',
      bootstrapButton: '../../bower_components/bootstrap/js/button',
      bootstrapCarousel: '../../bower_components/bootstrap/js/carousel',
      bootstrapCollapse: '../../bower_components/bootstrap/js/collapse',
      bootstrapDropdown: '../../bower_components/bootstrap/js/dropdown',
      bootstrapModal: '../../bower_components/bootstrap/js/modal',
      bootstrapPopover: '../../bower_components/bootstrap/js/popover',
      bootstrapScrollspy: '../../bower_components/bootstrap/js/scrollspy',
      bootstrapTab: '../../bower_components/bootstrap/js/tab',
      bootstrapTooltip: '../../bower_components/bootstrap/js/tooltip',
      bootstrapTransition: '../../bower_components/bootstrap/js/transition',

      // # Select2 multiselect widget
      select2: '../../bower_components/select2/select2',

      // # CoffeeScript
      cs: '../../bower_components/require-cs/cs',
      'coffee-script': '../../bower_components/coffeescript/extras/coffee-script'
    },

    // # Packages
    packages: [{
      name: 'css',
      location: '../../bower_components/require-css',
      main: 'css'
    }, {
      name: 'less',
      location: '../../bower_components/require-less',
      main: 'less'
    }],

    // # Shims
    shim: {
      // ## Aloha
      aloha: {
        // To disable MathJax comment out the `mathjax` entry in `deps` below.
        deps: ['jquery', 'mathjax', 'cs!configs/aloha', 'bootstrapModal', 'bootstrapPopover'],
        exports: 'Aloha',
        init: function () {
          return window.Aloha;
        }
      },

      // ## MathJax
      mathjax: {
        exports: 'MathJax',
        init: function () {
          // This config is copied from
          // `../../bower_components/aloha-editor/cnx/mathjax-config.coffee`
          //
          // It configures the TeX and AsciiMath inputs and the MML output
          // mostly for the Math editor.
          //
          // MathMenu and Zoom may not be needed but the `noErrors` is useful
          // for previewing as you type.
          window.MathJax.Hub.Config({
            jax: [
              'input/MathML',
              'input/TeX',
              'input/AsciiMath',
              'output/NativeMML',
              'output/HTML-CSS'
            ],
            extensions: [
              'asciimath2jax.js',
              'tex2jax.js',
              'mml2jax.js',
              'MathMenu.js',
              'MathZoom.js'
            ],
            tex2jax: {
              inlineMath: [
                ['[TEX_START]', '[TEX_END]'],
                ['\\(', '\\)']
              ]
            },
            TeX: {
              extensions: [
                'AMSmath.js',
                'AMSsymbols.js',
                'noErrors.js',
                'noUndefined.js'
              ],
              noErrors: {
                disabled: true
              }
            },
            AsciiMath: {
              noErrors: {
                disabled: true
              }
            }
          });

          return window.MathJax;
        }
      },

      zendesk: {
        deps: ['jquery', 'css!//assets.zendesk.com/external/zenbox/v2.6/zenbox'],
        exports: 'Zendesk',
        init: function ($) {
          if (typeof Zenbox !== 'undefined') {
            window.Zenbox.init({
              dropboxID: '20186520',
              url: 'https://openstaxcnx.zendesk.com',
              tabTooltip: 'Ask Us',
              tabImageURL: 'https://p2.zdassets.com/external/zenbox/images/tab_ask_us_right.png',
              tabColor: '#78b04a',
              tabPosition: 'Right'
            });

            // UGLY HACK: Remove Zenbox iframe so it doesn't cause issues in Aloha
            var $tab = $('#zenbox_tab'),
              $overlay = $('#zenbox_overlay'),
              $close = $('#zenbox_close');

            $overlay.remove();

            $tab.click(function () {
              $overlay.insertAfter($tab);
            });

            $close.click(function () {
              $overlay.remove();
            });
            // END HACK
          }

          return window.Zenbox;
        }
      },

      // ## UI Libraries
      // # Bootstrap Plugins
      bootstrapAffix: ['jquery'],
      bootstrapAlert: ['jquery'],
      bootstrapButton: ['jquery'],
      bootstrapCarousel: ['jquery'],
      bootstrapCollapse: ['jquery', 'bootstrapTransition'],
      bootstrapDropdown: ['jquery'],
      bootstrapModal: ['jquery', 'bootstrapTransition'],
      bootstrapPopover: ['jquery', 'bootstrapTooltip'],
      bootstrapScrollspy: ['jquery'],
      bootstrapTab: ['jquery'],
      bootstrapTooltip: ['jquery'],
      bootstrapTransition: ['jquery'],

      // Select2
      select2: {
        deps: ['jquery', 'css!../../bower_components/select2/select2'],
        exports: 'Select2'
      }
    },

    less: {
      logLevel: 1,

      globalVars: {
        dependencyDir: '"/bower_components"'
      }
    },

    // Handlebars Requirejs Plugin Configuration
    // Used when loading templates `'hbs!...'`.
    hbs: {
      templateExtension: 'html',
      helperPathCallback: function (name) {
        return 'cs!helpers/handlebars/' + name;
      }
    }
  });

})();
<|endoftext|>


<commit_before>import { remote, ipcRenderer } from 'electron';
import { t } from 'i18next';

import { openAboutDialog, closeAboutDialog } from './aboutDialog';
import { mountAddServerView, toggleAddServerViewVisible } from './addServerView';
import certificates from './certificates';
import dock from './dock';
import menus from './menus';
import { openScreenSharingDialog, closeScreenSharingDialog, selectScreenSharingSource } from './screenSharingDialog';
import servers from './servers';
import sidebar from './sidebar';
import tray from './tray';
import setTouchBar from './touchBar';
import { openUpdateDialog, closeUpdateDialog } from './updateDialog';
import {
	setupUpdates,
	canUpdate,
	canAutoUpdate,
	canSetAutoUpdate,
	setAutoUpdate,
	checkForUpdates,
	skipUpdateVersion,
	downloadUpdate,
} from './updates';
import webview, { mountWebViews } from './webview';
import { processDeepLink } from './deepLinks';
import { mountMainWindow, updateMainWindow, unmountMainWindow } from './mainWindow';

const { app, getCurrentWindow, shell } = remote;

const updatePreferences = () => {
	const mainWindow = getCurrentWindow();
	const showWindowOnUnreadChanged = localStorage.getItem('showWindowOnUnreadChanged') === 'true';
	const hasTrayIcon = localStorage.getItem('hideTray')
		? localStorage.getItem('hideTray') !== 'true' : process.platform !== 'linux';
	const hasMenuBar = localStorage.getItem('autohideMenu') !== 'true';
	const hasSidebar = localStorage.getItem('sidebar-closed') !== 'true';

	menus.setState({
		showTrayIcon: hasTrayIcon,
		showFullScreen: mainWindow.isFullScreen(),
		showWindowOnUnreadChanged,
		showMenuBar: hasMenuBar,
		showServerList: hasSidebar,
	});

	tray.setState({
		showIcon: hasTrayIcon,
	});

	dock.setState({
		hasTrayIcon,
	});

	sidebar.setState({
		visible: hasSidebar,
	});

	webview.setSidebarPaddingEnabled(!hasSidebar);
};

const updateServers = () => {
	const sorting = JSON.parse(localStorage.getItem('rocket.chat.sortOrder')) || [];

	menus.setState({
		servers: Object.values(servers.hosts)
			.sort(({ url: a }, { url: b }) => (sidebar ? sorting.indexOf(a) - sorting.indexOf(b) : 0))
			.map(({ title, url }) => ({ title, url })),
		currentServerUrl: servers.active,
	});

	sidebar.setState({
		hosts: servers.hosts,
		sorting,
		active: servers.active,
	});
};

const updateWindowState = () => tray.setState({ isMainWindowVisible: getCurrentWindow().isVisible() });

const destroyAll = () => {
	try {
		menus.removeAllListeners();
		tray.destroy();
		dock.destroy();
		const mainWindow = getCurrentWindow();
		mainWindow.removeListener('hide', updateWindowState);
		mainWindow.removeListener('show', updateWindowState);
	} catch (error) {
		remote.getGlobal('console').error(error);
	}
};

export default () => {
	window.addEventListener('beforeunload', destroyAll);
	window.addEventListener('focus', () => webview.focusActive());

	const handleConnectionStatus = () => {
		document.body.classList.toggle('offline', !navigator.onLine);
	};
	window.addEventListener('online', handleConnectionStatus);
	window.addEventListener('offline', handleConnectionStatus);
	handleConnectionStatus();

	const handleActivate = () => {
		remote.getCurrentWindow().show();
	};

	const handleLogin = (event, webContents, request, authInfo, callback) => {
		for (const url of Object.keys(servers.hosts)) {
			const server = servers.hosts[url];
			if (request.url.indexOf(url) === 0 && server.username) {
				callback(server.username, server.password);
				break;
			}
		}
	};

	const handleOpenUrl = (event, url) => {
		processDeepLink(url);
	};

	const handleSecondInstance = (event, argv) => {
		argv.slice(2).forEach(processDeepLink);
	};

	remote.app.on('activate', handleActivate);
	remote.app.on('login', handleLogin);
	remote.app.on('open-url', handleOpenUrl);
	remote.app.on('second-instance', handleSecondInstance);

	remote.ipcMain.handle('can-update', () => canUpdate());
	remote.ipcMain.handle('can-auto-update', () => canAutoUpdate());
	remote.ipcMain.handle('can-set-auto-update', () => canSetAutoUpdate());
	remote.ipcMain.on('set-auto-update', (_, canAutoUpdate) => setAutoUpdate(canAutoUpdate));
	remote.ipcMain.on('check-for-updates', (event, ...args) => checkForUpdates(event, ...args));
	remote.ipcMain.on('skip-update-version', (_, ...args) => skipUpdateVersion(...args));
	remote.ipcMain.on('remind-update-later', () => {});
	remote.ipcMain.on('download-update', () => downloadUpdate());
	remote.ipcMain.on('open-about-dialog', (_, ...args) => openAboutDialog(...args));
	remote.ipcMain.on('close-about-dialog', (_, ...args) => closeAboutDialog(...args));
	remote.ipcMain.on('open-screen-sharing-dialog', (_, ...args) => openScreenSharingDialog(...args));
	remote.ipcMain.on('close-screen-sharing-dialog', (_, ...args) => closeScreenSharingDialog(...args));
	remote.ipcMain.on('select-screen-sharing-source', (_, ...args) => selectScreenSharingSource(...args));
	remote.ipcMain.on('open-update-dialog', (_, ...args) => openUpdateDialog(...args));
	remote.ipcMain.on('close-update-dialog', (_, ...args) => closeUpdateDialog(...args));

	window.addEventListener('unload', () => {
		remote.app.removeListener('activate', handleActivate);
		remote.app.removeListener('login', handleLogin);
		remote.app.removeListener('open-url', handleOpenUrl);
		remote.app.removeListener('second-instance', handleSecondInstance);

		remote.ipcMain.removeHandler('can-update');
		remote.ipcMain.removeHandler('can-auto-update');
		remote.ipcMain.removeHandler('can-set-auto-update');
		remote.ipcMain.removeAllListeners('set-auto-update');
		remote.ipcMain.removeAllListeners('check-for-updates');
		remote.ipcMain.removeAllListeners('skip-update-version');
		remote.ipcMain.removeAllListeners('remind-update-later');
		remote.ipcMain.removeAllListeners('download-update');
		remote.ipcMain.removeAllListeners('open-about-dialog');
		remote.ipcMain.removeAllListeners('close-about-dialog');
		remote.ipcMain.removeAllListeners('open-screen-sharing-dialog');
		remote.ipcMain.removeAllListeners('close-screen-sharing-dialog');
		remote.ipcMain.removeAllListeners('select-screen-sharing-source');
		remote.ipcMain.removeAllListeners('open-update-dialog');
		remote.ipcMain.removeAllListeners('close-update-dialog');

		unmountMainWindow();
	});

	menus.on('quit', () => app.quit());
	menus.on('about', () => ipcRenderer.send('open-about-dialog'));
	menus.on('open-url', (url) => shell.openExternal(url));

	menus.on('add-new-server', () => {
		getCurrentWindow().show();
		servers.clearActive();
		toggleAddServerViewVisible(true);
	});

	menus.on('select-server', ({ url }) => {
		getCurrentWindow().show();
		servers.setActive(url);
	});

	menus.on('reload-server', ({ ignoringCache = false, clearCertificates = false } = {}) => {
		if (clearCertificates) {
			certificates.clear();
		}

		const activeWebview = webview.getActive();
		if (!activeWebview) {
			return;
		}

		if (ignoringCache) {
			activeWebview.reloadIgnoringCache();
			return;
		}

		activeWebview.reload();
	});

	menus.on('open-devtools-for-server', () => {
		const activeWebview = webview.getActive();
		if (activeWebview) {
			activeWebview.openDevTools();
		}
	});

	menus.on('go-back', () => webview.goBack());
	menus.on('go-forward', () => webview.goForward());

	menus.on('reload-app', () => getCurrentWindow().reload());

	menus.on('toggle-devtools', () => getCurrentWindow().toggleDevTools());

	menus.on('reset-app-data', async () => {
		const { response } = await remote.dialog.showMessageBox({
			type: 'question',
			buttons: [t('dialog.resetAppData.yes'), t('dialog.resetAppData.cancel')],
			defaultId: 1,
			title: t('dialog.resetAppData.title'),
			message: t('dialog.resetAppData.message'),
		});

		if (response !== 0) {
			return;
		}

		remote.app.relaunch({ args: [remote.process.argv[1], '--reset-app-data'] });
		remote.app.quit();
	});

	menus.on('toggle', (property) => {
		switch (property) {
			case 'showTrayIcon': {
				const previousValue = localStorage.getItem('hideTray') !== 'true';
				const newValue = !previousValue;
				localStorage.setItem('hideTray', JSON.stringify(!newValue));
				break;
			}

			case 'showFullScreen': {
				const mainWindow = getCurrentWindow();
				mainWindow.setFullScreen(!mainWindow.isFullScreen());
				break;
			}

			case 'showWindowOnUnreadChanged': {
				const previousValue = localStorage.getItem('showWindowOnUnreadChanged') === 'true';
				const newValue = !previousValue;
				localStorage.setItem('showWindowOnUnreadChanged', JSON.stringify(newValue));
				break;
			}

			case 'showMenuBar': {
				const previousValue = localStorage.getItem('autohideMenu') !== 'true';
				const newValue = !previousValue;
				localStorage.setItem('autohideMenu', JSON.stringify(!newValue));
				break;
			}

			case 'showServerList': {
				const previousValue = localStorage.getItem('sidebar-closed') !== 'true';
				const newValue = !previousValue;
				localStorage.setItem('sidebar-closed', JSON.stringify(!newValue));
				break;
			}
		}

		updatePreferences();
	});

	servers.on('loaded', () => {
		document.querySelector('.app-page').classList.remove('app-page--loading');
		updateServers();
	});

	servers.on('host-added', (hostUrl) => {
		webview.add(servers.get(hostUrl));
		updateServers();
	});

	servers.on('host-removed', (hostUrl) => {
		webview.remove(hostUrl);
		servers.clearActive();
		toggleAddServerViewVisible(true);
		updateServers();
	});

	servers.on('active-setted', (hostUrl) => {
		webview.setActive(hostUrl);
		toggleAddServerViewVisible(false);
		updateServers();
	});

	servers.on('active-cleared', (hostUrl) => {
		webview.deactiveAll(hostUrl);
		toggleAddServerViewVisible(true);
		updateServers();
	});

	servers.on('title-setted', () => {
		updateServers();
	});

	sidebar.on('select-server', (hostUrl) => {
		servers.setActive(hostUrl);
	});

	sidebar.on('reload-server', (hostUrl) => {
		webview.getByUrl(hostUrl).reload();
	});

	sidebar.on('remove-server', (hostUrl) => {
		servers.removeHost(hostUrl);
	});

	sidebar.on('open-devtools-for-server', (hostUrl) => {
		webview.getByUrl(hostUrl).openDevTools();
	});

	sidebar.on('add-server', () => {
		servers.clearActive();
		toggleAddServerViewVisible(true);
	});

	sidebar.on('servers-sorted', (sorting) => {
		localStorage.setItem('rocket.chat.sortOrder', JSON.stringify(sorting));
		updateServers();
	});

	getCurrentWindow().on('hide', updateWindowState);
	getCurrentWindow().on('show', updateWindowState);

	tray.on('created', () => updateMainWindow({ hideOnClose: true }));
	tray.on('destroyed', () => updateMainWindow({ hideOnClose: false }));
	tray.on('set-main-window-visibility', (visible) =>
		(visible ? getCurrentWindow().show() : getCurrentWindow().hide()));
	tray.on('quit', () => app.quit());

	webview.on('ipc-message-unread-changed', (hostUrl, [badge]) => {
		if (typeof badge === 'number' && localStorage.getItem('showWindowOnUnreadChanged') === 'true') {
			const mainWindow = remote.getCurrentWindow();
			if (!mainWindow.isFocused()) {
				mainWindow.once('focus', () => mainWindow.flashFrame(false));
				mainWindow.showInactive();
				mainWindow.flashFrame(true);
			}
		}

		sidebar.setState({
			badges: {
				...sidebar.state.badges,
				[hostUrl]: badge || null,
			},
		});

		const mentionCount = Object.values(sidebar.state.badges)
			.filter((badge) => Number.isInteger(badge))
			.reduce((sum, count) => sum + count, 0);
		const globalBadge = mentionCount
			|| (Object.values(sidebar.state.badges).some((badge) => !!badge) && '•')
			|| null;

		tray.setState({ badge: globalBadge });
		dock.setState({ badge: globalBadge });
	});

	webview.on('ipc-message-title-changed', (hostUrl, [title]) => {
		servers.setHostTitle(hostUrl, title);
	});

	webview.on('ipc-message-focus', (hostUrl) => {
		servers.setActive(hostUrl);
	});

	webview.on('ipc-message-sidebar-style', (hostUrl, [style]) => {
		sidebar.setState({
			styles: {
				...sidebar.state.styles,
				[hostUrl]: style || null,
			},
		});
	});

	webview.on('dom-ready', () => {
		const hasSidebar = localStorage.getItem('sidebar-closed') !== 'true';
		sidebar.setState({
			visible: hasSidebar,
		});
		webview.setSidebarPaddingEnabled(!hasSidebar);
	});

	webview.on('did-navigate-in-page', (hostUrl, event) => {
		if (event.url.includes(hostUrl)) {
			servers.hosts[hostUrl].lastPath = event.url;
		}
	});

	if (process.platform === 'darwin') {
		setTouchBar();
	}

	setupUpdates();
	servers.initialize();
	certificates.initialize();

	mountAddServerView();
	sidebar.mount();

	mountWebViews();
	servers.forEach(::webview.add);

	servers.restoreActive();

	mountMainWindow();

	updatePreferences();
	updateServers();
	updateWindowState();

	remote.process.argv.slice(2).forEach(processDeepLink);
};
<commit_msg>[IMPROVE] Focus main window on second app instance event (#1418)<commit_after>import { remote, ipcRenderer } from 'electron';
import { t } from 'i18next';

import { openAboutDialog, closeAboutDialog } from './aboutDialog';
import { mountAddServerView, toggleAddServerViewVisible } from './addServerView';
import certificates from './certificates';
import dock from './dock';
import menus from './menus';
import { openScreenSharingDialog, closeScreenSharingDialog, selectScreenSharingSource } from './screenSharingDialog';
import servers from './servers';
import sidebar from './sidebar';
import tray from './tray';
import setTouchBar from './touchBar';
import { openUpdateDialog, closeUpdateDialog } from './updateDialog';
import {
	setupUpdates,
	canUpdate,
	canAutoUpdate,
	canSetAutoUpdate,
	setAutoUpdate,
	checkForUpdates,
	skipUpdateVersion,
	downloadUpdate,
} from './updates';
import webview, { mountWebViews } from './webview';
import { processDeepLink } from './deepLinks';
import { mountMainWindow, updateMainWindow, unmountMainWindow } from './mainWindow';

const { app, getCurrentWindow, shell } = remote;

const updatePreferences = () => {
	const mainWindow = getCurrentWindow();
	const showWindowOnUnreadChanged = localStorage.getItem('showWindowOnUnreadChanged') === 'true';
	const hasTrayIcon = localStorage.getItem('hideTray')
		? localStorage.getItem('hideTray') !== 'true' : process.platform !== 'linux';
	const hasMenuBar = localStorage.getItem('autohideMenu') !== 'true';
	const hasSidebar = localStorage.getItem('sidebar-closed') !== 'true';

	menus.setState({
		showTrayIcon: hasTrayIcon,
		showFullScreen: mainWindow.isFullScreen(),
		showWindowOnUnreadChanged,
		showMenuBar: hasMenuBar,
		showServerList: hasSidebar,
	});

	tray.setState({
		showIcon: hasTrayIcon,
	});

	dock.setState({
		hasTrayIcon,
	});

	sidebar.setState({
		visible: hasSidebar,
	});

	webview.setSidebarPaddingEnabled(!hasSidebar);
};

const updateServers = () => {
	const sorting = JSON.parse(localStorage.getItem('rocket.chat.sortOrder')) || [];

	menus.setState({
		servers: Object.values(servers.hosts)
			.sort(({ url: a }, { url: b }) => (sidebar ? sorting.indexOf(a) - sorting.indexOf(b) : 0))
			.map(({ title, url }) => ({ title, url })),
		currentServerUrl: servers.active,
	});

	sidebar.setState({
		hosts: servers.hosts,
		sorting,
		active: servers.active,
	});
};

const updateWindowState = () => tray.setState({ isMainWindowVisible: getCurrentWindow().isVisible() });

const destroyAll = () => {
	try {
		menus.removeAllListeners();
		tray.destroy();
		dock.destroy();
		const mainWindow = getCurrentWindow();
		mainWindow.removeListener('hide', updateWindowState);
		mainWindow.removeListener('show', updateWindowState);
	} catch (error) {
		remote.getGlobal('console').error(error);
	}
};

export default () => {
	window.addEventListener('beforeunload', destroyAll);
	window.addEventListener('focus', () => webview.focusActive());

	const handleConnectionStatus = () => {
		document.body.classList.toggle('offline', !navigator.onLine);
	};
	window.addEventListener('online', handleConnectionStatus);
	window.addEventListener('offline', handleConnectionStatus);
	handleConnectionStatus();

	const handleActivate = () => {
		remote.getCurrentWindow().show();
	};

	const handleLogin = (event, webContents, request, authInfo, callback) => {
		for (const url of Object.keys(servers.hosts)) {
			const server = servers.hosts[url];
			if (request.url.indexOf(url) === 0 && server.username) {
				callback(server.username, server.password);
				break;
			}
		}
	};

	const handleOpenUrl = (event, url) => {
		processDeepLink(url);
	};

	const handleSecondInstance = (event, argv) => {
		ipcRenderer.send('main-window/focus');
		argv.slice(2).forEach(processDeepLink);
	};

	remote.app.on('activate', handleActivate);
	remote.app.on('login', handleLogin);
	remote.app.on('open-url', handleOpenUrl);
	remote.app.on('second-instance', handleSecondInstance);

	remote.ipcMain.handle('can-update', () => canUpdate());
	remote.ipcMain.handle('can-auto-update', () => canAutoUpdate());
	remote.ipcMain.handle('can-set-auto-update', () => canSetAutoUpdate());
	remote.ipcMain.on('set-auto-update', (_, canAutoUpdate) => setAutoUpdate(canAutoUpdate));
	remote.ipcMain.on('check-for-updates', (event, ...args) => checkForUpdates(event, ...args));
	remote.ipcMain.on('skip-update-version', (_, ...args) => skipUpdateVersion(...args));
	remote.ipcMain.on('remind-update-later', () => {});
	remote.ipcMain.on('download-update', () => downloadUpdate());
	remote.ipcMain.on('open-about-dialog', (_, ...args) => openAboutDialog(...args));
	remote.ipcMain.on('close-about-dialog', (_, ...args) => closeAboutDialog(...args));
	remote.ipcMain.on('open-screen-sharing-dialog', (_, ...args) => openScreenSharingDialog(...args));
	remote.ipcMain.on('close-screen-sharing-dialog', (_, ...args) => closeScreenSharingDialog(...args));
	remote.ipcMain.on('select-screen-sharing-source', (_, ...args) => selectScreenSharingSource(...args));
	remote.ipcMain.on('open-update-dialog', (_, ...args) => openUpdateDialog(...args));
	remote.ipcMain.on('close-update-dialog', (_, ...args) => closeUpdateDialog(...args));

	window.addEventListener('unload', () => {
		remote.app.removeListener('activate', handleActivate);
		remote.app.removeListener('login', handleLogin);
		remote.app.removeListener('open-url', handleOpenUrl);
		remote.app.removeListener('second-instance', handleSecondInstance);

		remote.ipcMain.removeHandler('can-update');
		remote.ipcMain.removeHandler('can-auto-update');
		remote.ipcMain.removeHandler('can-set-auto-update');
		remote.ipcMain.removeAllListeners('set-auto-update');
		remote.ipcMain.removeAllListeners('check-for-updates');
		remote.ipcMain.removeAllListeners('skip-update-version');
		remote.ipcMain.removeAllListeners('remind-update-later');
		remote.ipcMain.removeAllListeners('download-update');
		remote.ipcMain.removeAllListeners('open-about-dialog');
		remote.ipcMain.removeAllListeners('close-about-dialog');
		remote.ipcMain.removeAllListeners('open-screen-sharing-dialog');
		remote.ipcMain.removeAllListeners('close-screen-sharing-dialog');
		remote.ipcMain.removeAllListeners('select-screen-sharing-source');
		remote.ipcMain.removeAllListeners('open-update-dialog');
		remote.ipcMain.removeAllListeners('close-update-dialog');

		unmountMainWindow();
	});

	menus.on('quit', () => app.quit());
	menus.on('about', () => ipcRenderer.send('open-about-dialog'));
	menus.on('open-url', (url) => shell.openExternal(url));

	menus.on('add-new-server', () => {
		getCurrentWindow().show();
		servers.clearActive();
		toggleAddServerViewVisible(true);
	});

	menus.on('select-server', ({ url }) => {
		getCurrentWindow().show();
		servers.setActive(url);
	});

	menus.on('reload-server', ({ ignoringCache = false, clearCertificates = false } = {}) => {
		if (clearCertificates) {
			certificates.clear();
		}

		const activeWebview = webview.getActive();
		if (!activeWebview) {
			return;
		}

		if (ignoringCache) {
			activeWebview.reloadIgnoringCache();
			return;
		}

		activeWebview.reload();
	});

	menus.on('open-devtools-for-server', () => {
		const activeWebview = webview.getActive();
		if (activeWebview) {
			activeWebview.openDevTools();
		}
	});

	menus.on('go-back', () => webview.goBack());
	menus.on('go-forward', () => webview.goForward());

	menus.on('reload-app', () => getCurrentWindow().reload());

	menus.on('toggle-devtools', () => getCurrentWindow().toggleDevTools());

	menus.on('reset-app-data', async () => {
		const { response } = await remote.dialog.showMessageBox({
			type: 'question',
			buttons: [t('dialog.resetAppData.yes'), t('dialog.resetAppData.cancel')],
			defaultId: 1,
			title: t('dialog.resetAppData.title'),
			message: t('dialog.resetAppData.message'),
		});

		if (response !== 0) {
			return;
		}

		remote.app.relaunch({ args: [remote.process.argv[1], '--reset-app-data'] });
		remote.app.quit();
	});

	menus.on('toggle', (property) => {
		switch (property) {
			case 'showTrayIcon': {
				const previousValue = localStorage.getItem('hideTray') !== 'true';
				const newValue = !previousValue;
				localStorage.setItem('hideTray', JSON.stringify(!newValue));
				break;
			}

			case 'showFullScreen': {
				const mainWindow = getCurrentWindow();
				mainWindow.setFullScreen(!mainWindow.isFullScreen());
				break;
			}

			case 'showWindowOnUnreadChanged': {
				const previousValue = localStorage.getItem('showWindowOnUnreadChanged') === 'true';
				const newValue = !previousValue;
				localStorage.setItem('showWindowOnUnreadChanged', JSON.stringify(newValue));
				break;
			}

			case 'showMenuBar': {
				const previousValue = localStorage.getItem('autohideMenu') !== 'true';
				const newValue = !previousValue;
				localStorage.setItem('autohideMenu', JSON.stringify(!newValue));
				break;
			}

			case 'showServerList': {
				const previousValue = localStorage.getItem('sidebar-closed') !== 'true';
				const newValue = !previousValue;
				localStorage.setItem('sidebar-closed', JSON.stringify(!newValue));
				break;
			}
		}

		updatePreferences();
	});

	servers.on('loaded', () => {
		document.querySelector('.app-page').classList.remove('app-page--loading');
		updateServers();
	});

	servers.on('host-added', (hostUrl) => {
		webview.add(servers.get(hostUrl));
		updateServers();
	});

	servers.on('host-removed', (hostUrl) => {
		webview.remove(hostUrl);
		servers.clearActive();
		toggleAddServerViewVisible(true);
		updateServers();
	});

	servers.on('active-setted', (hostUrl) => {
		webview.setActive(hostUrl);
		toggleAddServerViewVisible(false);
		updateServers();
	});

	servers.on('active-cleared', (hostUrl) => {
		webview.deactiveAll(hostUrl);
		toggleAddServerViewVisible(true);
		updateServers();
	});

	servers.on('title-setted', () => {
		updateServers();
	});

	sidebar.on('select-server', (hostUrl) => {
		servers.setActive(hostUrl);
	});

	sidebar.on('reload-server', (hostUrl) => {
		webview.getByUrl(hostUrl).reload();
	});

	sidebar.on('remove-server', (hostUrl) => {
		servers.removeHost(hostUrl);
	});

	sidebar.on('open-devtools-for-server', (hostUrl) => {
		webview.getByUrl(hostUrl).openDevTools();
	});

	sidebar.on('add-server', () => {
		servers.clearActive();
		toggleAddServerViewVisible(true);
	});

	sidebar.on('servers-sorted', (sorting) => {
		localStorage.setItem('rocket.chat.sortOrder', JSON.stringify(sorting));
		updateServers();
	});

	getCurrentWindow().on('hide', updateWindowState);
	getCurrentWindow().on('show', updateWindowState);

	tray.on('created', () => updateMainWindow({ hideOnClose: true }));
	tray.on('destroyed', () => updateMainWindow({ hideOnClose: false }));
	tray.on('set-main-window-visibility', (visible) =>
		(visible ? getCurrentWindow().show() : getCurrentWindow().hide()));
	tray.on('quit', () => app.quit());

	webview.on('ipc-message-unread-changed', (hostUrl, [badge]) => {
		if (typeof badge === 'number' && localStorage.getItem('showWindowOnUnreadChanged') === 'true') {
			const mainWindow = remote.getCurrentWindow();
			if (!mainWindow.isFocused()) {
				mainWindow.once('focus', () => mainWindow.flashFrame(false));
				mainWindow.showInactive();
				mainWindow.flashFrame(true);
			}
		}

		sidebar.setState({
			badges: {
				...sidebar.state.badges,
				[hostUrl]: badge || null,
			},
		});

		const mentionCount = Object.values(sidebar.state.badges)
			.filter((badge) => Number.isInteger(badge))
			.reduce((sum, count) => sum + count, 0);
		const globalBadge = mentionCount
			|| (Object.values(sidebar.state.badges).some((badge) => !!badge) && '•')
			|| null;

		tray.setState({ badge: globalBadge });
		dock.setState({ badge: globalBadge });
	});

	webview.on('ipc-message-title-changed', (hostUrl, [title]) => {
		servers.setHostTitle(hostUrl, title);
	});

	webview.on('ipc-message-focus', (hostUrl) => {
		servers.setActive(hostUrl);
	});

	webview.on('ipc-message-sidebar-style', (hostUrl, [style]) => {
		sidebar.setState({
			styles: {
				...sidebar.state.styles,
				[hostUrl]: style || null,
			},
		});
	});

	webview.on('dom-ready', () => {
		const hasSidebar = localStorage.getItem('sidebar-closed') !== 'true';
		sidebar.setState({
			visible: hasSidebar,
		});
		webview.setSidebarPaddingEnabled(!hasSidebar);
	});

	webview.on('did-navigate-in-page', (hostUrl, event) => {
		if (event.url.includes(hostUrl)) {
			servers.hosts[hostUrl].lastPath = event.url;
		}
	});

	if (process.platform === 'darwin') {
		setTouchBar();
	}

	setupUpdates();
	servers.initialize();
	certificates.initialize();

	mountAddServerView();
	sidebar.mount();

	mountWebViews();
	servers.forEach(::webview.add);

	servers.restoreActive();

	mountMainWindow();

	updatePreferences();
	updateServers();
	updateWindowState();

	remote.process.argv.slice(2).forEach(processDeepLink);
};
<|endoftext|>