OpenVeo Manage AngularJS back end

API Docs for: 1.0.0-alpha.0
Show:

File: app/client/admin/js/ovManage/ManageableController.js

'use strict';

(function(app) {

  /* global interact, Ps */
  /**
   * Controls the view responsible of a list of manageables.
   */
  function ManageableController(
    $q,
    $scope,
    $rootScope,
    $filter,
    $timeout,
    $location,
    ManageFactory,
    GroupFactory,
    DeviceFactory,
    entityService,
    MANAGE_NAME,
    MANAGE_DEVICE_STATES) {

    var self = this,
      actionEl = document.querySelector('.item-detail .action-page'),
      detailEl = document.querySelector('.item-detail .detail-page'),
      historyEl = document.querySelector('.item-detail .history-page');

    self.lastDeviceSelected = null;

    /**
     * Defines an ui-state for a manageable.
     *
     * @param {Object} target The target manageable
     * @param {String} uiState The ui-state to set for the manageable
     */
    function addUiState(target, uiState) {
      var element = (angular.element(target).scope().$parent.device) ?
        angular.element(target).scope().$parent.device : angular.element(target).scope().group;

      if (!element['ui-state']) {
        element['ui-state'] = [];
      }
      element['ui-state'].push(uiState);
      $scope.$apply();
    }

    /**
     * Removes an ui-state for a manageable.
     *
     * @param {Object} target The target manageable
     * @param {String} uiState The ui-state to remove for the manageable
     */
    function removeUiState(target, uiState) {
      var element = (angular.element(target).scope().device) ?
        angular.element(target).scope().device : angular.element(target).scope().group;
      var index = element['ui-state'].indexOf(uiState);

      element['ui-state'].splice(index, 1);
      $scope.$apply();
    }

    /**
     * Handles drag move event on manageables.
     *
     * @param {Event} event the user Event related to the dragEvent
     */
    function dragMoveListener(event) {
      var element = angular.element(event.target),
        x = (parseFloat(element.attr('data-x')) || 0) + event.dx,
        y = (parseFloat(element.attr('data-y')) || 0) + event.dy;

      // Translate the element
      element.css({
        '-webkit-transform': 'translate(' + x + 'px, ' + y + 'px)',
        transform: 'translate(' + x + 'px, ' + y + 'px)'
      });

      // Update the position attributes
      element.attr('data-x', x);
      element.attr('data-y', y);
    }

    /**
     * Resets the manageable element to its initial position.
     *
     * @param {Object} target the element to reset
     */
    function resetPosition(target) {
      var element = angular.element(target);

      // Set transition duration for reset
      element.css({
        '-webkit-transition-duration': '.35s',
        'transition-duration': '.35s',
        '-webkit-transform': 'translate(0, 0)',
        transform: 'translate(0, 0)'
      });
      element.parent().css({
        'z-index': 2
      });

      // Update the position attributes
      element.attr('data-x', 0);
      element.attr('data-y', 0);
    }

    /**
     * Sets position of the target element to the dropzone element.
     *
     * @param {Object} target the dropzone element
     * @param {Object} relatedTarget target the dragged element to move
     */
    function mergePosition(target, relatedTarget) {
      var x = parseInt(target.getBoundingClientRect().left) -
          (relatedTarget.parentNode.getBoundingClientRect().left + 15),
        y = parseInt(target.getBoundingClientRect().top - relatedTarget.parentNode.getBoundingClientRect().top),
        element = angular.element(relatedTarget);

      if (target.classList.contains('group')) {
        x = parseInt(x + 41);
        y = parseInt(y - 66);
      }

      element.css({
        '-webkit-transition-duration': '.5s',
        'transition-duration': '.5s',
        '-webkit-transform': 'translate(' + x + 'px, ' + y + 'px)',
        transform: 'translate(' + x + 'px, ' + y + 'px)'
      });

      removeUiState(target, 'can-drop');
    }

    /**
     * Sets items as draggable.
     */
    function draggable() {
      interact('.draggable').draggable({
        inertia: false,

        // Define the restricted area
        restrict: {
          restriction: '#page-content-wrapper',
          endOnly: true
        },
        autoScroll: true,

        onstart: function(event) {
          var element = angular.element(event.target);

          addUiState(event.target, 'drag');

          // Set transition duration for reset
          element.css({
            '-webkit-transition-duration': '0s',
            'transition-duration': '0s'
          });
          element.parent().css({
            'z-index': 1
          });
        },

        onmove: dragMoveListener,

        onend: function(event) {
          var target = event.target;

          if (!angular.element(target).hasClass('drop-target')) {
            removeUiState(target, 'drag');
            resetPosition(target);
          }
        }
      });
    }

    /**
     * Adds a device to a group.
     *
     * @param {String} deviceId The id of the device to add to the group
     * @param {String} groupId The id of group
     * @return {Promise} Promise resolving when device is added
     */
    function addDeviceToGroup(deviceId, groupId) {
      var p = $q.defer();
      var device = DeviceFactory.getDevice(deviceId);
      var group = GroupFactory.getGroup(groupId);
      var isCollision = DeviceFactory.isGroupSchedulesCollision(device.id, group);

      if (isCollision) {
        $scope.$emit('setAlert',
                     'danger',
                     $filter('translate')('MANAGE.GROUP.ADD_DEVICE_SCHEDULES_COLLISION_ERROR', null, {
                       deviceName: $filter('translate')(device.name),
                       groupName: $filter('translate')(group.name)
                     })
        );
        p.reject();
      } else {
        ManageFactory.addDeviceToGroup(deviceId, groupId).then(function() {
          GroupFactory.addDeviceToGroup(device, groupId);
          p.resolve();
        }, function(error) {
          $scope.$emit('setAlert', 'danger', $filter('translate')('MANAGE.GROUP.ADD_DEVICE_ERROR', null, {
            code: error.code,
            name: $filter('translate')(group.name)
          }), 4000);
          p.reject(error);
        });
      }

      return p.promise;
    }

    /**
     * Sets drag drop zones.
     */
    function dragDropDevice() {
      interact('.dropzone').dropzone({

        // only accept elements matching this CSS selector
        accept: '.draggable',

        // Require a 25% element overlap for a drop to be possible
        overlap: 0.25,

        ondragenter: function(event) {
          addUiState(event.target, 'can-drop');
          addUiState(event.relatedTarget, 'drop-target');
        },
        ondragleave: function(event) {
          removeUiState(event.relatedTarget, 'drop-target');
          removeUiState(event.target, 'can-drop');
        },
        ondrop: function(event) {
          var relatedTarget = angular.element(event.relatedTarget),
            target = angular.element(event.target);

          mergePosition(event.target, event.relatedTarget);
          $timeout(function() {

            if (!target.hasClass('group')) {

              // No group
              // Create it
              ManageFactory.createGroup().then(function(group) {
                GroupFactory.addGroup(group);

                // Add both devices (the dragged one and the target one)
                // to the new group
                addDeviceToGroup(relatedTarget.attr('data-id'), group.id);
                addDeviceToGroup(target.attr('data-id'), group.id);

              }, function(error) {
                $scope.$emit('setAlert', 'danger', $filter('translate')('MANAGE.GROUP.CREATE_ERROR', null, {
                  code: error.code
                }), 4000);
                resetPosition(relatedTarget);
                resetPosition(target);
              });

            } else {

              // Add dragged device to the target group
              addDeviceToGroup(relatedTarget.attr('data-id'), target.attr('data-id')).catch(function() {
                resetPosition(relatedTarget);
              });

            }

            $scope.$apply();
          }, 350);
        }
      });
    }

    /**
     * Initializes the scrollBars for device detail window.
     */
    function initScrollbar() {

      // Recreate elements to avoid errors
      actionEl = document.querySelector('.item-detail .action-page');
      detailEl = document.querySelector('.item-detail .detail-page');
      historyEl = document.querySelector('.item-detail .history-page');

      actionEl.setAttribute('style', 'height:' + parseInt(window.innerHeight - 100) + 'px');
      detailEl.setAttribute('style', 'height:' + parseInt(window.innerHeight - 100) + 'px');
      historyEl.setAttribute('style', 'height:' + parseInt(window.innerHeight - 100) + 'px');

      Ps.initialize(actionEl);
      Ps.initialize(detailEl);
      Ps.initialize(historyEl);
    }

    /**
     * Updates the scrollbars on device detail change.
     */
    function updateScrollbar() {
      actionEl.scrollTop = 0;
      detailEl.scrollTop = 0;
      historyEl.scrollTop = 0;

      Ps.update(actionEl);
      Ps.update(detailEl);
      Ps.update(historyEl);
    }

    /**
     * Destroys scrollbars when device detail is closed.
     */
    function destroyScrollbar() {
      Ps.destroy(actionEl);
      Ps.destroy(detailEl);
      Ps.destroy(historyEl);
    }

    /**
     * Sets a click event listener on a manageable.
     */
    function setManageableClickListener() {

      // Avoid to fire the same event multiple times
      var events = interact('.device.accepted > .well, .device-group > .group')._iEvents;
      if (Object.keys(events).length && events.tap) {
        delete events.tap;
      }

      interact('.device.accepted > .well, .device-group > .group').on('tap', function(event) {
        if (event.double) {
          return;
        }

        var manageableId = event.currentTarget.getAttribute('data-id'),
          currentTarget = angular.element(event.currentTarget);

        if (!$scope.manage.openedItem) {

          // No manageable loaded in the detail panel yet

          // Set the selected manageable
          $rootScope.$broadcast('manageable.load', manageableId, currentTarget.hasClass('group'));

          $scope.manage.openedItem = manageableId;
          $scope.manage.showDetail = true;
          $scope.organizeLayout($scope.manage.showDetail);

          initScrollbar();
        } else if ($scope.manage.openedItem == manageableId) {

          // Manageable to load in the panel is the one already loaded
          // Close the panel
          $rootScope.$broadcast('manageable.closeDetails');

        } else {

          // Manageable to load in the details panel is a different manageable
          // Display new manageable details
          // And activate first tab

          $scope.manage.openedItem = manageableId;
          $scope.manage.showDetail = false;
          $scope.setActivePage(0);

          $rootScope.$broadcast('manageable.load', manageableId, currentTarget.hasClass('group'));
          $scope.manage.showDetail = true;
          updateScrollbar();
        }

        $scope.$apply();
      });
    }

    /**
     * Sets a double click event listener on a manageable.
     * Go to the group detail page on double click.
     */
    function setManageableDbClickListener() {

      // Avoid to fire the same event multiple times
      var events = interact('.device-group > .group')._iEvents;
      if (Object.keys(events).length && events.doubletap) {
        delete events.doubletap;
      }

      interact('.device-group > .group').on('doubletap', function(event) {
        var groupId = event.currentTarget.getAttribute('data-id');

        $rootScope.$broadcast('manageable.load', groupId, true);
        $scope.manage.showDetail = false;
        $location.path('manage/group-detail/' + groupId);
        $scope.manage.absUrl = $location.absUrl();
        destroyScrollbar();

        $scope.$apply();
      });
    }

    /**
     * Adds pending/refused device to the accepted list of devices.
     *
     * @method addToAcceptedDevices
     * @param {Object} device The device to accept
     */
    self.addToAcceptedDevices = function(device) {
      ManageFactory.updateDeviceState(device.id, MANAGE_DEVICE_STATES.ACCEPTED).then(function() {
        ManageFactory.askForDevicesSettings([device.id]);
      }, function(error) {
        $scope.$emit('setAlert', 'danger', $filter('translate')('MANAGE.DEVICE.ADD_ACCEPTED_ERROR', null, {
          code: error.code
        }), 4000);
      });
    };

    /**
     * Adds the new device to the refused list of devices.
     *
     * @method addToRefusedDevices
     * @param {Object} device The device object
     */
    self.addToRefusedDevices = function(device) {
      ManageFactory.updateDeviceState(device.id, MANAGE_DEVICE_STATES.REFUSED).catch(function(error) {
        $scope.$emit('setAlert', 'danger', $filter('translate')('MANAGE.DEVICE.ADD_REFUSED_ERROR', null, {
          code: error.code
        }), 4000);
      });
    };

    // Manage drag and drop events
    draggable();
    dragDropDevice();

    // Manage click events
    setManageableClickListener();
    setManageableDbClickListener();

  }

  app.controller('ManageManageableController', ManageableController);
  ManageableController.$inject = [
    '$q',
    '$scope',
    '$rootScope',
    '$filter',
    '$timeout',
    '$location',
    'ManageFactory',
    'ManageGroupFactory',
    'ManageDeviceFactory',
    'entityService',
    'MANAGE_NAME',
    'MANAGE_DEVICE_STATES'
  ];

})(angular.module('ov.manage'));