<commit_before>/*
 * angular-ui-tab-scroll
 * https://github.com/VersifitTechnologies/angular-ui-tab-scroll
 *
 * Version: 2.2.3
 * License: MIT
 */

angular.module('ui.tab.scroll', [])
    .provider('scrollableTabsetConfig', function(){

          //the default options
          var defaultConfig = {
            showDropDown: true,
            showTooltips: true,
            tooltipLeftPlacement: 'right',
            tooltipRightPlacement: 'left',
            scrollBy: '50',
            autoRecalculate: false
          };

          var config = angular.extend({}, defaultConfig);

          return {
            setShowDropDown : function(value){
              config.showDropDown = (value == true);
            },
            setShowTooltips : function(value){
              config.showTooltips = (value == true);
            },
            setTooltipLeftPlacement : function(value){
              config.tooltipLeftPlacement = value;
            },
            setTooltipRightPlacement : function(value){
              config.tooltipRightPlacement = value;
            },
            setScrollBy : function(value){
              config.scrollBy = value;
            },
            setAutoRecalculate : function(value){
              config.autoRecalculate = (value == true);
            },
            $get: function(){
              return {
                showDropDown: config.showDropDown,
                showTooltips: config.showTooltips,
                tooltipLeftPlacement: config.tooltipLeftPlacement,
                tooltipRightPlacement: config.tooltipRightPlacement,
                scrollBy: config.scrollBy,
                autoRecalculate: config.autoRecalculate
              };
            }
          };
        }
    )
    .directive('scrollableTabset', [
      'scrollableTabsetConfig', '$window', '$interval', '$timeout','$sce',
      function(scrollableTabsetConfig, $window, $interval, $timeout, $sce) {

        return {
          restrict: 'AE',
          transclude: true,

          scope: {
            showDropDown: '@',
            showTooltips: '@',
            tooltipLeftPlacement: '@',
            tooltipRightPlacement: '@',
            scrollBy: '@',
            autoRecalculate: '@',
            api: '=?'
          },

          template: [
            '
',
              '<button type="button" ng-mousedown="scrollButtonDown(\'left\', $event)" ng-mouseup="scrollButtonUp()" ng-hide="hideButtons"' +
              ' ng-disabled="disableLeft" class="btn nav-button left-nav-button"' +
              ' tooltip-placement="{{tooltipLeftDirection}}" uib-tooltip-html="tooltipLeftHtml"></button>',
              '

',
              '<button type="button" ng-mousedown="scrollButtonDown(\'right\', $event)" ng-mouseup="scrollButtonUp()" ng-hide="hideButtons"' +
              ' ng-disabled="disableRight" class="btn nav-button right-nav-button"' +
              ' tooltip-placement="{{tooltipRightDirection}}" uib-tooltip-html="tooltipRightHtml"></button>',
              '
',
                '<button type="button" class="btn" uib-dropdown-toggle></button>',
                '
',
                  '
',
                    '{{tab.tabScrollTitle}}',
                  '
',
                '
',
              '
',
            '
'
          ].join(''),

          link: function($scope, $el) {

            $scope.dropdownTabs = [];
            $scope.hideButtons = true;
            $scope.hideDropDown = true;
            $scope.tooltipRightHtml = '';
            $scope.tooltipLeftHtml = '';
            $scope.disableLeft = true;
            $scope.disableRight = true;
            $scope.tooltipLeftDirection = $scope.tooltipLeftPlacement ? $scope.tooltipLeftPlacement : scrollableTabsetConfig.tooltipLeftPlacement;
            $scope.tooltipRightDirection =  $scope.tooltipRightPlacement ? $scope.tooltipRightPlacement : scrollableTabsetConfig.tooltipRightPlacement;
            $scope.mouseDownInterval = null;
            $scope.isHolding = false;
            $scope.winResizeTimeout;
            $scope.userShowDropDown = $scope.showDropDown ? $scope.showDropDown === 'true' : scrollableTabsetConfig.showDropDown;
            $scope.userShowTooltips = $scope.showTooltips ? $scope.showTooltips === 'true' : scrollableTabsetConfig.showTooltips == true;
            $scope.scrollByPixels = parseInt($scope.scrollBy ? $scope.scrollBy : scrollableTabsetConfig.scrollBy);

            $scope.api = {
              doRecalculate: function(){
                $timeout(function(){$scope.reCalcAll()});
              },

              scrollTabIntoView: function(arg){
                $timeout(function(){$scope.scrollTabIntoView(arg)});
              }
            };

            $scope.scrollTo = function(element, change, duration, callback, isLinear) {
              var start = element.scrollLeft;
              var increment = 20;
              var position = 0;

              var animateScroll = function(elapsedTime) {
                elapsedTime += increment;
                if(isLinear === true) {
                  position = $scope.linearTween(elapsedTime, start, change, duration);
                } else {
                  position = $scope.easeInOutQuad(elapsedTime, start, change, duration);
                }
                element.scrollLeft = position;
                if (elapsedTime < duration) {
                  setTimeout(function() {
                    animateScroll(elapsedTime);
                  }, increment);
                }else{
                  callback();
                }
              };

              animateScroll(0);
            }

            $scope.linearTween = function (currentTime, start, change, duration) {
              return change * currentTime / duration + start;
            };

            $scope.easeInOutQuad = function(currentTime, start, change, duration) {
              currentTime /= duration / 2;
              if (currentTime < 1) {
                return change / 2 * currentTime * currentTime + start;
              }
              currentTime --;
              return -change / 2 * (currentTime * (currentTime - 2) - 1) + start;
            }

            $scope.onWindowResize = function() {
              // delay for a bit to avoid running lots of times.
              clearTimeout($scope.winResizeTimeout);
              $scope.winResizeTimeout = setTimeout(function(){
                $scope.reCalcAll();
                $scope.$apply();
              }, 250);
            };

            $scope.cancelMouseDownInterval = function() {
              $scope.isHolding = false;

              if($scope.mouseDownInterval) {
                $interval.cancel($scope.mouseDownInterval);
                $scope.mouseDownInterval = null;
              }
            };

            $scope.scrollButtonDown = function(direction, event) {
              event.stopPropagation();
              $scope.isHolding = true;

              var realScroll = direction === 'left' ? 0 - $scope.scrollByPixels : $scope.scrollByPixels;
              $scope.scrollTo($scope.tabContainer, realScroll, 150, function(){
                $timeout(function(){
                  $scope.reCalcSides();
                });
              }, true);

              $scope.mouseDownInterval = $interval(function() {

                if($scope.isHolding) {
                  $scope.scrollTo($scope.tabContainer, realScroll, 150, function(){
                    $timeout(function(){
                      $scope.reCalcSides();
                    });
                  }, true);

                  if(event.target.disabled) {
                    $scope.cancelMouseDownInterval();
                  }
                }
              }, 100);
            }

            $scope.scrollButtonUp = function() {
              $scope.cancelMouseDownInterval();
            }

            $scope.activateTab = function(tab) {
              if(tab.disabled)return;
              tab.active = true;
              $timeout(function () {
                $scope.scrollTabIntoView();
              });
            }

            $scope.reCalcSides = function() {
              if(!$scope.tabContainer || $scope.hideButtons)return;
              $scope.disableRight = $scope.tabContainer.scrollLeft >= $scope.tabContainer.scrollWidth - $scope.tabContainer.offsetWidth;
              $scope.disableLeft = $scope.tabContainer.scrollLeft <= 0;

              if($scope.userShowTooltips){
                $scope.reCalcTooltips();
              }
            };

            $scope.reCalcTooltips = function(){
              if(!$scope.tabContainer || $scope.hideButtons)return;
              var rightTooltips = [];
              var leftTooltips = [];

              var allTabs = $scope.tabContainer.querySelectorAll('li');
              angular.forEach(allTabs, function(tab) {

                var rightPosition = parseInt(tab.getBoundingClientRect().left + tab.getBoundingClientRect().width - $scope.tabContainer.getBoundingClientRect().left);
                var leftPosition = tab.getBoundingClientRect().left - $scope.tabContainer.getBoundingClientRect().left;
                var heading = tab.getAttribute("data-tabScrollHeading");
                var ignore = tab.getAttribute("data-tabScrollIgnore");

                if(rightPosition > $scope.tabContainer.offsetWidth && !ignore ) {
                  if(heading) {
                    rightTooltips.push(heading)
                  } else if (tab.textContent)rightTooltips.push(tab.textContent);
                }

                if (leftPosition < 0 && !ignore ) {
                  if(heading) {
                    leftTooltips.push(heading)
                  } else if (tab.textContent)leftTooltips.push(tab.textContent);
                }

              });

              var rightTooltipsHtml = rightTooltips.join('
');
              $scope.tooltipRightHtml = $sce.trustAsHtml(rightTooltipsHtml);

              var leftTooltipsHtml = leftTooltips.join('
');
              $scope.tooltipLeftHtml = $sce.trustAsHtml(leftTooltipsHtml);
            };

            $scope.scrollTabIntoView = function(arg){
              if(!$scope.tabContainer || $scope.hideButtons)return;

              var argInt = parseInt(arg);
              var tabToScroll;

              // first we find the tab element.
              if(argInt) { // scroll tab index into view
                var allTabs = $scope.tabContainer.querySelectorAll('li');
                if(allTabs.length > argInt) { // only if its really exist
                  tabToScroll = allTabs[argInt];
                }
              } else { // scroll selected tab into view
                var activeTab = $scope.tabContainer.querySelector('li.active');
                if(activeTab) {
                  tabToScroll = activeTab;
                }
              }

              // now let's scroll it into view.
              if(tabToScroll) {
                var rightPosition = parseInt(tabToScroll.getBoundingClientRect().left + tabToScroll.getBoundingClientRect().width - $scope.tabContainer.getBoundingClientRect().left);
                var leftPosition = tabToScroll.getBoundingClientRect().left - $scope.tabContainer.getBoundingClientRect().left;
                if (leftPosition < 0) {
                  var dif = leftPosition - 20;
                  $scope.scrollTo($scope.tabContainer, dif, 700, function(){
                    $timeout(function(){
                      $scope.reCalcSides();
                    });
                  });
                } else if(rightPosition > $scope.tabContainer.offsetWidth){
                  var dif = rightPosition - $scope.tabContainer.offsetWidth + 20;
                  $scope.scrollTo($scope.tabContainer, dif, 700, function(){
                    $timeout(function(){
                      $scope.reCalcSides();
                    });
                  });
                }
              }
            };

            // init is called only once!
            $scope.init = function() {
              $scope.tabContainer = $el[0].querySelector('.spacer ul.nav-tabs');
              if(!$scope.tabContainer)return;

              var autoRecalc = $scope.autoRecalculate ? $scope.autoRecalculate === 'true' : scrollableTabsetConfig.autoRecalculate;
              if(autoRecalc) {
                var tabsetElement = angular.element($el[0].querySelector('.spacer div'));
                $scope.$watchCollection(
                    function () {
                      return tabsetElement.isolateScope() ? tabsetElement.isolateScope().tabs : false;
                    },
                    function () {
                      $timeout(function () {
                        $scope.reCalcAll()
                      });
                    }
                );
              }

              $scope.reCalcAll();

              // attaching event to window resize.
              angular.element($window).on('resize', $scope.onWindowResize);
            };

            // re-calculate if the scroll buttons are needed, than call re-calculate for both buttons.
            $scope.reCalcAll = function() {
              if(!$scope.tabContainer)return;

              $scope.hideButtons = $scope.tabContainer.scrollWidth <= $scope.tabContainer.offsetWidth;
              $scope.hideDropDown = $scope.userShowDropDown ? $scope.hideButtons : true;

              if(!$scope.hideButtons) {

                if(!$scope.hideDropDown) {
                  var allTabs = $scope.tabContainer.querySelectorAll('li');
                  $scope.dropdownTabs = [];
                  angular.forEach(allTabs, function (tab) {
                    var ignore = tab.getAttribute("data-tabScrollIgnore");
                    if(!ignore){
                      var heading = tab.getAttribute("data-tabScrollHeading");
                      var tabScope = angular.element(tab).isolateScope();
                      //push new field to use as title in the drop down.
                      tabScope.tabScrollTitle = heading ? heading : tab.textContent.trim();
                      $scope.dropdownTabs.push(tabScope);
                    }
                  });
                }

                $scope.reCalcSides();
              }
            };

            // this is how we init for the first time.
            $timeout(function(){
              $scope.init();
            });

            // when scope destroyed
            $scope.$on('$destroy', function () {
              angular.element($window).off('resize', $scope.onWindowResize);
            });

          }
        };
      }]);
<commit_msg>Updated tab activation to be compatible with centralized active state of bootstrap-ui >=1.3.3<commit_after>/*
 * angular-ui-tab-scroll
 * https://github.com/VersifitTechnologies/angular-ui-tab-scroll
 *
 * Version: 2.2.3
 * License: MIT
 */

angular.module('ui.tab.scroll', [])
    .provider('scrollableTabsetConfig', function(){

          //the default options
          var defaultConfig = {
            showDropDown: true,
            showTooltips: true,
            tooltipLeftPlacement: 'right',
            tooltipRightPlacement: 'left',
            scrollBy: '50',
            autoRecalculate: false
          };

          var config = angular.extend({}, defaultConfig);

          return {
            setShowDropDown : function(value){
              config.showDropDown = (value == true);
            },
            setShowTooltips : function(value){
              config.showTooltips = (value == true);
            },
            setTooltipLeftPlacement : function(value){
              config.tooltipLeftPlacement = value;
            },
            setTooltipRightPlacement : function(value){
              config.tooltipRightPlacement = value;
            },
            setScrollBy : function(value){
              config.scrollBy = value;
            },
            setAutoRecalculate : function(value){
              config.autoRecalculate = (value == true);
            },
            $get: function(){
              return {
                showDropDown: config.showDropDown,
                showTooltips: config.showTooltips,
                tooltipLeftPlacement: config.tooltipLeftPlacement,
                tooltipRightPlacement: config.tooltipRightPlacement,
                scrollBy: config.scrollBy,
                autoRecalculate: config.autoRecalculate
              };
            }
          };
        }
    )
    .directive('scrollableTabset', [
      'scrollableTabsetConfig', '$window', '$interval', '$timeout','$sce',
      function(scrollableTabsetConfig, $window, $interval, $timeout, $sce) {

        return {
          restrict: 'AE',
          transclude: true,

          scope: {
            showDropDown: '@',
            showTooltips: '@',
            tooltipLeftPlacement: '@',
            tooltipRightPlacement: '@',
            scrollBy: '@',
            autoRecalculate: '@',
            api: '=?'
          },

          template: [
            '
',
              '<button type="button" ng-mousedown="scrollButtonDown(\'left\', $event)" ng-mouseup="scrollButtonUp()" ng-hide="hideButtons"' +
              ' ng-disabled="disableLeft" class="btn nav-button left-nav-button"' +
              ' tooltip-placement="{{tooltipLeftDirection}}" uib-tooltip-html="tooltipLeftHtml"></button>',
              '

',
              '<button type="button" ng-mousedown="scrollButtonDown(\'right\', $event)" ng-mouseup="scrollButtonUp()" ng-hide="hideButtons"' +
              ' ng-disabled="disableRight" class="btn nav-button right-nav-button"' +
              ' tooltip-placement="{{tooltipRightDirection}}" uib-tooltip-html="tooltipRightHtml"></button>',
              '
',
                '<button type="button" class="btn" uib-dropdown-toggle></button>',
                '
',
                  '
',
                    '{{tab.tabScrollTitle}}',
                  '
',
                '
',
              '
',
            '
'
          ].join(''),

          link: function($scope, $el) {

            $scope.dropdownTabs = [];
            $scope.hideButtons = true;
            $scope.hideDropDown = true;
            $scope.tooltipRightHtml = '';
            $scope.tooltipLeftHtml = '';
            $scope.disableLeft = true;
            $scope.disableRight = true;
            $scope.tooltipLeftDirection = $scope.tooltipLeftPlacement ? $scope.tooltipLeftPlacement : scrollableTabsetConfig.tooltipLeftPlacement;
            $scope.tooltipRightDirection =  $scope.tooltipRightPlacement ? $scope.tooltipRightPlacement : scrollableTabsetConfig.tooltipRightPlacement;
            $scope.mouseDownInterval = null;
            $scope.isHolding = false;
            $scope.winResizeTimeout;
            $scope.userShowDropDown = $scope.showDropDown ? $scope.showDropDown === 'true' : scrollableTabsetConfig.showDropDown;
            $scope.userShowTooltips = $scope.showTooltips ? $scope.showTooltips === 'true' : scrollableTabsetConfig.showTooltips == true;
            $scope.scrollByPixels = parseInt($scope.scrollBy ? $scope.scrollBy : scrollableTabsetConfig.scrollBy);

            $scope.api = {
              doRecalculate: function(){
                $timeout(function(){$scope.reCalcAll()});
              },

              scrollTabIntoView: function(arg){
                $timeout(function(){$scope.scrollTabIntoView(arg)});
              }
            };

            $scope.scrollTo = function(element, change, duration, callback, isLinear) {
              var start = element.scrollLeft;
              var increment = 20;
              var position = 0;

              var animateScroll = function(elapsedTime) {
                elapsedTime += increment;
                if(isLinear === true) {
                  position = $scope.linearTween(elapsedTime, start, change, duration);
                } else {
                  position = $scope.easeInOutQuad(elapsedTime, start, change, duration);
                }
                element.scrollLeft = position;
                if (elapsedTime < duration) {
                  setTimeout(function() {
                    animateScroll(elapsedTime);
                  }, increment);
                }else{
                  callback();
                }
              };

              animateScroll(0);
            }

            $scope.linearTween = function (currentTime, start, change, duration) {
              return change * currentTime / duration + start;
            };

            $scope.easeInOutQuad = function(currentTime, start, change, duration) {
              currentTime /= duration / 2;
              if (currentTime < 1) {
                return change / 2 * currentTime * currentTime + start;
              }
              currentTime --;
              return -change / 2 * (currentTime * (currentTime - 2) - 1) + start;
            }

            $scope.onWindowResize = function() {
              // delay for a bit to avoid running lots of times.
              clearTimeout($scope.winResizeTimeout);
              $scope.winResizeTimeout = setTimeout(function(){
                $scope.reCalcAll();
                $scope.$apply();
              }, 250);
            };

            $scope.cancelMouseDownInterval = function() {
              $scope.isHolding = false;

              if($scope.mouseDownInterval) {
                $interval.cancel($scope.mouseDownInterval);
                $scope.mouseDownInterval = null;
              }
            };

            $scope.scrollButtonDown = function(direction, event) {
              event.stopPropagation();
              $scope.isHolding = true;

              var realScroll = direction === 'left' ? 0 - $scope.scrollByPixels : $scope.scrollByPixels;
              $scope.scrollTo($scope.tabContainer, realScroll, 150, function(){
                $timeout(function(){
                  $scope.reCalcSides();
                });
              }, true);

              $scope.mouseDownInterval = $interval(function() {

                if($scope.isHolding) {
                  $scope.scrollTo($scope.tabContainer, realScroll, 150, function(){
                    $timeout(function(){
                      $scope.reCalcSides();
                    });
                  }, true);

                  if(event.target.disabled) {
                    $scope.cancelMouseDownInterval();
                  }
                }
              }, 100);
            }

            $scope.scrollButtonUp = function() {
              $scope.cancelMouseDownInterval();
            }

            $scope.activateTab = function(tab) {
              if(tab.disabled)return;
              tab.select();
              $timeout(function () {
                $scope.scrollTabIntoView();
              });
            }

            $scope.reCalcSides = function() {
              if(!$scope.tabContainer || $scope.hideButtons)return;
              $scope.disableRight = $scope.tabContainer.scrollLeft >= $scope.tabContainer.scrollWidth - $scope.tabContainer.offsetWidth;
              $scope.disableLeft = $scope.tabContainer.scrollLeft <= 0;

              if($scope.userShowTooltips){
                $scope.reCalcTooltips();
              }
            };

            $scope.reCalcTooltips = function(){
              if(!$scope.tabContainer || $scope.hideButtons)return;
              var rightTooltips = [];
              var leftTooltips = [];

              var allTabs = $scope.tabContainer.querySelectorAll('li');
              angular.forEach(allTabs, function(tab) {

                var rightPosition = parseInt(tab.getBoundingClientRect().left + tab.getBoundingClientRect().width - $scope.tabContainer.getBoundingClientRect().left);
                var leftPosition = tab.getBoundingClientRect().left - $scope.tabContainer.getBoundingClientRect().left;
                var heading = tab.getAttribute("data-tabScrollHeading");
                var ignore = tab.getAttribute("data-tabScrollIgnore");

                if(rightPosition > $scope.tabContainer.offsetWidth && !ignore ) {
                  if(heading) {
                    rightTooltips.push(heading)
                  } else if (tab.textContent)rightTooltips.push(tab.textContent);
                }

                if (leftPosition < 0 && !ignore ) {
                  if(heading) {
                    leftTooltips.push(heading)
                  } else if (tab.textContent)leftTooltips.push(tab.textContent);
                }

              });

              var rightTooltipsHtml = rightTooltips.join('
');
              $scope.tooltipRightHtml = $sce.trustAsHtml(rightTooltipsHtml);

              var leftTooltipsHtml = leftTooltips.join('
');
              $scope.tooltipLeftHtml = $sce.trustAsHtml(leftTooltipsHtml);
            };

            $scope.scrollTabIntoView = function(arg){
              if(!$scope.tabContainer || $scope.hideButtons)return;

              var argInt = parseInt(arg);
              var tabToScroll;

              // first we find the tab element.
              if(argInt) { // scroll tab index into view
                var allTabs = $scope.tabContainer.querySelectorAll('li');
                if(allTabs.length > argInt) { // only if its really exist
                  tabToScroll = allTabs[argInt];
                }
              } else { // scroll selected tab into view
                var activeTab = $scope.tabContainer.querySelector('li.active');
                if(activeTab) {
                  tabToScroll = activeTab;
                }
              }

              // now let's scroll it into view.
              if(tabToScroll) {
                var rightPosition = parseInt(tabToScroll.getBoundingClientRect().left + tabToScroll.getBoundingClientRect().width - $scope.tabContainer.getBoundingClientRect().left);
                var leftPosition = tabToScroll.getBoundingClientRect().left - $scope.tabContainer.getBoundingClientRect().left;
                if (leftPosition < 0) {
                  var dif = leftPosition - 20;
                  $scope.scrollTo($scope.tabContainer, dif, 700, function(){
                    $timeout(function(){
                      $scope.reCalcSides();
                    });
                  });
                } else if(rightPosition > $scope.tabContainer.offsetWidth){
                  var dif = rightPosition - $scope.tabContainer.offsetWidth + 20;
                  $scope.scrollTo($scope.tabContainer, dif, 700, function(){
                    $timeout(function(){
                      $scope.reCalcSides();
                    });
                  });
                }
              }
            };

            // init is called only once!
            $scope.init = function() {
              $scope.tabContainer = $el[0].querySelector('.spacer ul.nav-tabs');
              if(!$scope.tabContainer)return;

              var autoRecalc = $scope.autoRecalculate ? $scope.autoRecalculate === 'true' : scrollableTabsetConfig.autoRecalculate;
              if(autoRecalc) {
                var tabsetElement = angular.element($el[0].querySelector('.spacer div'));
                $scope.$watchCollection(
                    function () {
                      return tabsetElement.isolateScope() ? tabsetElement.isolateScope().tabs : false;
                    },
                    function () {
                      $timeout(function () {
                        $scope.reCalcAll()
                      });
                    }
                );
              }

              $scope.reCalcAll();

              // attaching event to window resize.
              angular.element($window).on('resize', $scope.onWindowResize);
            };

            // re-calculate if the scroll buttons are needed, than call re-calculate for both buttons.
            $scope.reCalcAll = function() {
              if(!$scope.tabContainer)return;

              $scope.hideButtons = $scope.tabContainer.scrollWidth <= $scope.tabContainer.offsetWidth;
              $scope.hideDropDown = $scope.userShowDropDown ? $scope.hideButtons : true;

              if(!$scope.hideButtons) {

                if(!$scope.hideDropDown) {
                  var allTabs = $scope.tabContainer.querySelectorAll('li');
                  $scope.dropdownTabs = [];
                  angular.forEach(allTabs, function (tab) {
                    var ignore = tab.getAttribute("data-tabScrollIgnore");
                    if(!ignore){
                      var heading = tab.getAttribute("data-tabScrollHeading");
                      var tabScope = angular.element(tab).isolateScope();
                      //push new field to use as title in the drop down.
                      tabScope.tabScrollTitle = heading ? heading : tab.textContent.trim();
                      $scope.dropdownTabs.push(tabScope);
                    }
                  });
                }

                $scope.reCalcSides();
              }
            };

            // this is how we init for the first time.
            $timeout(function(){
              $scope.init();
            });

            // when scope destroyed
            $scope.$on('$destroy', function () {
              angular.element($window).off('resize', $scope.onWindowResize);
            });

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


<commit_before>angular.module('app', [
    'app.run',
	'app.filters',
	'app.services',
	'app.components',
	'app.routes',
	'app.config',
	'app.partials'
]);

angular.module('app.run', []);
angular.module('app.routes', []);
angular.module('app.filters', []);
angular.module('app.services', []);
angular.module('app.config', []);
angular.module('app.components', [
	'ui.router', 'ngMaterial', 'angular-loading-bar',
	'restangular', 'ngStorage', 'satellizer'
]);

<commit_msg>- enabled modules<commit_after>angular.module('app', [
    'app.run',
	'app.filters',
	'app.services',
	'app.components',
	'app.routes',
	'app.config',
	'app.partials'
]);

angular.module('app.run', []);
angular.module('app.routes', []);
angular.module('app.filters', []);
angular.module('app.services', []);
angular.module('app.config', []);
angular.module('app.components', [
	'ui.router', 'md.data.table', 'ngMaterial', 'angular-loading-bar',
	'restangular', 'ngStorage', 'satellizer', 'ui.tree'
]);

<|endoftext|>


<commit_before>Seed({
		name : "Mold.Lib.Directive",
		dna : "static",
		include : [
			"Mold.Lib.Event",
			"Mold.Lib.Controller",
			"Mold.Lib.CssParser",
			"Mold.Lib.Element",
			"Mold.Lib.Info",
			"Mold.Lib.DomObserver"
		]
	},
	function(){

		var _directives = [],
			_directivesIndex = {},
			_cache = {},
			_events = {},
			doc = (Mold.isMoldJS) ? false : document,
			cssParser = new Mold.Lib.CssParser(document);

		//Mold.mixin(this, new Mold.Lib.Event(this));
			_events = new Mold.Lib.Event(_events);

		/*
			Directive Exampel
			{
				at : "attribute|element|class|style-property",
				name : "mold-event",
				seed : "seedname", [optional]
				collect {}, [optional]
				replace : true, [optional]
				watchable : true, [optional]
				action : function(node, element, template){

				}
			}
		*/

		var _watchList = [];

		var _isInWatchList = function(element){
			return !!Mold.find(_watchList, function(selected){
				if(selected === element){
					return true;
				}
				return false;
			})
		}

		var _watch = function(element, callback){
			//if(!_isInWatchList(element)){
				var that = this;
				var selected = Mold.Lib.DomObserver.observe(element,  { childList: false, characterData : false, subtree : true});
				selected
					.on("element.inserted", function(e){
						callback.call(that, e.data.mutation);
					})
					.on("attribute.changed", function(e){
						callback.call(that, e.data.mutation);
					})
				_watchList.push(element);
			//}

		}

		var _collectChilds = function(elements, collection, elementName, output, watchable){

			var i = 0,
				len = elements.length,
				childs = {},
				selected;

			if(!output[elementName]){
				output[elementName] = [];
			}

			for(; i < len; i++){
				selected = elements[i];
				childs = {};

				Mold.each(collection.childs, function(item, name){
					output[elementName][i] = output[elementName][i] || {};
					_collect(selected, item, output[elementName][i], watchable);
					if(output[elementName].len != output[elementName].length){
						output[elementName].len = output[elementName].length;
					}
				})
			};
			return output;
		}

		var _collect = function(scope, collection, output, watchable){

			if(Mold.isArray(collection)){
				Mold.each(collection, function(item, name){

					_collect(scope, item, output, watchable)
				});
			}else{

				if(collection.element){

					var elements = scope.getElementsByTagName(collection.element);
					var elementName = collection.name || collection.element;

					if(collection.childs){
						_collectChilds(elements, collection, elementName, output, watchable)
					}

					if(watchable){

						_watch(scope, function(mutation){

							if(mutation.type === "childList"){
								Mold.each(mutation.addedNodes, function(selectedNode){
									if(
										selectedNode.nodeName
										&& collection.element
										&& (selectedNode.nodeName.toLowerCase() === collection.element.toLowerCase())
									){
										output[elementName] = output[elementName] || [];
										output[elementName].concat(
											_collectChilds(
												elements,
												collection,
												elementName,
												output,
												watchable
											)
										);
										output.trigger("element.added", { name: elementName, node : selectedNode, mutation : mutation})
									}
								});
							}else if (mutation.type === "attributes"){
								var collectedAttributes = (Mold.isArray(collection.attribute)) ? collection.attribute : [collection.attribute]
								Mold.each(collectedAttributes, function(selectedAttribute){
									if(mutation.attributeName === selectedAttribute){
										output.trigger('attribute.changed',  { name: selectedAttribute, mutation : mutation});
									}

								});
							}
						});
					}
				}else if(collection.content){

					if(collection.content === "html"){
						var content = scope.innerHTML;
					}

					if(content){
						var contentName = collection.name || collection.content;
						output[contentName] = content;

						if(watchable){
							_watch(scope, function(mutation){
								output[contentName] = scope.innerHTML;
								output.trigger("html.change", { name: contentName, mutation : mutation})
							});
						}
					}

				}else if(collection.attribute){

					var collectedAttributes = (Mold.isArray(collection.attribute)) ? collection.attribute : [collection.attribute]
					Mold.each(collectedAttributes, function(selectedAttribute){

						if(!scope.getAttribute(selectedAttribute)){
							scope[selectedAttribute] = false;
						}
						var attribute = Mold.is(scope[selectedAttribute]) ? scope[selectedAttribute] : scope.getAttribute(selectedAttribute);

						/*
						if(!attribute){
							attribute = false;
						}*/
						var attributeName = selectedAttribute.name || selectedAttribute;
						output[attributeName] = attribute;
						if(watchable){
							_watch(scope, function(mutation){

								if(
									mutation.type === "attributes"
									&& mutation.attributeName === attributeName
								){
									output[attributeName] = mutation.target.getAttribute(attributeName);
									output.trigger(attributeName + ".changed", { name: attributeName, mutation : mutation})
									output.trigger("attribute.changed", { name: attributeName, mutation : mutation})
								}
							});
						}

					});
				}
			}
			return output;
		}

		var _add = function(directive, scope, fromtemplate){
			if(!directive._id){
				directive._id = Mold.getId();
			}

			directive.apply = function(node, element, template, index, style){

				if(!element.hasDirective || !element.hasDirective(directive._id)){
					directive.scope = element;
					console.log("add directive", element.hasDirective)
					if(!element.hasDirective){
						element.hasDirective = function(id){
							return  Mold.contains(element.directives, id);
						}
					}
					if(!element.directives){
						element.directives = [];
					}
					element.directives.push(directive._id)
					if(directive.seed){

						if(directive.collect){

							var collection = _collect(directive.scope, directive.collect, Mold.mixin({}, new Mold.Lib.Event({})), directive.watchable);
						}

						if(style){
							collection = style;
						}

						var scope = {
							append : function(element){
								if(directive.replace) {
									directive.replaceElement(element);
								}else{
									directive.appendElement(element);
								}
							}
						}

						if(typeof directive.seed === "string"){
							var seed = Mold.getSeed(directive.seed);

						}else{
							var seed = new Mold.Lib.Controller(directive.seed);
						}
						directive.instance = new seed(scope, new Mold.Lib.Element(element), collection, directive.component);
						if(directive.register){
							switch(directive.register){
								case 'collection':
									directive.instance.register(collection);
									break;
								case 'component':
									directive.instance.register(directive.component);
									break;
								case 'all':
									directive.instance.register(collection);
									directive.instance.register(directive.component);
									break;

							}
						}
						if(directive.instance.scope){
							if(directive.replace) {
								directive.replaceElement(directive.instance.scope);
							}else{
								directive.appendElement(directive.instance.scope);
							}
						}else{
							directive.instance.scope = directive.scope;
							directive.instance.trigger("scope");
						}
					}

					if(directive.action){

						directive.action.call(directive, node, element, template, index);
					}
				}
			}

			directive.appendElement = function(child){
				return directive.scope.appendChild(child);
			}

			directive.replaceElement = function(child){
				return directive.scope.parentNode.replaceChild(child, directive.scope);
			}

			_directives.push(directive);
			_directivesIndex[directive.at] = _directivesIndex[directive.at] || {};
			_directivesIndex[directive.at][directive.name] = _directivesIndex[directive.at][directive.name] || [];
			_directivesIndex[directive.at][directive.name].push(_directives[_directives.length -1]);
			_events.trigger("directive.added", {
				directive : directive,
				scope : scope,
				template : fromtemplate
			 });
		}

		var _appendStyleProperty = function(element){
			Mold.each(_directives, function(directive){
				if(element.nodeType !== 1){
					return false;
				}
				if(directive.at !== "style-property"){
					return false;
				}
				var elementStyles = cssParser.getElementsByStyleProperty(directive.name);

				Mold.each(elementStyles, function(selected){

					if(!selected.element.hasDirective || !selected.element.hasDirective(directive._id)){
						styleAttributes = selected.properties;
						element = selected.element;
						node = element;
						directive.apply(
							node,
							element,
							false,
							0,
							(styleAttributes) ? styleAttributes : false
						);

					}
				});

			})
		}

		var _appendElement = function(element){

			var cacheName = element.nodeName,
				template = false,
				styleAttributes = false;

			Mold.each(_directives, function(directive){

				if(element.nodeType !== 1){
					return false;
				}

				if(!!element.hasDirective && element.hasDirective(directive._id)){
					return false;
				}

				if(directive.at === "element" && directive.name.toUpperCase() !== element.nodeName){
					return false;
				}

				if(
					directive.at === "attribute"
					&& element.getAttribute
					&&!element.getAttribute(directive.name)
					|| (directive.value && directive.value !== element.getAttribute(directive.name))
				){

					return false;
				}

				if(directive.at === "class"){

				}

				if(directive.at === "style-property"){
					return false;
				}

				var index = _cache[cacheName+"len"];
				if(directive.at === "attribute"){
					node =  element.getAttributeNode(directive.name);
				}else{
					node = element;
				}

				directive.apply(
					node,
					element,
					template,
					index,
					(styleAttributes) ? styleAttributes[index] : false
				);
				_cache[cacheName+"len"]++;

			});
		}

		_events.on("directive.added", function(e){
			var directive = e.data.directive,
				scope = e.data.scope || document;

			switch(directive.at){
				case "element":
					var elements = scope.querySelectorAll(directive.name);
					break;
				case "attribute":
					if(directive.value){
						var elements = scope.querySelectorAll('['+directive.name+'~="' + directive.value + '\"]');
					}else{
						var elements = scope.querySelectorAll("["+directive.name+"]");
					}
					break;
				case "class":
					var elements = scope.getElementsByClassName(directive.name);
					break;
				case "style-property":

					var elementStyles = cssParser.getElementsByStyleProperty(directive.name),
						elements = [],
						styleAttributes = [];

					Mold.each(elementStyles, function(selected, name){
						elements.push(selected.element)
						styleAttributes.push(selected.properties)
					});

					break;
				default:
					break;
			}

			if(elements && elements.length){

				var index = 0,
					template = e.data.template || false,
					cacheName = false,
					node = false;

				Mold.each(elements, function(element, elementName){
					if(elementName != "length"){
						switch(directive.at){
							case "element":
								node = element;
								break;
							case "attribute":
								node = element.getAttributeNode(directive.name);
								break;
							case "class":
								node = element;
								break;
						}
						directive.apply(
							node,
							element,
							template,
							index,
							(styleAttributes) ? styleAttributes[index] : false
						);

						cacheName = node.nodeName
						index++;
					}
				});
				_cache[cacheName+"len"] = index;
			}
		});

		var bodyElement = new Mold.Lib.Element(document.body);
		Mold.Lib.DomObserver.observe(bodyElement, { childList: false, characterData : false, subtree : true});

		bodyElement.on("attribute.changed", function(e){
			_appendElement(e.data.mutation.target);
			_appendStyleProperty(e.data.mutation.target);
		});

		bodyElement.on("element.inserted", function(e){
			_appendElement(e.data.mutation.target);
			_appendStyleProperty(e.data.mutation.target);
		});

		Mold.Lib.Observer.on("create.element", function(e){
			_appendElement(e.data.element);
			_appendStyleProperty(e.data.element);
		})

		return {
			on : _events.on,
			trigger : _events.trigger,
			appendElement : _appendElement,
			add : function(directive, scope, template){
				/*
				if(!directive.overwrite){
					directive = this.get(directive.at, directive.name. directive.value) || directive;
				}*/
				if(directive.at !== "style-property"){

					_add(directive, scope, template);

				}else{
					cssParser.at("ready", function(){
						_add(directive, scope, template);
					});
				}
			},

			get : function(type, name, value){
				name = name.toLowerCase();
				if(_directivesIndex[type] && _directivesIndex[type][name]){
					return _directivesIndex[type][name];
				}else{
					return false;
				}
			}
		}
	}
)<commit_msg>add directives to subelements<commit_after>Seed({
		name : "Mold.Lib.Directive",
		dna : "static",
		include : [
			"Mold.Lib.Event",
			"Mold.Lib.Controller",
			"Mold.Lib.CssParser",
			"Mold.Lib.Element",
			"Mold.Lib.Info",
			"Mold.Lib.DomObserver"
		]
	},
	function(){

		var _directives = [],
			_directivesIndex = {},
			_cache = {},
			_events = {},
			doc = (Mold.isMoldJS) ? false : document,
			cssParser = new Mold.Lib.CssParser(document);

		//Mold.mixin(this, new Mold.Lib.Event(this));
			_events = new Mold.Lib.Event(_events);

		/*
			Directive Exampel
			{
				at : "attribute|element|class|style-property",
				name : "mold-event",
				seed : "seedname", [optional]
				collect {}, [optional]
				replace : true, [optional]
				watchable : true, [optional]
				action : function(node, element, template){

				}
			}
		*/

		var _watchList = [];

		var _isInWatchList = function(element){
			return !!Mold.find(_watchList, function(selected){
				if(selected === element){
					return true;
				}
				return false;
			})
		}

		var _watch = function(element, callback){
			//if(!_isInWatchList(element)){
				var that = this;
				var selected = Mold.Lib.DomObserver.observe(element,  { childList: false, characterData : false, subtree : true});
				selected
					.on("element.inserted", function(e){
						callback.call(that, e.data.mutation);
					})
					.on("attribute.changed", function(e){
						callback.call(that, e.data.mutation);
					})
				_watchList.push(element);
			//}

		}

		var _collectChilds = function(elements, collection, elementName, output, watchable){

			var i = 0,
				len = elements.length,
				childs = {},
				selected;

			if(!output[elementName]){
				output[elementName] = [];
			}

			for(; i < len; i++){
				selected = elements[i];
				childs = {};

				Mold.each(collection.childs, function(item, name){
					output[elementName][i] = output[elementName][i] || {};
					_collect(selected, item, output[elementName][i], watchable);
					if(output[elementName].len != output[elementName].length){
						output[elementName].len = output[elementName].length;
					}
				})
			};
			return output;
		}

		var _collect = function(scope, collection, output, watchable){

			if(Mold.isArray(collection)){
				Mold.each(collection, function(item, name){

					_collect(scope, item, output, watchable)
				});
			}else{

				if(collection.element){

					var elements = scope.getElementsByTagName(collection.element);
					var elementName = collection.name || collection.element;

					if(collection.childs){
						_collectChilds(elements, collection, elementName, output, watchable)
					}

					if(watchable){

						_watch(scope, function(mutation){

							if(mutation.type === "childList"){
								Mold.each(mutation.addedNodes, function(selectedNode){
									if(
										selectedNode.nodeName
										&& collection.element
										&& (selectedNode.nodeName.toLowerCase() === collection.element.toLowerCase())
									){
										output[elementName] = output[elementName] || [];
										output[elementName].concat(
											_collectChilds(
												elements,
												collection,
												elementName,
												output,
												watchable
											)
										);
										output.trigger("element.added", { name: elementName, node : selectedNode, mutation : mutation})
									}
								});
							}else if (mutation.type === "attributes"){
								var collectedAttributes = (Mold.isArray(collection.attribute)) ? collection.attribute : [collection.attribute]
								Mold.each(collectedAttributes, function(selectedAttribute){
									if(mutation.attributeName === selectedAttribute){
										output.trigger('attribute.changed',  { name: selectedAttribute, mutation : mutation});
									}

								});
							}
						});
					}
				}else if(collection.content){

					if(collection.content === "html"){
						var content = scope.innerHTML;
					}

					if(content){
						var contentName = collection.name || collection.content;
						output[contentName] = content;

						if(watchable){
							_watch(scope, function(mutation){
								output[contentName] = scope.innerHTML;
								output.trigger("html.change", { name: contentName, mutation : mutation})
							});
						}
					}

				}else if(collection.attribute){

					var collectedAttributes = (Mold.isArray(collection.attribute)) ? collection.attribute : [collection.attribute]
					Mold.each(collectedAttributes, function(selectedAttribute){

						if(!scope.getAttribute(selectedAttribute)){
							scope[selectedAttribute] = false;
						}
						var attribute = Mold.is(scope[selectedAttribute]) ? scope[selectedAttribute] : scope.getAttribute(selectedAttribute);

						/*
						if(!attribute){
							attribute = false;
						}*/
						var attributeName = selectedAttribute.name || selectedAttribute;
						output[attributeName] = attribute;
						if(watchable){
							_watch(scope, function(mutation){

								if(
									mutation.type === "attributes"
									&& mutation.attributeName === attributeName
								){
									output[attributeName] = mutation.target.getAttribute(attributeName);
									output.trigger(attributeName + ".changed", { name: attributeName, mutation : mutation})
									output.trigger("attribute.changed", { name: attributeName, mutation : mutation})
								}
							});
						}

					});
				}
			}
			return output;
		}

		var _add = function(directive, scope, fromtemplate){
			if(!directive._id){
				directive._id = Mold.getId();
			}

			directive.apply = function(node, element, template, index, style){
				if(!element.hasDirective || !element.hasDirective(directive._id)){
					directive.scope = element;

					if(!element.hasDirective){
						element.hasDirective = function(id){
							return  Mold.contains(element.directives, id);
						}
					}
					if(!element.directives){
						element.directives = [];
					}
					element.directives.push(directive._id)
					if(directive.seed){

						if(directive.collect){

							var collection = _collect(directive.scope, directive.collect, Mold.mixin({}, new Mold.Lib.Event({})), directive.watchable);
						}

						if(style){
							collection = style;
						}

						var scope = {
							append : function(element){
								if(directive.replace) {
									directive.replaceElement(element);
								}else{
									directive.appendElement(element);
								}
							}
						}

						if(typeof directive.seed === "string"){
							var seed = Mold.getSeed(directive.seed);

						}else{
							var seed = new Mold.Lib.Controller(directive.seed);
						}
						directive.instance = new seed(scope, new Mold.Lib.Element(element), collection, directive.component);
						if(directive.register){
							switch(directive.register){
								case 'collection':
									directive.instance.register(collection);
									break;
								case 'component':
									directive.instance.register(directive.component);
									break;
								case 'all':
									directive.instance.register(collection);
									directive.instance.register(directive.component);
									break;

							}
						}
						if(directive.instance.scope){
							if(directive.replace) {
								directive.replaceElement(directive.instance.scope);
							}else{
								directive.appendElement(directive.instance.scope);
							}
						}else{
							directive.instance.scope = directive.scope;
							directive.instance.trigger("scope");
						}
					}

					if(directive.action){

						directive.action.call(directive, node, element, template, index);
					}
				}
			}

			directive.appendElement = function(child){
				return directive.scope.appendChild(child);
			}

			directive.replaceElement = function(child){
				return directive.scope.parentNode.replaceChild(child, directive.scope);
			}

			_directives.push(directive);
			_directivesIndex[directive.at] = _directivesIndex[directive.at] || {};
			_directivesIndex[directive.at][directive.name] = _directivesIndex[directive.at][directive.name] || [];
			_directivesIndex[directive.at][directive.name].push(_directives[_directives.length -1]);
			_events.trigger("directive.added", {
				directive : directive,
				scope : scope,
				template : fromtemplate
			 });
		}

		var _appendStyleProperty = function(element){
			Mold.each(_directives, function(directive){
				if(element.nodeType !== 1){
					return false;
				}
				if(directive.at !== "style-property"){
					return false;
				}
				var elementStyles = cssParser.getElementsByStyleProperty(directive.name);

				Mold.each(elementStyles, function(selected){

					if(!selected.element.hasDirective || !selected.element.hasDirective(directive._id)){
						styleAttributes = selected.properties;
						element = selected.element;
						node = element;
						directive.apply(
							node,
							element,
							false,
							0,
							(styleAttributes) ? styleAttributes : false
						);

					}
				});

			})
		}

		var _appendElement = function(element){

			var cacheName = element.nodeName,
				template = false,
				styleAttributes = false;

			Mold.each(_directives, function(directive){

				if(element.nodeType !== 1){
					return false;
				}

				if(!!element.hasDirective && element.hasDirective(directive._id)){
					return false;
				}

				if(directive.at === "element" && directive.name.toUpperCase() !== element.nodeName){
					return false;
				}

				if(
					directive.at === "attribute"
					&& element.getAttribute
					&&!element.getAttribute(directive.name)
					|| (directive.value && directive.value !== element.getAttribute(directive.name))
				){

					return false;
				}

				if(directive.at === "class"){

				}

				if(directive.at === "style-property"){
					return false;
				}

				var index = _cache[cacheName+"len"];
				if(directive.at === "attribute"){
					node =  element.getAttributeNode(directive.name);
				}else{
					node = element;
				}

				directive.apply(
					node,
					element,
					template,
					index,
					(styleAttributes) ? styleAttributes[index] : false
				);
				_cache[cacheName+"len"]++;

			});
		}

		_events.on("directive.added", function(e){
			var directive = e.data.directive,
				scope = e.data.scope || document;

			switch(directive.at){
				case "element":
					var elements = scope.querySelectorAll(directive.name);
					break;
				case "attribute":
					if(directive.value){
						var elements = scope.querySelectorAll('['+directive.name+'~="' + directive.value + '\"]');
					}else{
						var elements = scope.querySelectorAll("["+directive.name+"]");
					}
					break;
				case "class":
					var elements = scope.getElementsByClassName(directive.name);
					break;
				case "style-property":

					var elementStyles = cssParser.getElementsByStyleProperty(directive.name),
						elements = [],
						styleAttributes = [];

					Mold.each(elementStyles, function(selected, name){
						elements.push(selected.element)
						styleAttributes.push(selected.properties)
					});

					break;
				default:
					break;
			}

			if(elements && elements.length){

				var index = 0,
					template = e.data.template || false,
					cacheName = false,
					node = false;

				Mold.each(elements, function(element, elementName){
					if(elementName != "length"){
						switch(directive.at){
							case "element":
								node = element;
								break;
							case "attribute":
								node = element.getAttributeNode(directive.name);
								break;
							case "class":
								node = element;
								break;
						}
						directive.apply(
							node,
							element,
							template,
							index,
							(styleAttributes) ? styleAttributes[index] : false
						);

						cacheName = node.nodeName
						index++;
					}
				});
				_cache[cacheName+"len"] = index;
			}
		});

		var bodyElement = new Mold.Lib.Element(document.body);
		Mold.Lib.DomObserver.observe(bodyElement, { childList: false, characterData : false, subtree : true});

		var _appendSubElements = function(element){
			for(var i = 0, len = element.childNodes.length; i < len; i++){
				if(element.childNodes[i].nodeType === 1){
					_appendElement(element.childNodes[i]);
					_appendSubElements(element.childNodes[i]);
				}
			}
		}

		bodyElement.on("attribute.changed", function(e){
			_appendElement(e.data.mutation.target);
			_appendStyleProperty(e.data.mutation.target);
		});

		bodyElement.on("element.inserted", function(e){
			_appendElement(e.data.mutation.target);
			_appendSubElements(e.data.mutation.target);
			_appendStyleProperty(e.data.mutation.target);
		});

		Mold.Lib.Observer.on("create.element", function(e){
			_appendElement(e.data.element);
			_appendStyleProperty(e.data.element);
		})

		return {
			on : _events.on,
			trigger : _events.trigger,
			appendElement : _appendElement,
			add : function(directive, scope, template){
				/*
				if(!directive.overwrite){
					directive = this.get(directive.at, directive.name. directive.value) || directive;
				}*/
				if(directive.at !== "style-property"){

					_add(directive, scope, template);

				}else{
					cssParser.at("ready", function(){
						_add(directive, scope, template);
					});
				}
			},

			get : function(type, name, value){
				name = name.toLowerCase();
				if(_directivesIndex[type] && _directivesIndex[type][name]){
					return _directivesIndex[type][name];
				}else{
					return false;
				}
			}
		}
	}
)<|endoftext|>


<commit_before>
const expect = require('chai').expect;
const RG = require('../../../client/src/battles');

const Spirit = RG.Actor.Spirit;
const Actor = RG.Actor.Rogue;

describe('RG.Actor.Spirit', () => {
    it('Is an ethereal being, doesnt block passage', () => {
        const level = RG.FACT.createLevel('arena', 10, 10);
        const spirit = new Spirit('Wolf spirit');
        const actor = new Actor('Being');

        const spiritX = 2;
        const spiritY = 3;

        const map = level.getMap();
        expect(map.isPassable(spiritX, spiritY)).to.equal(true);
        level.addActor(spirit, spiritX, spiritY);
        level.addActor(actor, 3, 4);
        expect(map.isPassable(3, 4)).to.equal(false);

        expect(map.isPassable(spiritX, spiritY)).to.equal(true);

        const anotherBeing = new Actor('Being2');
        level.addActor(anotherBeing, spiritX, spiritY);
        expect(map.isPassable(spiritX, spiritY)).to.equal(false);

        const spiritGem = new RG.Item.SpiritGem('Lesser gem');
        const spiritCell = map.getCell(spiritX, spiritY);
        expect(spiritCell.getProp('actors').length).to.equal(2);

        expect(spiritGem.getStrength()).to.equal(0);
        spirit.get('Stats').setStrength(66);

        spiritGem.useItem({target: spiritCell});
        expect(spiritCell.getProp('actors').length).to.equal(1);
        expect(spiritGem.getStrength()).to.equal(66);
    });
});
<commit_msg>Corrected failing test due to refactoring.<commit_after>
const expect = require('chai').expect;
const RG = require('../../../client/src/battles');

const Spirit = RG.Actor.Spirit;
const Actor = RG.Actor.Rogue;

describe('RG.Actor.Spirit', () => {
    it('Is an ethereal being, doesnt block passage', () => {
        const level = RG.FACT.createLevel('arena', 10, 10);
        const spirit = new Spirit('Wolf spirit');
        const actor = new Actor('Being');

        const spiritX = 2;
        const spiritY = 3;

        const map = level.getMap();
        expect(map.isPassable(spiritX, spiritY)).to.equal(true);
        level.addActor(spirit, spiritX, spiritY);
        level.addActor(actor, 3, 4);
        expect(map.isPassable(3, 4)).to.equal(false);

        expect(map.isPassable(spiritX, spiritY)).to.equal(true);

        const anotherBeing = new Actor('Being2');
        level.addActor(anotherBeing, spiritX, spiritY);
        expect(map.isPassable(spiritX, spiritY)).to.equal(false);

        const spiritGem = new RG.Item.SpiritGem('Lesser gem');
        const spiritCell = map.getCell(spiritX, spiritY);
        expect(spiritCell.getProp('actors').length).to.equal(2);

        expect(spiritGem.getStrength()).to.equal(0);
        spirit.get('Stats').setStrength(66);

        actor.getInvEq().addItem(spiritGem);
        spiritGem.useItem({target: spiritCell});
        expect(spiritGem.has('SpiritBind')).to.equal(true);
    });
});
<|endoftext|>


<commit_before>import { Menubar } from 'electron-menubar'
import { logo, logger, i18n } from '../utils'
import { app, ipcMain } from 'electron'

export default async function (ctx) {
  return new Promise(resolve => {
    const menubar = new Menubar({
      index: `file://${__dirname}/app/index.html`,
      icon: logo('ice'),
      tooltip: i18n.t('ipfsNode'),
      preloadWindow: true,
      window: {
        resizable: false,
        fullscreen: false,
        skipTaskbar: true,
        width: 280,
        height: 385,
        backgroundColor: '#ffffff',
        webPreferences: {
          nodeIntegration: true
        }
      }
    })

    ctx.menubarWindow = {
      it: menubar,
      send: (type, ...args) => {
        if (menubar && menubar.window && menubar.window.webContents) {
          menubar.window.webContents.send(type, ...args)
        }
      }
    }

    const ready = () => {
      logger.info('Menubar is ready')
      menubar.tray.setHighlightMode('always')
      resolve()
    }

    ipcMain.on('app.quit', async () => {
      logger.info('Stopping daemon')

      await new Promise((resolve, reject) => {
        ctx.ipfsd.stop(err => {
          if (err) reject(err)
          else resolve()
        })
      })

      logger.info('Done. Quitting app')
      app.quit()
    })

    if (menubar.isReady()) ready()
    else menubar.on('ready', ready)
  })
}
<commit_msg>fix: flash of white while menubar loads (#731)<commit_after>import { Menubar } from 'electron-menubar'
import { logo, logger, i18n } from '../utils'
import { app, ipcMain } from 'electron'

export default async function (ctx) {
  return new Promise(resolve => {
    const menubar = new Menubar({
      index: `file://${__dirname}/app/index.html`,
      icon: logo('ice'),
      tooltip: i18n.t('ipfsNode'),
      preloadWindow: true,
      window: {
        resizable: false,
        fullscreen: false,
        skipTaskbar: true,
        width: 280,
        height: 385,
        backgroundColor: '#0b3a53',
        webPreferences: {
          nodeIntegration: true
        }
      }
    })

    ctx.menubarWindow = {
      it: menubar,
      send: (type, ...args) => {
        if (menubar && menubar.window && menubar.window.webContents) {
          menubar.window.webContents.send(type, ...args)
        }
      }
    }

    const ready = () => {
      logger.info('Menubar is ready')
      menubar.tray.setHighlightMode('always')
      resolve()
    }

    ipcMain.on('app.quit', async () => {
      logger.info('Stopping daemon')

      await new Promise((resolve, reject) => {
        ctx.ipfsd.stop(err => {
          if (err) reject(err)
          else resolve()
        })
      })

      logger.info('Done. Quitting app')
      app.quit()
    })

    if (menubar.isReady()) ready()
    else menubar.on('ready', ready)
  })
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2013 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Object representing an icon on a block.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

goog.module('Blockly.Icon');
goog.module.declareLegacyNamespace();

/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const Bubble = goog.requireType('Blockly.Bubble');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Size = goog.require('Blockly.utils.Size');
const Svg = goog.require('Blockly.utils.Svg');
const browserEvents = goog.require('Blockly.browserEvents');
const dom = goog.require('Blockly.utils.dom');
const {getRelativeXY, isRightButton} = goog.require('Blockly.utils');

/**
 * Class for an icon.
 * @param {BlockSvg} block The block associated with this icon.
 * @constructor
 * @abstract
 */
const Icon = function(block) {
  /**
   * The block this icon is attached to.
   * @type {BlockSvg}
   * @protected
   */
  this.block_ = block;

  /**
   * The icon SVG group.
   * @type {?SVGGElement}
   */
  this.iconGroup_ = null;
};

/**
 * Does this icon get hidden when the block is collapsed.
 */
Icon.prototype.collapseHidden = true;

/**
 * Height and width of icons.
 */
Icon.prototype.SIZE = 17;

/**
 * Bubble UI (if visible).
 * @type {?Bubble}
 * @protected
 */
Icon.prototype.bubble_ = null;

/**
 * Absolute coordinate of icon's center.
 * @type {?Coordinate}
 * @protected
 */
Icon.prototype.iconXY_ = null;

/**
 * Create the icon on the block.
 */
Icon.prototype.createIcon = function() {
  if (this.iconGroup_) {
    // Icon already exists.
    return;
  }
  /* Here's the markup that will be generated:
  <g class="blocklyIconGroup">
    ...
  </g>
  */
  this.iconGroup_ =
      dom.createSvgElement(Svg.G, {'class': 'blocklyIconGroup'}, null);
  if (this.block_.isInFlyout) {
    dom.addClass(
        /** @type {!Element} */ (this.iconGroup_), 'blocklyIconGroupReadonly');
  }
  this.drawIcon_(this.iconGroup_);

  this.block_.getSvgRoot().appendChild(this.iconGroup_);
  browserEvents.conditionalBind(
      this.iconGroup_, 'mouseup', this, this.iconClick_);
  this.updateEditable();
};

/**
 * Dispose of this icon.
 */
Icon.prototype.dispose = function() {
  // Dispose of and unlink the icon.
  dom.removeNode(this.iconGroup_);
  this.iconGroup_ = null;
  // Dispose of and unlink the bubble.
  this.setVisible(false);
  this.block_ = null;
};

/**
 * Add or remove the UI indicating if this icon may be clicked or not.
 */
Icon.prototype.updateEditable = function() {
  // No-op on the base class.
};

/**
 * Is the associated bubble visible?
 * @return {boolean} True if the bubble is visible.
 */
Icon.prototype.isVisible = function() {
  return !!this.bubble_;
};

/**
 * Clicking on the icon toggles if the bubble is visible.
 * @param {!Event} e Mouse click event.
 * @protected
 */
Icon.prototype.iconClick_ = function(e) {
  if (this.block_.workspace.isDragging()) {
    // Drag operation is concluding.  Don't open the editor.
    return;
  }
  if (!this.block_.isInFlyout && !isRightButton(e)) {
    this.setVisible(!this.isVisible());
  }
};

/**
 * Change the colour of the associated bubble to match its block.
 */
Icon.prototype.applyColour = function() {
  if (this.isVisible()) {
    this.bubble_.setColour(this.block_.style.colourPrimary);
  }
};

/**
 * Notification that the icon has moved.  Update the arrow accordingly.
 * @param {!Coordinate} xy Absolute location in workspace coordinates.
 */
Icon.prototype.setIconLocation = function(xy) {
  this.iconXY_ = xy;
  if (this.isVisible()) {
    this.bubble_.setAnchorLocation(xy);
  }
};

/**
 * Notification that the icon has moved, but we don't really know where.
 * Recompute the icon's location from scratch.
 */
Icon.prototype.computeIconLocation = function() {
  // Find coordinates for the centre of the icon and update the arrow.
  const blockXY = this.block_.getRelativeToSurfaceXY();
  const iconXY = getRelativeXY(
      /** @type {!SVGElement} */ (this.iconGroup_));
  const newXY = new Coordinate(
      blockXY.x + iconXY.x + this.SIZE / 2,
      blockXY.y + iconXY.y + this.SIZE / 2);
  if (!Coordinate.equals(this.getIconLocation(), newXY)) {
    this.setIconLocation(newXY);
  }
};

/**
 * Returns the center of the block's icon relative to the surface.
 * @return {?Coordinate} Object with x and y properties in
 *     workspace coordinates.
 */
Icon.prototype.getIconLocation = function() {
  return this.iconXY_;
};

/**
 * Get the size of the icon as used for rendering.
 * This differs from the actual size of the icon, because it bulges slightly
 * out of its row rather than increasing the height of its row.
 * @return {!Size} Height and width.
 */
// TODO (#2562): Remove getCorrectedSize.
Icon.prototype.getCorrectedSize = function() {
  return new Size(Icon.prototype.SIZE, Icon.prototype.SIZE - 2);
};

/**
 * Draw the icon.
 * @param {!Element} group The icon group.
 * @protected
 */
Icon.prototype.drawIcon_;

/**
 * Show or hide the icon.
 * @param {boolean} visible True if the icon should be visible.
 */
Icon.prototype.setVisible;

exports = Icon;
<commit_msg>Made icon size field const<commit_after>/**
 * @license
 * Copyright 2013 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Object representing an icon on a block.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

goog.module('Blockly.Icon');
goog.module.declareLegacyNamespace();

/* eslint-disable-next-line no-unused-vars */
const BlockSvg = goog.requireType('Blockly.BlockSvg');
/* eslint-disable-next-line no-unused-vars */
const Bubble = goog.requireType('Blockly.Bubble');
const Coordinate = goog.require('Blockly.utils.Coordinate');
const Size = goog.require('Blockly.utils.Size');
const Svg = goog.require('Blockly.utils.Svg');
const browserEvents = goog.require('Blockly.browserEvents');
const dom = goog.require('Blockly.utils.dom');
const {getRelativeXY, isRightButton} = goog.require('Blockly.utils');

/**
 * Class for an icon.
 * @param {BlockSvg} block The block associated with this icon.
 * @constructor
 * @abstract
 */
const Icon = function(block) {
  /**
   * The block this icon is attached to.
   * @type {BlockSvg}
   * @protected
   */
  this.block_ = block;

  /**
   * The icon SVG group.
   * @type {?SVGGElement}
   */
  this.iconGroup_ = null;
};

/**
 * Does this icon get hidden when the block is collapsed.
 */
Icon.prototype.collapseHidden = true;

/**
 * Height and width of icons.
 * @const
 */
Icon.prototype.SIZE = 17;

/**
 * Bubble UI (if visible).
 * @type {?Bubble}
 * @protected
 */
Icon.prototype.bubble_ = null;

/**
 * Absolute coordinate of icon's center.
 * @type {?Coordinate}
 * @protected
 */
Icon.prototype.iconXY_ = null;

/**
 * Create the icon on the block.
 */
Icon.prototype.createIcon = function() {
  if (this.iconGroup_) {
    // Icon already exists.
    return;
  }
  /* Here's the markup that will be generated:
  <g class="blocklyIconGroup">
    ...
  </g>
  */
  this.iconGroup_ =
      dom.createSvgElement(Svg.G, {'class': 'blocklyIconGroup'}, null);
  if (this.block_.isInFlyout) {
    dom.addClass(
        /** @type {!Element} */ (this.iconGroup_), 'blocklyIconGroupReadonly');
  }
  this.drawIcon_(this.iconGroup_);

  this.block_.getSvgRoot().appendChild(this.iconGroup_);
  browserEvents.conditionalBind(
      this.iconGroup_, 'mouseup', this, this.iconClick_);
  this.updateEditable();
};

/**
 * Dispose of this icon.
 */
Icon.prototype.dispose = function() {
  // Dispose of and unlink the icon.
  dom.removeNode(this.iconGroup_);
  this.iconGroup_ = null;
  // Dispose of and unlink the bubble.
  this.setVisible(false);
  this.block_ = null;
};

/**
 * Add or remove the UI indicating if this icon may be clicked or not.
 */
Icon.prototype.updateEditable = function() {
  // No-op on the base class.
};

/**
 * Is the associated bubble visible?
 * @return {boolean} True if the bubble is visible.
 */
Icon.prototype.isVisible = function() {
  return !!this.bubble_;
};

/**
 * Clicking on the icon toggles if the bubble is visible.
 * @param {!Event} e Mouse click event.
 * @protected
 */
Icon.prototype.iconClick_ = function(e) {
  if (this.block_.workspace.isDragging()) {
    // Drag operation is concluding.  Don't open the editor.
    return;
  }
  if (!this.block_.isInFlyout && !isRightButton(e)) {
    this.setVisible(!this.isVisible());
  }
};

/**
 * Change the colour of the associated bubble to match its block.
 */
Icon.prototype.applyColour = function() {
  if (this.isVisible()) {
    this.bubble_.setColour(this.block_.style.colourPrimary);
  }
};

/**
 * Notification that the icon has moved.  Update the arrow accordingly.
 * @param {!Coordinate} xy Absolute location in workspace coordinates.
 */
Icon.prototype.setIconLocation = function(xy) {
  this.iconXY_ = xy;
  if (this.isVisible()) {
    this.bubble_.setAnchorLocation(xy);
  }
};

/**
 * Notification that the icon has moved, but we don't really know where.
 * Recompute the icon's location from scratch.
 */
Icon.prototype.computeIconLocation = function() {
  // Find coordinates for the centre of the icon and update the arrow.
  const blockXY = this.block_.getRelativeToSurfaceXY();
  const iconXY = getRelativeXY(
      /** @type {!SVGElement} */ (this.iconGroup_));
  const newXY = new Coordinate(
      blockXY.x + iconXY.x + this.SIZE / 2,
      blockXY.y + iconXY.y + this.SIZE / 2);
  if (!Coordinate.equals(this.getIconLocation(), newXY)) {
    this.setIconLocation(newXY);
  }
};

/**
 * Returns the center of the block's icon relative to the surface.
 * @return {?Coordinate} Object with x and y properties in
 *     workspace coordinates.
 */
Icon.prototype.getIconLocation = function() {
  return this.iconXY_;
};

/**
 * Get the size of the icon as used for rendering.
 * This differs from the actual size of the icon, because it bulges slightly
 * out of its row rather than increasing the height of its row.
 * @return {!Size} Height and width.
 */
// TODO (#2562): Remove getCorrectedSize.
Icon.prototype.getCorrectedSize = function() {
  return new Size(Icon.prototype.SIZE, Icon.prototype.SIZE - 2);
};

/**
 * Draw the icon.
 * @param {!Element} group The icon group.
 * @protected
 */
Icon.prototype.drawIcon_;

/**
 * Show or hide the icon.
 * @param {boolean} visible True if the icon should be visible.
 */
Icon.prototype.setVisible;

exports = Icon;
<|endoftext|>


<commit_before>/**
 * Things Gateway Database.
 *
 * Stores a list of Things connected to the gateway.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

'use strict';

const config = require('config');
const sqlite3 = require('sqlite3').verbose();
const fs = require('fs');
const Passwords = require('./passwords');
const assert = require('assert');

// Imported as a module so we use a relative path.
const ThingsData = require('../static/things.json');

const TABLES = [
  'users',
  'jsonwebtoken_to_user',
  'things',
];

var Database = {
  /**
   * SQLLite3 Database object.
   */
  db: null,

  /**
   * Open the database.
   */
  open: function() {
    var filename = config.get('database.filename');
    var removeBeforeOpen = config.get('database.removeBeforeOpen');
    // Check if database already exists
    var exists = fs.existsSync(filename);
    if (exists && removeBeforeOpen) {
      fs.unlinkSync(filename);
      exists = false;
    }
    console.log(exists ? 'Opening' : 'Creating', 'database:', filename);
    // Open database or create it if it doesn't exist
    this.db = new sqlite3.Database(filename);
    // If database newly created, populate with default data
    if (!exists) {
      this.populate();
    }
  },

  /**
   * Populate the database with default data.
   */
  populate: function() {
    console.log('Populating database with default things...');
    var db = this.db;

    // Create Things table
    var thingsTableSQL = 'CREATE TABLE IF NOT EXISTS things (' +
      'id TEXT PRIMARY KEY,' +
      'description TEXT' +
    ');';
    db.serialize(function() {
      db.run(thingsTableSQL);
      // Populate Things table
      var insertSQL = db.prepare(
        'INSERT INTO things (id, description) VALUES (?, ?)');
      for (var thing of ThingsData) {
        var thingId = thing.id;
        delete thing.id;
        insertSQL.run(thingId, JSON.stringify(thing));
      }
      insertSQL.finalize();
    });

    // Create Users table
    var usersTableSQL = 'CREATE TABLE IF NOT EXISTS users (' +
      'id INTEGER PRIMARY KEY ASC,' +
      'email TEXT UNIQUE,' +
      'password TEXT,' +
      'name TEXT' +
    ');';
    db.serialize(function() {
      db.run(usersTableSQL);
      // Add default user if provided
      var defaultUser = config.get('authentication.defaultUser');
      if(!defaultUser) {
        return;
      }
      var passwordHash = Passwords.hashSync(defaultUser.password);
      db.run('INSERT INTO users (email, password, name) VALUES (?, ?, ?)',
        [defaultUser.email, passwordHash, defaultUser.name],
        function(error) {
        if (error) {
          console.error('Failed to save default user.');
        } else {
          console.log('Saved default user ' + defaultUser.email);
        }
      });
    });

    /**
     * This really should have a foreign key constraint but it does not work
     * with our version of node-sqlite / sqlite.
     *
     * https://github.com/mapbox/node-sqlite3/pull/660
     *
     * Instead, the INTEGER user is either the id of the user or -1 if NULL
     */
    var jwtTableSQL = 'CREATE TABLE IF NOT EXISTS jsonwebtoken_to_user (' +
      'id INTEGER PRIMARY KEY ASC,' +
      'keyId TEXT UNIQUE,' + // public id (kid in JWT terms).
      'user INTEGER,' +
      'issuedAt DATE,' +
      'publicKey TEXT' +
    ');';

    db.serialize(function() {
      db.run(jwtTableSQL);
    });
  },

  /**
   * Get all Things stored in the database.
   *
   * @return Promise which resolves with a list of Thing objects.
   */
  getThings: function() {
    return new Promise((function(resolve, reject) {
      this.db.all('SELECT id, description FROM things',
        (function(err, rows) {
        if (err) {
          reject(err);
        } else {
          var things = [];
          for (var row of rows) {
            var thing = JSON.parse(row.description);
            thing.id = row.id;
            things.push(thing);
          }
          resolve(things);
        }
      }));
    }).bind(this));
  },

  /**
   * Get a thing by its id.
   *
   * @param {string} id ID of the Thing to get.
   */
  getThing: function(id) {
    return new Promise((function(resolve, reject) {
      var db = this.db;
      db.get('SELECT id, description FROM things WHERE id = ?', id,
        function(error, row) {
        if (error || row === undefined) {
          reject(error);
        } else {
          var thing = JSON.parse(row.description);
          thing.id = row.id;
          resolve(thing);
        }
      });
    }).bind(this));
  },

  /**
   * Add a new Thing to the Database.
   *
   * @param String id The ID to give the new Thing.
   * @param String description A serialised Thing description.
   */
  createThing: function(id, description) {
    return new Promise((function(resolve, reject) {
      var db = this.db;
      db.run('INSERT INTO things (id, description) VALUES (?, ?)',
        [id, JSON.stringify(description)], function(error) {
          if (error) {
            reject(error);
          } else {
            resolve(description);
          }
        });
    }).bind(this));
  },

  /**
   * Remove a Thing from the Database.
   *
   * @param String id The ID of the Thing to remove.
   */
  removeThing: function(id) {
    return new Promise((function(resolve, reject) {
      var db = this.db;
      db.run('DELETE FROM things WHERE id = ?', id, function(error) {
        if (error) {
          reject(error);
        } else {
          resolve();
        }
      });
    }).bind(this));
  },

  /**
   * Get a user by their email address.
   */
  getUser: function(email) {
    return new Promise((function(resolve, reject) {
      var db = this.db;
      db.get('SELECT * FROM users WHERE email = ?', email,
        function(error, row) {
        if (error) {
          reject(error);
        } else {
          resolve(row);
        }
      });
    }).bind(this));
  },

  /**
   * Get a user by it's primary key (id).
   */
  getUserById: async function(id) {
    return await this.get(
      'SELECT * FROM users WHERE id = ?',
      id
    );
  },

  /**
   * Get all Users stored in the database.
   *
   * @return {Promise<Array<User>>} resolves with a list of User objects
   */
  getUsers: function() {
    return new Promise((resolve, reject) => {
      this.db.all('SELECT * FROM users', (err, rows) => {
        if (err) {
          reject(err);
        } else {
          resolve(rows);
        }
      });
    });
  },

  getUserCount: async function() {
    const {count} = await this.get('SELECT count(*) as count FROM users');
    return count;
  },

  /**
   * Create a user
   * @param {User} user
   * @return {Promise<User>}
   */
  createUser: async function(user) {
    const result = await this.run(
      'INSERT INTO users (email, password, name) VALUES (?, ?, ?)',
      [user.email, user.password, user.name]
    );
    assert(typeof result.lastID === 'number');
    return result.lastID;
  },

  deleteUser: function(userId) {
    assert(typeof userId === 'number');
    const deleteUser = this.run(
      'DELETE FROM users WHERE id = ?',
      [userId]
    );
    const deleteTokens = this.deleteJSONWebTokensForUser(userId);
    /**
     * XXX: This is a terrible hack until we get foreign key constraint support
     * turned on with node-sqlite. As is this could leave junk around in the db.
     */
    return Promise.all([deleteTokens, deleteUser]);
  },

  /**
   * Delete all jsonwebtoken's for a given user.
   */
  deleteJSONWebTokensForUser: function(userId) {
    assert(typeof userId === 'number');
    return this.run(
      'DELETE FROM jsonwebtoken_to_user WHERE user = ?',
      [userId]
    );
  },

  /**
   * Insert a JSONWebToken into the database and return it's primary key.
   */
  createJSONWebToken: async function(token) {
    const {keyId, user, publicKey, issuedAt} = token;
    const result = await this.run(
      'INSERT INTO jsonwebtoken_to_user (keyId, user, issuedAt, publicKey) ' +
      'VALUES (?, ?, ?, ?)',
      [keyId, user, issuedAt, publicKey]
    );
    assert(typeof result.lastID === 'number');
    return result.lastID;
  },

  /**
   * Get a JWT by it's key id.
   */
  getJSONWebTokenByKeyId: function(keyId) {
    assert(typeof keyId === 'string');
    return this.get(
      'SELECT * FROM jsonwebtoken_to_user WHERE keyId = ?',
      keyId
    );
  },

  /**
   * Delete a JWT by it's key id.
   */
  deleteJSONWebTokenByKeyId: async function(keyId) {
    assert(typeof keyId === 'string');
    const result = await this.run(
      'DELETE FROM jsonwebtoken_to_user WHERE keyId = ?',
      keyId
    );
    return result.changes !== 0;
  },

  /**
   * ONLY for tests (clears all tables).
   */
  deleteEverything: async function() {
    return Promise.all(TABLES.map((t) => {
      return this.run(`DELETE FROM ${t}`);
    }));
  },

  get: function(sql, ...params) {
    return new Promise((accept, reject) => {
      params.push(function(err, row) {
        if (err) {
          reject(err);
          return;
        }
        accept(row);
      });

      try {
        this.db.get(sql, ...params);
      } catch (err) {
        reject(err);
        return;
      }
    });
  },

  run: function(sql, values) {
    return new Promise((accept, reject) => {
      try {
        this.db.run(sql, values, function (err) {
          if (err) {
            reject(err);
            return;
          }
          // node-sqlite puts results on "this" so avoid arrrow fn.
          accept(this);
        });
      } catch (err) {
        reject(err);
      }
    });
  }
};

module.exports = Database;
<commit_msg>Document Database.run<commit_after>/**
 * Things Gateway Database.
 *
 * Stores a list of Things connected to the gateway.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

'use strict';

const config = require('config');
const sqlite3 = require('sqlite3').verbose();
const fs = require('fs');
const Passwords = require('./passwords');
const assert = require('assert');

// Imported as a module so we use a relative path.
const ThingsData = require('../static/things.json');

const TABLES = [
  'users',
  'jsonwebtoken_to_user',
  'things',
];

var Database = {
  /**
   * SQLLite3 Database object.
   */
  db: null,

  /**
   * Open the database.
   */
  open: function() {
    var filename = config.get('database.filename');
    var removeBeforeOpen = config.get('database.removeBeforeOpen');
    // Check if database already exists
    var exists = fs.existsSync(filename);
    if (exists && removeBeforeOpen) {
      fs.unlinkSync(filename);
      exists = false;
    }
    console.log(exists ? 'Opening' : 'Creating', 'database:', filename);
    // Open database or create it if it doesn't exist
    this.db = new sqlite3.Database(filename);
    // If database newly created, populate with default data
    if (!exists) {
      this.populate();
    }
  },

  /**
   * Populate the database with default data.
   */
  populate: function() {
    console.log('Populating database with default things...');
    var db = this.db;

    // Create Things table
    var thingsTableSQL = 'CREATE TABLE IF NOT EXISTS things (' +
      'id TEXT PRIMARY KEY,' +
      'description TEXT' +
    ');';
    db.serialize(function() {
      db.run(thingsTableSQL);
      // Populate Things table
      var insertSQL = db.prepare(
        'INSERT INTO things (id, description) VALUES (?, ?)');
      for (var thing of ThingsData) {
        var thingId = thing.id;
        delete thing.id;
        insertSQL.run(thingId, JSON.stringify(thing));
      }
      insertSQL.finalize();
    });

    // Create Users table
    var usersTableSQL = 'CREATE TABLE IF NOT EXISTS users (' +
      'id INTEGER PRIMARY KEY ASC,' +
      'email TEXT UNIQUE,' +
      'password TEXT,' +
      'name TEXT' +
    ');';
    db.serialize(function() {
      db.run(usersTableSQL);
      // Add default user if provided
      var defaultUser = config.get('authentication.defaultUser');
      if(!defaultUser) {
        return;
      }
      var passwordHash = Passwords.hashSync(defaultUser.password);
      db.run('INSERT INTO users (email, password, name) VALUES (?, ?, ?)',
        [defaultUser.email, passwordHash, defaultUser.name],
        function(error) {
        if (error) {
          console.error('Failed to save default user.');
        } else {
          console.log('Saved default user ' + defaultUser.email);
        }
      });
    });

    /**
     * This really should have a foreign key constraint but it does not work
     * with our version of node-sqlite / sqlite.
     *
     * https://github.com/mapbox/node-sqlite3/pull/660
     *
     * Instead, the INTEGER user is either the id of the user or -1 if NULL
     */
    var jwtTableSQL = 'CREATE TABLE IF NOT EXISTS jsonwebtoken_to_user (' +
      'id INTEGER PRIMARY KEY ASC,' +
      'keyId TEXT UNIQUE,' + // public id (kid in JWT terms).
      'user INTEGER,' +
      'issuedAt DATE,' +
      'publicKey TEXT' +
    ');';

    db.serialize(function() {
      db.run(jwtTableSQL);
    });
  },

  /**
   * Get all Things stored in the database.
   *
   * @return Promise which resolves with a list of Thing objects.
   */
  getThings: function() {
    return new Promise((function(resolve, reject) {
      this.db.all('SELECT id, description FROM things',
        (function(err, rows) {
        if (err) {
          reject(err);
        } else {
          var things = [];
          for (var row of rows) {
            var thing = JSON.parse(row.description);
            thing.id = row.id;
            things.push(thing);
          }
          resolve(things);
        }
      }));
    }).bind(this));
  },

  /**
   * Get a thing by its id.
   *
   * @param {string} id ID of the Thing to get.
   */
  getThing: function(id) {
    return new Promise((function(resolve, reject) {
      var db = this.db;
      db.get('SELECT id, description FROM things WHERE id = ?', id,
        function(error, row) {
        if (error || row === undefined) {
          reject(error);
        } else {
          var thing = JSON.parse(row.description);
          thing.id = row.id;
          resolve(thing);
        }
      });
    }).bind(this));
  },

  /**
   * Add a new Thing to the Database.
   *
   * @param String id The ID to give the new Thing.
   * @param String description A serialised Thing description.
   */
  createThing: function(id, description) {
    return new Promise((function(resolve, reject) {
      var db = this.db;
      db.run('INSERT INTO things (id, description) VALUES (?, ?)',
        [id, JSON.stringify(description)], function(error) {
          if (error) {
            reject(error);
          } else {
            resolve(description);
          }
        });
    }).bind(this));
  },

  /**
   * Remove a Thing from the Database.
   *
   * @param String id The ID of the Thing to remove.
   */
  removeThing: function(id) {
    return new Promise((function(resolve, reject) {
      var db = this.db;
      db.run('DELETE FROM things WHERE id = ?', id, function(error) {
        if (error) {
          reject(error);
        } else {
          resolve();
        }
      });
    }).bind(this));
  },

  /**
   * Get a user by their email address.
   */
  getUser: function(email) {
    return new Promise((function(resolve, reject) {
      var db = this.db;
      db.get('SELECT * FROM users WHERE email = ?', email,
        function(error, row) {
        if (error) {
          reject(error);
        } else {
          resolve(row);
        }
      });
    }).bind(this));
  },

  /**
   * Get a user by it's primary key (id).
   */
  getUserById: async function(id) {
    return await this.get(
      'SELECT * FROM users WHERE id = ?',
      id
    );
  },

  /**
   * Get all Users stored in the database.
   *
   * @return {Promise<Array<User>>} resolves with a list of User objects
   */
  getUsers: function() {
    return new Promise((resolve, reject) => {
      this.db.all('SELECT * FROM users', (err, rows) => {
        if (err) {
          reject(err);
        } else {
          resolve(rows);
        }
      });
    });
  },

  getUserCount: async function() {
    const {count} = await this.get('SELECT count(*) as count FROM users');
    return count;
  },

  /**
   * Create a user
   * @param {User} user
   * @return {Promise<User>}
   */
  createUser: async function(user) {
    const result = await this.run(
      'INSERT INTO users (email, password, name) VALUES (?, ?, ?)',
      [user.email, user.password, user.name]
    );
    assert(typeof result.lastID === 'number');
    return result.lastID;
  },

  deleteUser: function(userId) {
    assert(typeof userId === 'number');
    const deleteUser = this.run(
      'DELETE FROM users WHERE id = ?',
      [userId]
    );
    const deleteTokens = this.deleteJSONWebTokensForUser(userId);
    /**
     * XXX: This is a terrible hack until we get foreign key constraint support
     * turned on with node-sqlite. As is this could leave junk around in the db.
     */
    return Promise.all([deleteTokens, deleteUser]);
  },

  /**
   * Delete all jsonwebtoken's for a given user.
   */
  deleteJSONWebTokensForUser: function(userId) {
    assert(typeof userId === 'number');
    return this.run(
      'DELETE FROM jsonwebtoken_to_user WHERE user = ?',
      [userId]
    );
  },

  /**
   * Insert a JSONWebToken into the database and return it's primary key.
   */
  createJSONWebToken: async function(token) {
    const {keyId, user, publicKey, issuedAt} = token;
    const result = await this.run(
      'INSERT INTO jsonwebtoken_to_user (keyId, user, issuedAt, publicKey) ' +
      'VALUES (?, ?, ?, ?)',
      [keyId, user, issuedAt, publicKey]
    );
    assert(typeof result.lastID === 'number');
    return result.lastID;
  },

  /**
   * Get a JWT by it's key id.
   */
  getJSONWebTokenByKeyId: function(keyId) {
    assert(typeof keyId === 'string');
    return this.get(
      'SELECT * FROM jsonwebtoken_to_user WHERE keyId = ?',
      keyId
    );
  },

  /**
   * Delete a JWT by it's key id.
   */
  deleteJSONWebTokenByKeyId: async function(keyId) {
    assert(typeof keyId === 'string');
    const result = await this.run(
      'DELETE FROM jsonwebtoken_to_user WHERE keyId = ?',
      keyId
    );
    return result.changes !== 0;
  },

  /**
   * ONLY for tests (clears all tables).
   */
  deleteEverything: async function() {
    return Promise.all(TABLES.map((t) => {
      return this.run(`DELETE FROM ${t}`);
    }));
  },

  get: function(sql, ...params) {
    return new Promise((accept, reject) => {
      params.push(function(err, row) {
        if (err) {
          reject(err);
          return;
        }
        accept(row);
      });

      try {
        this.db.get(sql, ...params);
      } catch (err) {
        reject(err);
        return;
      }
    });
  },

  /**
   * Run a SQL statement
   * @param {String} sql
   * @param {Array<any>} values
   * @return {Promise<Object>} promise resolved to `this` of statement result
   */
  run: function(sql, values) {
    return new Promise((accept, reject) => {
      try {
        this.db.run(sql, values, function (err) {
          if (err) {
            reject(err);
            return;
          }
          // node-sqlite puts results on "this" so avoid arrrow fn.
          accept(this);
        });
      } catch (err) {
        reject(err);
      }
    });
  }
};

module.exports = Database;
<|endoftext|>


<commit_before>import protodef from 'rethinkdb/proto-def';
import reqlTermExamples from './ReqlTermExamples';
import {isArr, isObj, ensure} from './util';

// Map RethinkDB AST term ids to their canonical names. This is simply and
// inverse mapping of protodef.Term.TermType.
const termIdToName = {};
Object.keys(protodef.Term.TermType).forEach(termName => {
  const termId = protodef.Term.TermType[termName];
  termIdToName[termId] = termName;
});

// Construct and return a rethinkdb driver AST term. The supplied termId is the
// numerical term id from the wire protocol. Args is the array of arguments
// that the term takes, and options is the optional options object that the
// term takes. Both args and options should be passed in as AST objects, not
// JSON wire protocol objects.
//
// E.g., buildAstTerm(15, ['abc']) returns the same thing as r.table('abc')
const buildAstTerm = (termId, args, options) => {
  const termName = termIdToName[termId];
  const termClass = reqlTermExamples[termName].constructor;
  const termSuperClass = termClass.__super__.constructor;
  if (termSuperClass.name === 'RDBOp') {
    const term = Object.create(termClass.prototype);
    return termSuperClass.call(term, options, ...args);
  } else if (termSuperClass.name === 'RDBConstant') {
    return new termClass(options, ...args);
  } else {
    throw new Error('Unexpected term type: ' + termSuperClass.name);
  }
};

// Parse a reql JSON term and return an AST object. The term parameter should
// come directly from the JSON format it was sent over the RethinkDB JSON wire
// protocol. May throw an exception if the term is ill formed.
const reqlJsonToAst = term => {
  if (isArr(term)) {
    const [termId, args, options] = term;
    ensure(term.length <= 3, 'Too many array elements');
    ensure(isArr(args), 'Invalid args type');
    ensure(options === undefined || isObj(options), 'Invalid options type');
    return buildAstTerm(termId, args.map(reqlJsonToAst), reqlJsonToAst(options));
  } else if (isObj(term)) {
    const result = {};
    Object.keys(term).forEach(key => {
      result[key] = reqlJsonToAst(term[key]);
    });
    return result;
  } else {
    return term;
  }
};

export {
  reqlJsonToAst,
};
<commit_msg>Wrap primitives in r.expr() in reqlJsonToAst()<commit_after>import protodef from 'rethinkdb/proto-def';
import r from 'rethinkdb';
import reqlTermExamples from './ReqlTermExamples';
import {isArr, isObj, ensure} from './util';

// Map RethinkDB AST term ids to their canonical names. This is simply and
// inverse mapping of protodef.Term.TermType.
const termIdToName = {};
Object.keys(protodef.Term.TermType).forEach(termName => {
  const termId = protodef.Term.TermType[termName];
  termIdToName[termId] = termName;
});

// Construct and return a rethinkdb driver AST term. The supplied termId is the
// numerical term id from the wire protocol. Args is the array of arguments
// that the term takes, and options is the optional options object that the
// term takes. Both args and options should be passed in as AST objects, not
// JSON wire protocol objects.
//
// E.g., buildAstTerm(15, ['abc']) returns the same thing as r.table('abc')
const buildAstTerm = (termId, args, options) => {
  const termName = termIdToName[termId];
  const termClass = reqlTermExamples[termName].constructor;
  const termSuperClass = termClass.__super__.constructor;
  if (termSuperClass.name === 'RDBOp') {
    const term = Object.create(termClass.prototype);
    return termSuperClass.call(term, options, ...args);
  } else if (termSuperClass.name === 'RDBConstant') {
    return new termClass(options, ...args);
  } else {
    throw new Error('Unexpected term type: ' + termSuperClass.name);
  }
};

// Parse a reql JSON term and return an AST object. The term parameter should
// come directly from the JSON format it was sent over the RethinkDB JSON wire
// protocol. May throw an exception if the term is ill formed.
const reqlJsonToAst = term => {
  if (isArr(term)) {
    const [termId, args, options] = term;
    ensure(term.length <= 3, 'Too many array elements');
    ensure(isArr(args), 'Invalid args type');
    ensure(options === undefined || isObj(options), 'Invalid options type');
    return buildAstTerm(termId, args.map(reqlJsonToAst), reqlJsonToAst(options));
  } else if (isObj(term)) {
    const result = {};
    Object.keys(term).forEach(key => {
      result[key] = reqlJsonToAst(term[key]);
    });
    return result;
  } else if (term === undefined) {
    return undefined;
  } else {
    return r.expr(term);
  }
};

export {
  reqlJsonToAst,
};
<|endoftext|>