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

   vscrollService.$inject = ['$q'];
   vscrollCtrl.$inject = ['$scope', '$element', '$window'];
   vscrollPortYDirective.$inject = ['$rootScope', '$parse'];
   vscrollPortXDirective.$inject = ['$rootScope', '$parse'];

   angular.module('vscroll', [])
       .service('vscroll', vscrollService)
       .filter('vscroll', vscrollFilter)
       .directive('vscroll', vscrollDirective)
       .directive('vscrollPortY', vscrollPortYDirective)
       .directive('vscrollPortX', vscrollPortXDirective)
       .directive('vscrollRow', vscrollRowDirective)
       .directive('vscrollColumn', vscrollColumnDirective)
       .directive('vscrollMark', vscrollMarkDirective);

   var getHeight = function (element) {
      var height = element.offsetHeight,
          style = getComputedStyle(element);

      height += parseInt(style.marginTop) + parseInt(style.marginBottom);
      return height;
   };

   var getWidth = function (element) {
      var width = element.offsetWidth,
          style = getComputedStyle(element);

      width += parseInt(style.marginLeft) + parseInt(style.marginRight);
      return width;
   };

   var findIndexAt = function (items, value) {
      var length = items.length;
      var min = 0;
      var max = length - 1;
      while (min <= max) {
         var mid = (min + max) >> 1;
         var k = items[mid];
         if (k === value) {
            return mid;
         }
         else if (k < value) {
            min = mid + 1;
         }
         else {
            max = mid - 1;
         }
      }

      return min;
   };

   var invalidateFactory = function (items) {
      return function (offsets, index, count) {
         var threshold = items.length,
             cursor = offsets.length,
             diff = Math.min(count, threshold + index) - cursor;

         for (var i = threshold - diff; i < threshold; i++) {
            var value = items[i]();
            if (cursor === 0) {
               offsets[cursor] = value;
            }
            else {
               offsets[cursor] = offsets[cursor - 1] + value;
            }

            cursor++;
         }
      };
   };

   var getPosition = function (offsets, value) {
      var index = findIndexAt(offsets, value);
      if (index > 0) {
         return {
            value: value,
            index: index,
            offset: offsets[index - 1]
         };
      }

      return {
         value: value,
         index: 0,
         offset: 0
      };
   };

   var Event = function () {
      var events = [];

      this.on = function (f) {
         events.push(f);
         return function () {
            var index = events.indexOf(f);
            if (index >= 0) {
               events.splice(index, 1);
            }
         }
      };

      this.emit = function (e) {
         var temp = angular.copy(events);
         for (var i = 0, length = temp.length; i < length; i++) {
            temp[i](e);
         }
      };
   };

   function vscrollPortCtrlFactory(contextFactory) {
      return function ($element) {
         this.markup = {};

         var self = this,
             items = [],
             max = 0,
             offsets = [],
             position = {index: 0, offset: 0, value: 0},
             invalidate = invalidateFactory(items),
             context = contextFactory($element, this.markup),
             move = context.move,
             getPosition = context.getPosition;

         var empty = function () {
            return 0;
         };

         this.update = function (count, view) {
            invalidate(offsets, position.index, count);
            position = getPosition(offsets, view);

            var offset = position.value - position.offset;
            if (offset >= 0) {
               max = Math.max(max, position.offset);
               var frame1 = Math.max(0, position.value - offset);
               var frame2 = Math.max(0, max - frame1);
               move(frame1, frame2);
            }

            return position.index;
         };

         this.invalidate = function (count, view) {
            max = 0;
            return self.update(count, view);
         };

         this.reset = function () {
            max = 0;
			items = [];
            offsets = [];
            position = {index: 0, offset: 0, value: 0};
            move(0, 0);
         };

         this.setItem = function (index, element) {
            items[index] = element;
         };

         this.removeItem = function (index) {
            if (index === 0) {
               items.shift();
               return;
            }

            var lastIndex = items.length - 1;
            if (index === lastIndex) {
               items.pop();
               return;
            }

            // TODO: think how to avoid this
            items[index] = empty;
         };
      };
   }

   function vscrollService($q) {
      return function (settings) {
         settings = angular.extend({
            threshold: 64,
            fetch: angular.noop
         }, settings);

         var container = {
            count: 0,
            total: 0,
            position: 0,
            cursor: 0,
            page: 0,
            items: [],
            force: true,
            resetEvent: new Event(),
            updateEvent: new Event(),

            apply: function (f, emit) {
               f();
               emit();
            },

            update: function (count, force) {
               var self = this,
                   threshold = settings.threshold,
                   cursor = self.cursor,
                   prevPage = self.page,
                   page = Math.ceil((cursor + threshold) / threshold) - 1;

               self.count = count;
               self.total = Math.max(self.total, count);
               if (force || page > prevPage) {
                  self.page = page;

                  var deferred = $q.defer();
                  deferred.promise
                      .then(function (count) {
                         self.total = count;
                         self.force = true;

                         self.updateEvent.emit({
                            force: angular.isUndefined(force) ? false : force
                         });
                      });

                  if (page === 0) {
                     settings.fetch(0, threshold, deferred);
                  }
                  else {
                     if (self.total < skip) {
                        deferred.resolve(self.total);
                     }
                     else {
                        var skip = (prevPage + 1) * threshold;
                        var take = (page - prevPage) * threshold;
                        settings.fetch(skip, take, deferred);
                     }
                  }

               }
            },

            reset: function () {
               this.count = 0;
               this.position = 0;
               this.cursor = 0;
               this.page = 0;
               this.items = [];

               this.force = true;
               this.resetEvent.emit();
               this.update(0, true);
            }
         };

         container.update(0, true);

         return {
            settings: settings,
            container: container
         };
      };
   }

   function vscrollFilter() {
      var empty = [];

      return function (data, context) {
         if (!data) {
            return empty;
         }

         if (!context) {
            throw new Error('vscroll filter context is not set');
         }

         var settings = context.settings,
             container = context.container,
             view = container.items,
             position = container.position,
             cursor = container.cursor,
             threshold = settings.threshold,
             count = data.length;

         container.update(count);

         if (count) {
            if (container.force ||
                (cursor <= count && cursor !== position)) {

               var first = Math.max(cursor + Math.min(count - (cursor + threshold), 0), cursor),
                   last = Math.min(cursor + threshold, count);

               container.position = cursor;
               view.length = last - first;
               for (var i = first, j = 0; i < last; i++ , j++) {
                  view[j] = data[i];
               }

               container.force = false;
            }

            return view;
         }

         return empty;
      };
   }

   function vscrollPortLinkFactory(type, canApply, $rootScope, $parse) {
      return function (scope, element, attrs, ctrls) {
         var context = $parse(attrs[type])(scope);
         if (!context) {
            throw Error('Context for ' + type + ' is not set');
         }

         var view = ctrls[0],
             port = ctrls[1],
             position = null,
             container = context.container;

         element[0].tabIndex = 0;
         element.css('outline', 'none');

         var scrollOff = view.scrollEvent.on(
             function (e) {
                if (canApply(e, position)) {
                   position = e;
                   if (container.count) {
                      container.apply(
                          function () {
                             container.cursor = port.update(container.count, e);
                          },
                          function () {
                             if (container.cursor !== container.position) {
                                if (!$rootScope.$$phase) {
                                   scope.$digest();
                                }
                             }
                          });
                   }
                }
             });

         var resetOff = container.resetEvent.on(
             function () {
                port.reset();
                view.reset();
             });

         var updateOff = container.updateEvent.on(
             function (e) {
                if (e.force) {
                   if (position) {
                      container.cursor = port.invalidate(container.count, position);
                   }
                }
             });

         scope.$on('$destroy', function () {
            delete port.markup;

            scrollOff();
            resetOff();
            updateOff();
         });
      };
   }

   function vscrollCtrl($scope, $element, $window) {
      var self = this,
          content = $element[0],
          window = angular.element($window);

      this.scrollEvent = new Event();
      this.reset = function () {
         content.scrollTop = 0;
      };

      var onScroll =
          function () {
             self.scrollEvent.emit({
                width: content.scrollWidth,
                height: content.scrollHeight,
                top: content.scrollTop,
                left: content.scrollLeft
             });
          };

      var onResize =
          function () {
             self.reset();
             onScroll();
          };

      $element.bind('scroll', onScroll);

      window.bind('resize', onResize);

      $scope.$on('$destroy', function () {
         $element.unbind('scroll', onScroll);
         window.unbind('resize', onResize);
      });
   }

   function vscrollDirective() {
      return {
         restrict: 'A',
         controller: vscrollCtrl
      };
   }

   function vscrollPortYDirective($rootScope, $parse) {
      return {
         restrict: 'A',
         controller: ['$element', vscrollPortCtrlFactory(function (element, markup) {
            var move = function (pos, value) {
               if (markup.hasOwnProperty(pos)) {
                  var mark = markup[pos];
                  mark.css('height', value + 'px');
               } else {
                  element.css('padding-' + pos, value + 'px');
               }
            };

            return {
               getPosition: function (offsets, view) {
                  return getPosition(offsets, view.top);
               },
               move: function (top, bottom) {
                  move('top', top);
                  move('bottom', bottom);
               }
            };
         })],
         require: ['^vscroll', 'vscrollPortY'],
         link: vscrollPortLinkFactory(
             'vscrollPortY',
             function (newValue, oldValue) {
                return !oldValue || newValue.top !== oldValue.top;
             },
             $rootScope,
             $parse)
      };
   }

   function vscrollPortXDirective($rootScope, $parse) {
      return {
         restrict: 'A',
         controller: ['$element', vscrollPortCtrlFactory(function (element, markup) {
            var move = function (pos, value) {
               if (markup.hasOwnProperty(pos)) {
                  var mark = markup[pos];
                  mark.css('width', value + 'px');
               } else {
                  element.css('padding-' + pos, value + 'px');
               }
            };

            return {
               getPosition: function (offsets, view) {
                  return getPosition(offsets, view.left);
               },
               move: function (left, right) {
                  move('left', left);
                  move('right', right);
               }
            };
         })],
         require: ['^vscroll', 'vscrollPortX'],
         link: vscrollPortLinkFactory(
             'vscrollPortX',
             function (newValue, oldValue) {
                return !oldValue || newValue.left !== oldValue.left;
             },
             $rootScope,
             $parse)
      };
   }

   function vscrollRowDirective() {
      return {
         restrict: 'A',
         require: '^vscrollPortY',
         link: function (scope, element, attrs, port) {
            var index = parseInt(attrs.vscrollRow);
            if (isNaN(index)) {
               throw new Error('Incorrect index "' + attrs.vscrollRow + '" for vscroll row');
            }

            var row = element[0],
                size = function () {
                   return getHeight(row);
                };

            port.setItem(index, size);
            scope.$on('$destroy', function () {
               port.removeItem(index);
            });
         }
      };
   }

   function vscrollColumnDirective() {
      return {
         restrict: 'A',
         require: '^vscrollPortX',
         link: function (scope, element, attrs, port) {
            var index = parseInt(attrs.vscrollColumn);
            if (isNaN(index)) {
               throw new Error('Incorrect index "' + attrs.vscrollColumn + '" for vscroll column')
            }

            var column = element[0],
                size = function () {
                   return getWidth(column);
                };

            port.setItem(index, size);
            scope.$on('$destroy', function () {
               port.removeItem(index);
            });
         }
      };
   }

   function vscrollMarkDirective() {
      return {
         restrict: 'A',
         require: ['^?vscrollPortX', '^?vscrollPortY'],
         link: function (scope, element, attrs, ctrls) {
            var ports = ctrls.filter(function (ctrl) {
               return ctrl;
            });

            angular.forEach(ports, function (port) {
               port.markup[attrs.vscrollMark] = element;
            });

            scope.$on('$destroy', function () {
               angular.forEach(ports, function (port) {
                  if (port.markup) {
                     port.markup[attrs.vscrollMark] = null;
                  }
               });
            });
         }
      };
   }
})
(angular);<commit_msg>Fix for empty elements in items collection<commit_after>(function (angular) {
   'use strict';

   vscrollService.$inject = ['$q'];
   vscrollCtrl.$inject = ['$scope', '$element', '$window'];
   vscrollPortYDirective.$inject = ['$rootScope', '$parse'];
   vscrollPortXDirective.$inject = ['$rootScope', '$parse'];

   angular.module('vscroll', [])
       .service('vscroll', vscrollService)
       .filter('vscroll', vscrollFilter)
       .directive('vscroll', vscrollDirective)
       .directive('vscrollPortY', vscrollPortYDirective)
       .directive('vscrollPortX', vscrollPortXDirective)
       .directive('vscrollRow', vscrollRowDirective)
       .directive('vscrollColumn', vscrollColumnDirective)
       .directive('vscrollMark', vscrollMarkDirective);

   var getHeight = function (element) {
      var height = element.offsetHeight,
          style = getComputedStyle(element);

      height += parseInt(style.marginTop) + parseInt(style.marginBottom);
      return height;
   };

   var getWidth = function (element) {
      var width = element.offsetWidth,
          style = getComputedStyle(element);

      width += parseInt(style.marginLeft) + parseInt(style.marginRight);
      return width;
   };

   var findIndexAt = function (items, value) {
      var length = items.length;
      var min = 0;
      var max = length - 1;
      while (min <= max) {
         var mid = (min + max) >> 1;
         var k = items[mid];
         if (k === value) {
            return mid;
         }
         else if (k < value) {
            min = mid + 1;
         }
         else {
            max = mid - 1;
         }
      }

      return min;
   };

   var invalidateFactory = function (items) {
      return function (offsets, index, count) {
         var threshold = items.length,
             cursor = offsets.length,
             diff = Math.min(count, threshold + index) - cursor;

         for (var i = threshold - diff; i < threshold; i++) {
            var value = items[i]();
            if (cursor === 0) {
               offsets[cursor] = value;
            }
            else {
               offsets[cursor] = offsets[cursor - 1] + value;
            }

            cursor++;
         }
      };
   };

   var getPosition = function (offsets, value) {
      var index = findIndexAt(offsets, value);
      if (index > 0) {
         return {
            value: value,
            index: index,
            offset: offsets[index - 1]
         };
      }

      return {
         value: value,
         index: 0,
         offset: 0
      };
   };

   var Event = function () {
      var events = [];

      this.on = function (f) {
         events.push(f);
         return function () {
            var index = events.indexOf(f);
            if (index >= 0) {
               events.splice(index, 1);
            }
         }
      };

      this.emit = function (e) {
         var temp = angular.copy(events);
         for (var i = 0, length = temp.length; i < length; i++) {
            temp[i](e);
         }
      };
   };

   function vscrollPortCtrlFactory(contextFactory) {
      return function ($element) {
         this.markup = {};

         var self = this,
             items = [],
             max = 0,
             offsets = [],
             position = {index: 0, offset: 0, value: 0},
             invalidate = invalidateFactory(items),
             context = contextFactory($element, this.markup),
             move = context.move,
             getPosition = context.getPosition;

         var empty = function () {
            return 0;
         };

         this.update = function (count, view) {
            invalidate(offsets, position.index, count);
            position = getPosition(offsets, view);

            var offset = position.value - position.offset;
            if (offset >= 0) {
               max = Math.max(max, position.offset);
               var frame1 = Math.max(0, position.value - offset);
               var frame2 = Math.max(0, max - frame1);
               move(frame1, frame2);
            }

            return position.index;
         };

         this.invalidate = function (count, view) {
            max = 0;
            return self.update(count, view);
         };

         this.reset = function () {
            max = 0;
			items = [];
            offsets = [];
            position = {index: 0, offset: 0, value: 0};
            move(0, 0);
         };

         this.setItem = function (index, element) {
            items[index] = element;
         };

         this.removeItem = function (index) {
            if (index === 0) {
               items.shift();
               return;
            }

            var length = items.length - 1;
            if (index === length) {
               items.pop();
               while (length-- && items[length] === empty) {
                  items.pop();
               }
               return;
            }

            // TODO: think how to avoid this
            items[index] = empty;
         };
      };
   }

   function vscrollService($q) {
      return function (settings) {
         settings = angular.extend({
            threshold: 64,
            fetch: angular.noop
         }, settings);

         var container = {
            count: 0,
            total: 0,
            position: 0,
            cursor: 0,
            page: 0,
            items: [],
            force: true,
            resetEvent: new Event(),
            updateEvent: new Event(),

            apply: function (f, emit) {
               f();
               emit();
            },

            update: function (count, force) {
               var self = this,
                   threshold = settings.threshold,
                   cursor = self.cursor,
                   prevPage = self.page,
                   page = Math.ceil((cursor + threshold) / threshold) - 1;

               self.count = count;
               self.total = Math.max(self.total, count);
               if (force || page > prevPage) {
                  self.page = page;

                  var deferred = $q.defer();
                  deferred.promise
                      .then(function (count) {
                         self.total = count;
                         self.force = true;

                         self.updateEvent.emit({
                            force: angular.isUndefined(force) ? false : force
                         });
                      });

                  if (page === 0) {
                     settings.fetch(0, threshold, deferred);
                  }
                  else {
                     if (self.total < skip) {
                        deferred.resolve(self.total);
                     }
                     else {
                        var skip = (prevPage + 1) * threshold;
                        var take = (page - prevPage) * threshold;
                        settings.fetch(skip, take, deferred);
                     }
                  }

               }
            },

            reset: function () {
               this.count = 0;
               this.position = 0;
               this.cursor = 0;
               this.page = 0;
               this.items = [];

               this.force = true;
               this.resetEvent.emit();
               this.update(0, true);
            }
         };

         container.update(0, true);

         return {
            settings: settings,
            container: container
         };
      };
   }

   function vscrollFilter() {
      var empty = [];

      return function (data, context) {
         if (!data) {
            return empty;
         }

         if (!context) {
            throw new Error('vscroll filter context is not set');
         }

         var settings = context.settings,
             container = context.container,
             view = container.items,
             position = container.position,
             cursor = container.cursor,
             threshold = settings.threshold,
             count = data.length;

         container.update(count);

         if (count) {
            if (container.force ||
                (cursor <= count && cursor !== position)) {

               var first = Math.max(cursor + Math.min(count - (cursor + threshold), 0), cursor),
                   last = Math.min(cursor + threshold, count);

               container.position = cursor;
               view.length = last - first;
               for (var i = first, j = 0; i < last; i++ , j++) {
                  view[j] = data[i];
               }

               container.force = false;
            }

            return view;
         }

         return empty;
      };
   }

   function vscrollPortLinkFactory(type, canApply, $rootScope, $parse) {
      return function (scope, element, attrs, ctrls) {
         var context = $parse(attrs[type])(scope);
         if (!context) {
            throw Error('Context for ' + type + ' is not set');
         }

         var view = ctrls[0],
             port = ctrls[1],
             position = null,
             container = context.container;

         element[0].tabIndex = 0;
         element.css('outline', 'none');

         var scrollOff = view.scrollEvent.on(
             function (e) {
                if (canApply(e, position)) {
                   position = e;
                   if (container.count) {
                      container.apply(
                          function () {
                             container.cursor = port.update(container.count, e);
                          },
                          function () {
                             if (container.cursor !== container.position) {
                                if (!$rootScope.$$phase) {
                                   scope.$digest();
                                }
                             }
                          });
                   }
                }
             });

         var resetOff = container.resetEvent.on(
             function () {
                port.reset();
                view.reset();
             });

         var updateOff = container.updateEvent.on(
             function (e) {
                if (e.force) {
                   if (position) {
                      container.cursor = port.invalidate(container.count, position);
                   }
                }
             });

         scope.$on('$destroy', function () {
            delete port.markup;

            scrollOff();
            resetOff();
            updateOff();
         });
      };
   }

   function vscrollCtrl($scope, $element, $window) {
      var self = this,
          content = $element[0],
          window = angular.element($window);

      this.scrollEvent = new Event();
      this.reset = function () {
         content.scrollTop = 0;
      };

      var onScroll =
          function () {
             self.scrollEvent.emit({
                width: content.scrollWidth,
                height: content.scrollHeight,
                top: content.scrollTop,
                left: content.scrollLeft
             });
          };

      var onResize =
          function () {
             self.reset();
             onScroll();
          };

      $element.bind('scroll', onScroll);

      window.bind('resize', onResize);

      $scope.$on('$destroy', function () {
         $element.unbind('scroll', onScroll);
         window.unbind('resize', onResize);
      });
   }

   function vscrollDirective() {
      return {
         restrict: 'A',
         controller: vscrollCtrl
      };
   }

   function vscrollPortYDirective($rootScope, $parse) {
      return {
         restrict: 'A',
         controller: ['$element', vscrollPortCtrlFactory(function (element, markup) {
            var move = function (pos, value) {
               if (markup.hasOwnProperty(pos)) {
                  var mark = markup[pos];
                  mark.css('height', value + 'px');
               } else {
                  element.css('padding-' + pos, value + 'px');
               }
            };

            return {
               getPosition: function (offsets, view) {
                  return getPosition(offsets, view.top);
               },
               move: function (top, bottom) {
                  move('top', top);
                  move('bottom', bottom);
               }
            };
         })],
         require: ['^vscroll', 'vscrollPortY'],
         link: vscrollPortLinkFactory(
             'vscrollPortY',
             function (newValue, oldValue) {
                return !oldValue || newValue.top !== oldValue.top;
             },
             $rootScope,
             $parse)
      };
   }

   function vscrollPortXDirective($rootScope, $parse) {
      return {
         restrict: 'A',
         controller: ['$element', vscrollPortCtrlFactory(function (element, markup) {
            var move = function (pos, value) {
               if (markup.hasOwnProperty(pos)) {
                  var mark = markup[pos];
                  mark.css('width', value + 'px');
               } else {
                  element.css('padding-' + pos, value + 'px');
               }
            };

            return {
               getPosition: function (offsets, view) {
                  return getPosition(offsets, view.left);
               },
               move: function (left, right) {
                  move('left', left);
                  move('right', right);
               }
            };
         })],
         require: ['^vscroll', 'vscrollPortX'],
         link: vscrollPortLinkFactory(
             'vscrollPortX',
             function (newValue, oldValue) {
                return !oldValue || newValue.left !== oldValue.left;
             },
             $rootScope,
             $parse)
      };
   }

   function vscrollRowDirective() {
      return {
         restrict: 'A',
         require: '^vscrollPortY',
         link: function (scope, element, attrs, port) {
            var index = parseInt(attrs.vscrollRow);
            if (isNaN(index)) {
               throw new Error('Incorrect index "' + attrs.vscrollRow + '" for vscroll row');
            }

            var row = element[0],
                size = function () {
                   return getHeight(row);
                };

            port.setItem(index, size);
            scope.$on('$destroy', function () {
               port.removeItem(index);
            });
         }
      };
   }

   function vscrollColumnDirective() {
      return {
         restrict: 'A',
         require: '^vscrollPortX',
         link: function (scope, element, attrs, port) {
            var index = parseInt(attrs.vscrollColumn);
            if (isNaN(index)) {
               throw new Error('Incorrect index "' + attrs.vscrollColumn + '" for vscroll column')
            }

            var column = element[0],
                size = function () {
                   return getWidth(column);
                };

            port.setItem(index, size);
            scope.$on('$destroy', function () {
               port.removeItem(index);
            });
         }
      };
   }

   function vscrollMarkDirective() {
      return {
         restrict: 'A',
         require: ['^?vscrollPortX', '^?vscrollPortY'],
         link: function (scope, element, attrs, ctrls) {
            var ports = ctrls.filter(function (ctrl) {
               return ctrl;
            });

            angular.forEach(ports, function (port) {
               port.markup[attrs.vscrollMark] = element;
            });

            scope.$on('$destroy', function () {
               angular.forEach(ports, function (port) {
                  if (port.markup) {
                     port.markup[attrs.vscrollMark] = null;
                  }
               });
            });
         }
      };
   }
})
(angular);<|endoftext|>


<commit_before>module.exports = function(grunt) {
  /*
   * Include grunt configuration
   */
  var gruntConfig = require( './config/grunt.conf.js' );

  /*
   * Helpers (preparing for grunt util deprecation)
   */
  var glob = require('glob');

  /*
   * Time grunt execution
   */
  require('time-grunt')(grunt);

  /*
   * JIT Grunt will automatically load other npm tasks
   */
  require('jit-grunt')(grunt);
  grunt.loadNpmTasks('grunt-protractor-runner');

  /*
   * Configuration
   */
  var gruntTasks = {
    pkg: grunt.file.readJSON('package.json'),
    /*
     * Placed at the top of the compiled source files.
     * Use the 'banner' -> banner: '<%= meta.banner %>'
     *
     */
    meta: {
      banner:
        '/*\n' +
        ' * Compiled <%= grunt.template.today("yyyy-mm-dd") %>\n' +
        ' * Docs @ <%= pkg.repository.url %>\n' +
        ' */\n'
    },
    /*
     * The shell task can run handy shell commands.
     * To configure the dev/prod server check out the 'sh' files
     * in './server'.
     *
     */
    shell: {
      dev_server: {
        command: './server/build-server.sh'
      },
      prod_server: {
        command: './server/production-server.sh'
      },
      run_selenium: {
        command: './node_modules/protractor/bin/webdriver-manager start'
      },
      update_selenium: {
        command: './node_modules/protractor/bin/webdriver-manager update'
      }
    },
    /*
     * The copy task copies files for both build and compile
     * phases, as well as test files.
     *
     * 'build_index' will process the 'index.html' file and write
     * <script> tags for all files (modules or vendor scripts).
     *
     * 'compile_index' will process the 'index.html' file without
     * providing any scripts. In this case, a single script will
     * be included: 'app.min.js'.
     *
     * See './src/index.html' for more details.
     *
     */
    copy: {
      build_app_js: {
        src: [
          findModuleFilesIn('./src/app/'),
          findModuleFilesIn('./src/common/')
        ],
        dest: '<%= build_dir %>'
      },
      build_app_data: {
        src: ['./src/app/**/*.json'],
        dest: '<%= build_dir %>/data/',
        expand: true,
        flatten: true,
        filter: 'isFile'
      },
      build_unit: {
        src: ['./src/**/*.spec.js'],
        dest: '<%= build_dir %>'
      },
      build_vendor_js: {
        src: ['<%= build.vendor_js %>'],
        dest: '<%= build_dir %>'
      },
      build_views: {
        cwd: './src',
        src: ['app/**/*.html', '!app/shared/**/*.html'],
        dest: '<%= build_dir %>/views/',
        expand: true,
        flatten: true,
        filter: 'isFile'
      },
      build_shared_views: {
        cwd: './src',
        src: ['app/shared/**/*.html'],
        dest: '<%= build_dir %>/shared-views/',
        expand: true,
        flatten: true,
        filter: 'isFile'
      },
      build_index: {
        cwd: './src',
        src: ['index.html'],
        dest: '<%= build_dir %>',
        expand: true,
        options: {
          process: processBuildScripts
        }
      },
      build_assets: {
        cwd: './src/assets',
        src: [
          'img/**/*',
          'fonts/**/*',
          '!**/*.less',
          '!**/*.css'
        ],
        dest: '<%= build_dir %>src/assets/',
        expand: true
      },
      build_fonts: {
        src: ['<%= common.vendor_fonts %>'],
        dest: '<%= build_dir %>src/assets/fonts/',
        expand: true,
        flatten: true,
        filter: 'isFile'
      },
      build_karma: {
        src: ['./config/karma.conf.js'],
        dest: '<%= build_dir %>',
        options: {
          process: processBuildScripts
        }
      },
      build_protractor: {
        cwd: './src',
        src: ['**/*.protractor.js', '**/*.e2e.js'],
        dest: '<%= build_dir %>src/e2e/',
        expand: true,
        flatten: true
      },
      compile_app_data: {
        cwd: '<%= build_dir %>',
        src: ['data/*.json'],
        dest: '<%= compile_dir %>',
        expand: true
      },
      compile_views: {
        cwd: '<%= build_dir %>',
        src: ['views/**/*.html'],
        dest: '<%= compile_dir %>',
        expand: true
      },
      compile_index: {
        cwd: './src',
        src: ['index.html'],
        dest: '<%= compile_dir %>',
        expand: true,
        options: {
          process: function processCompileIndex(content){
            return grunt.template.process(content);
          }
        }
      },
      compile_app_assets: {
        cwd: '<%= build_dir %>',
        src: ['src/assets/**/*', '!src/assets/**/*.md'],
        dest: '<%= compile_dir %>',
        expand: true
      }
    },
    /*
     * The clean task is used to remove files / directories that
     * are used and not needed anymore.
     *
     * It's also used to remove the build directory before a new
     * compile, see 'build_clean' below.
     *
     */
    clean: {
      build_clean: ['<%= build_dir %>'],
      build_css_clean: ['<%= build_dir %>/src/assets/css/app.custom.css'],
      compile_js_clean: ['<%= compile_dir %>/src/app/modules.min.js'],
      compile_shared_views_clean: ['<%= compile_dir %>/shared-views/'],
      clean_samples: [
        './src/app/about/',
        './src/app/home/',
        './src/app/profile-feature/',
        './src/app//shared/profile-model'
      ]
    },
    /*
     * The less task compiles all less files.
     * They will be afterwards concatenated with vendor
     * stylesheet, see the 'cssmin' task.
     *
     */
    less: {
      build_less: {
        src: ['./src/assets/**/*.less'],
        dest: '<%= build_dir %>/src/assets/css/app.custom.css',
        options: {
          cleancss: true,
          compress: true
        }
      }
    },
    /*
     * The cssmin task minifies css files and merges them into a
     * single file (done during build).
     *
     */
    cssmin: {
      build_css: {
        src: [
          '<%= build.vendor_css %>',
          './src/assets/**/*.css',
          '<%= build_dir %>/src/assets/css/app.custom.css'
        ],
        dest: '<%= build_dir %>/src/assets/css/app.min.css',
        options: {
          keepSpecialComments: 0,
          banner: '<%= meta.banner %>'
        }
      }
    },
    /*
     * This task autoprefixes CSS
     * You won't have to ever add -webkit-, -moz-, etc
     * in your CSS files EVER!
     *
     */
    autoprefixer: {
      options: {
        browsers: ['> 1%']
      },
      build_autoprefix_css: {
        src: '<%= build_dir %>/src/assets/css/app.min.css',
        dest: '<%= build_dir %>/src/assets/css/app.min.css'
      }
    },
    /*
     * The uglify task mainly minifies and mangles module scripts.
     * After all scripts are concatenated, it minifies without
     * mangling to remove comments, line breaks, etc that come
     * from vendor scripts.
     *
     */
    uglify: {
      compile_module_js: {
        options: {
          wrap: 'moduleExports',
          mangle: {
            except: ['exceptionLoggingService']
          }
        },
        src: [
          '<%= compile.vendor_js %>',
          findModuleFilesIn('./build/src/app/'),
          findModuleFilesIn('./build/src/common/')
        ],
        dest: '<%= compile_dir %>/src/app/modules.min.js'
      },
      compile_soft_uglify: {
        options: {
          mangle: false,
          banner: '<%= meta.banner %>'
        },
        src: ['<%= compile_dir %>/src/app/app.min.js'],
        dest: '<%= compile_dir %>/src/app/app.min.js'
      }
    },
    /*
     * The concat task is used in the compile phase to merge all
     * scripts into one.
     *
     */
    concat: {
      compile_js: {
        src: [
          '<%= compile.vendor_min_js %>',
          '<%= compile_dir %>/src/app/modules.min.js',
          '<%= compile_dir %>/shared-views/views.min.js'
        ],
        dest: '<%= compile_dir %>/src/app/app.min.js',
        options: {
          banner: '<%= meta.banner %>'
        }
      }
    },
    /*
     * The html2js task is used to convert all shared views into a module
     * that adds them to $templateCache. It also minifies HTML
     * markup.
     *
     */
    html2js: {
      options: {
        htmlmin: {
          collapseBooleanAttributes: false,
          collapseWhitespace: true,
          removeAttributeQuotes: false,
          removeComments: true,
          removeEmptyAttributes: false,
          removeRedundantAttributes: true,
          removeScriptTypeAttributes: true,
          removeStyleLinkTypeAttributes: true
        }
      },
      compile_shared_views: {
        src: ['<%= build_dir %>/shared-views/**/*.html'],
        dest: '<%= compile_dir %>/shared-views/views.min.js',
        options: {
          module: 'sharedViewsModule',
          base: './build',
          singleModule: true
        }
      }
    },
    /*
     * The jshint task detects errors and potential problems in
     * the JavaScript source and grunt files.
     *
     */
    jshint: {
      options: {
        jshintrc: true
      },
      src_js: ['./src/**/*.js'],
      gruntfiles: ['./gruntfile.js', 'grunt.conf.js']
    },
    /*
     * Karma is used to run unit tests.
     * It can run continiously (similar to the watch task).
     *
     */
    karma: {
      options: {
        configFile: '<%= build_dir %>/config/karma.conf.js'
      },
      unit: {
        port: 9191,
        background: false
      },
      continuous: {
        singleRun: false,
        autoWatch: true
      }
    },
    /*
     * Protractor is used to run E2E tests. (need a running
     * selenium server).
     *
     * To run locally, 'grunt test', 'grunt test:e2e' and
     * 'grunt protractor:build' will do the job.
     *
     * It also contains configuration for Travis CI.
     * Both '/build' (development) and '/application' (
     * production) paths are covered.
     *
     */
    protractor: {
      options: {
        keepAlive: false, // the grunt process stops when the test fails.
        noColor: false,
        args: {}
      },
      build: {
        options: {
          keepAlive: true,
          configFile: './config/protractor.conf.js', //Local tests
        }
      },
      ci_dev: {
        options: {
          configFile: './config/protractor-ci.conf.js'
        }
      },
      ci_prod: {
        options: {
          configFile: './config/protractor-ci-prod.conf.js'
        }
      }
    },
    /*
     * The `watch` task builds JavaScript, HTML and all assets
     * (including tests and the gruntfiles themselves) when any
     * of them are changed.
     *
     */
    watch: {
      options: {
        livereload: true
      },
      src_js: {
        files: ['./src/**/*.js', './src/**/*.json'],
        tasks: ['newer:jshint:src_js', 'newer:copy:build_app_js']
      },
      src_html: {
        files: ['./src/**/*.html', '!./src/index.html'],
        tasks: ['newer:copy:build_views', 'copy:build_shared_views']
      },
      src_index: {
        files: ['./src/index.html'],
        tasks: ['copy:build_index']
      },
      src_stylesheets: {
        files: ['./src/assets/**/*.less', './src/assets/**/*.css'],
        tasks: ['newer:less:build_less', 'cssmin:build_css', 'clean:build_css_clean', 'autoprefixer:build_autoprefix_css']
      },
      src_assets: {
        files: ['./src/assets/**/*', '!./src/assets/**/*.less', '!./src/assets/**/*.css'],
        tasks: ['newer:copy:build_assets']
      },
      gruntfiles: {
        files: ['./gruntfile.js', 'grunt.conf.js'],
        tasks: ['newer:jshint:gruntfiles'],
        options: {
          livereload: false
        }
      },
      tests: {
        files: ['src/**/*.spec.js', 'src/**/*.e2e.js', 'src/**/*.protractor.js'],
        tasks: ['newer:copy:build_karma', 'newer:copy:build_unit', 'newer:copy:build_protractor']
      }
    }
  };

  /*
   * When initializing Grunt, it needs to be extended with the
   * 'gruntConfig' object (imported from './config/grunt.conf.js')
   *
   */
  grunt.initConfig(
    grunt.util._.extend(gruntTasks, gruntConfig)
  );

  /*
   * Tasks.
   * Running 'grunt' should be enough for development.
   * When ready for production, 'grunt compile' will compile the
   * application into the './application' directory.
   *
   */
  grunt.registerTask('default', ['build', 'karma:unit', 'watch']);
  grunt.registerTask('build', [
    'jshint',
    'copy:build_app_js',
    'copy:build_app_data',
    'copy:build_vendor_js',
    'copy:build_views',
    'copy:build_shared_views',
    'copy:build_fonts',
    'copy:build_index',
    'copy:build_unit',
    'copy:build_protractor',
    'copy:build_karma',
    'copy:build_assets',
    'less:build_less',
    'cssmin:build_css',
    'clean:build_css_clean',
    'autoprefixer:build_autoprefix_css'
  ]);
  grunt.registerTask('compile', [
    'jshint',
    'clean:build_clean',
    'build',
    'copy:compile_views',
    'html2js:compile_shared_views',
    'uglify:compile_module_js',
    'concat:compile_js',
    'uglify:compile_soft_uglify',
    'clean:compile_js_clean',
    'clean:compile_shared_views_clean',
    'copy:compile_app_data',
    'copy:compile_app_assets',
    'copy:compile_index'
  ]);
  grunt.registerTask('test', ['karma:unit', 'protractor:build']);
  grunt.registerTask('test:unit', ['karma:unit']);
  grunt.registerTask('test:ci', ['karma:unit', 'protractor:ci_dev', 'protractor:ci_prod']);
  grunt.registerTask('test:e2e', ['protractor:build']);
  grunt.registerTask('dev', ['shell:dev_server']);
  grunt.registerTask('prod', ['shell:prod_server']);

  /*
   * Helper methods.
   * The methods below use the 'module_file_order' Array in
   * 'grunt.conf.js' to easily load modules in a consistent
   * manner, generate globbing patterns or process files.
   *
   * (i.e. the 'index.html' is processed in order to
   * automatically write <script> tags)
   *
   */

  /*
   * Generates a globbing pattern that matches the
   * 'module_file_order' Array in 'grunt.conf.js'.
   *
   * @param {String} modulePath - The module path
   * @returns {Array} output - An Array of globbing patterns
   *
   * (i.e. findModuleFilesIn('./src/app/') loads all module
   * files in './src/app' in the defined order)
   *
   */
  function findModuleFilesIn(modulePath){
    var output = [],
        i;

    for(i = 0; i <= gruntConfig.module_file_order.length-1; i++){
      output.push(modulePath + gruntConfig.module_file_order[i]);
    }

    return output;
  }

  /*
   * 'Manually globs (using 'node-glob')' for all JavaScript files
   * that should be included in the build phase, then stores them
   * into the 'buildScripts' Array. 'buildScripts' is then passed * as 'scripts' to the template.
   * The template is processed (usually loops through 'scripts').
   * (See 'index.html')
   *
   * @param {String} content - File content
   * @returns {String} - Processed content
   *
   */
  function processBuildScripts(content){
    var buildScripts = [],
        globFiles,
        customModules = gruntConfig.build.vendor_js
                        .concat(findModuleFilesIn('./src/app/'))
                        .concat(findModuleFilesIn('./src/common/'));

    customModules.forEach(function(module){
      globFiles = glob.sync(module);

      if(globFiles.length > 0){
        buildScripts = buildScripts.concat(globFiles);
      }
    });

    return grunt.template.process(content, {
      data: {
        scripts: buildScripts
      }
    });
  }
};<commit_msg>fix(gruntfile.js): watch will now copy json files on change<commit_after>module.exports = function(grunt) {
  /*
   * Include grunt configuration
   */
  var gruntConfig = require( './config/grunt.conf.js' );

  /*
   * Helpers (preparing for grunt util deprecation)
   */
  var glob = require('glob');

  /*
   * Time grunt execution
   */
  require('time-grunt')(grunt);

  /*
   * JIT Grunt will automatically load other npm tasks
   */
  require('jit-grunt')(grunt);
  grunt.loadNpmTasks('grunt-protractor-runner');

  /*
   * Configuration
   */
  var gruntTasks = {
    pkg: grunt.file.readJSON('package.json'),
    /*
     * Placed at the top of the compiled source files.
     * Use the 'banner' -> banner: '<%= meta.banner %>'
     *
     */
    meta: {
      banner:
        '/*\n' +
        ' * Compiled <%= grunt.template.today("yyyy-mm-dd") %>\n' +
        ' * Docs @ <%= pkg.repository.url %>\n' +
        ' */\n'
    },
    /*
     * The shell task can run handy shell commands.
     * To configure the dev/prod server check out the 'sh' files
     * in './server'.
     *
     */
    shell: {
      dev_server: {
        command: './server/build-server.sh'
      },
      prod_server: {
        command: './server/production-server.sh'
      },
      run_selenium: {
        command: './node_modules/protractor/bin/webdriver-manager start'
      },
      update_selenium: {
        command: './node_modules/protractor/bin/webdriver-manager update'
      }
    },
    /*
     * The copy task copies files for both build and compile
     * phases, as well as test files.
     *
     * 'build_index' will process the 'index.html' file and write
     * <script> tags for all files (modules or vendor scripts).
     *
     * 'compile_index' will process the 'index.html' file without
     * providing any scripts. In this case, a single script will
     * be included: 'app.min.js'.
     *
     * See './src/index.html' for more details.
     *
     */
    copy: {
      build_app_js: {
        src: [
          findModuleFilesIn('./src/app/'),
          findModuleFilesIn('./src/common/')
        ],
        dest: '<%= build_dir %>'
      },
      build_app_data: {
        src: ['./src/app/**/*.json'],
        dest: '<%= build_dir %>/data/',
        expand: true,
        flatten: true,
        filter: 'isFile'
      },
      build_unit: {
        src: ['./src/**/*.spec.js'],
        dest: '<%= build_dir %>'
      },
      build_vendor_js: {
        src: ['<%= build.vendor_js %>'],
        dest: '<%= build_dir %>'
      },
      build_views: {
        cwd: './src',
        src: ['app/**/*.html', '!app/shared/**/*.html'],
        dest: '<%= build_dir %>/views/',
        expand: true,
        flatten: true,
        filter: 'isFile'
      },
      build_shared_views: {
        cwd: './src',
        src: ['app/shared/**/*.html'],
        dest: '<%= build_dir %>/shared-views/',
        expand: true,
        flatten: true,
        filter: 'isFile'
      },
      build_index: {
        cwd: './src',
        src: ['index.html'],
        dest: '<%= build_dir %>',
        expand: true,
        options: {
          process: processBuildScripts
        }
      },
      build_assets: {
        cwd: './src/assets',
        src: [
          'img/**/*',
          'fonts/**/*',
          '!**/*.less',
          '!**/*.css'
        ],
        dest: '<%= build_dir %>src/assets/',
        expand: true
      },
      build_fonts: {
        src: ['<%= common.vendor_fonts %>'],
        dest: '<%= build_dir %>src/assets/fonts/',
        expand: true,
        flatten: true,
        filter: 'isFile'
      },
      build_karma: {
        src: ['./config/karma.conf.js'],
        dest: '<%= build_dir %>',
        options: {
          process: processBuildScripts
        }
      },
      build_protractor: {
        cwd: './src',
        src: ['**/*.protractor.js', '**/*.e2e.js'],
        dest: '<%= build_dir %>src/e2e/',
        expand: true,
        flatten: true
      },
      compile_app_data: {
        cwd: '<%= build_dir %>',
        src: ['data/*.json'],
        dest: '<%= compile_dir %>',
        expand: true
      },
      compile_views: {
        cwd: '<%= build_dir %>',
        src: ['views/**/*.html'],
        dest: '<%= compile_dir %>',
        expand: true
      },
      compile_index: {
        cwd: './src',
        src: ['index.html'],
        dest: '<%= compile_dir %>',
        expand: true,
        options: {
          process: function processCompileIndex(content){
            return grunt.template.process(content);
          }
        }
      },
      compile_app_assets: {
        cwd: '<%= build_dir %>',
        src: ['src/assets/**/*', '!src/assets/**/*.md'],
        dest: '<%= compile_dir %>',
        expand: true
      }
    },
    /*
     * The clean task is used to remove files / directories that
     * are used and not needed anymore.
     *
     * It's also used to remove the build directory before a new
     * compile, see 'build_clean' below.
     *
     */
    clean: {
      build_clean: ['<%= build_dir %>'],
      build_css_clean: ['<%= build_dir %>/src/assets/css/app.custom.css'],
      compile_js_clean: ['<%= compile_dir %>/src/app/modules.min.js'],
      compile_shared_views_clean: ['<%= compile_dir %>/shared-views/'],
      clean_samples: [
        './src/app/about/',
        './src/app/home/',
        './src/app/profile-feature/',
        './src/app//shared/profile-model'
      ]
    },
    /*
     * The less task compiles all less files.
     * They will be afterwards concatenated with vendor
     * stylesheet, see the 'cssmin' task.
     *
     */
    less: {
      build_less: {
        src: ['./src/assets/**/*.less'],
        dest: '<%= build_dir %>/src/assets/css/app.custom.css',
        options: {
          cleancss: true,
          compress: true
        }
      }
    },
    /*
     * The cssmin task minifies css files and merges them into a
     * single file (done during build).
     *
     */
    cssmin: {
      build_css: {
        src: [
          '<%= build.vendor_css %>',
          './src/assets/**/*.css',
          '<%= build_dir %>/src/assets/css/app.custom.css'
        ],
        dest: '<%= build_dir %>/src/assets/css/app.min.css',
        options: {
          keepSpecialComments: 0,
          banner: '<%= meta.banner %>'
        }
      }
    },
    /*
     * This task autoprefixes CSS
     * You won't have to ever add -webkit-, -moz-, etc
     * in your CSS files EVER!
     *
     */
    autoprefixer: {
      options: {
        browsers: ['> 1%']
      },
      build_autoprefix_css: {
        src: '<%= build_dir %>/src/assets/css/app.min.css',
        dest: '<%= build_dir %>/src/assets/css/app.min.css'
      }
    },
    /*
     * The uglify task mainly minifies and mangles module scripts.
     * After all scripts are concatenated, it minifies without
     * mangling to remove comments, line breaks, etc that come
     * from vendor scripts.
     *
     */
    uglify: {
      compile_module_js: {
        options: {
          wrap: 'moduleExports',
          mangle: {
            except: ['exceptionLoggingService']
          }
        },
        src: [
          '<%= compile.vendor_js %>',
          findModuleFilesIn('./build/src/app/'),
          findModuleFilesIn('./build/src/common/')
        ],
        dest: '<%= compile_dir %>/src/app/modules.min.js'
      },
      compile_soft_uglify: {
        options: {
          mangle: false,
          banner: '<%= meta.banner %>'
        },
        src: ['<%= compile_dir %>/src/app/app.min.js'],
        dest: '<%= compile_dir %>/src/app/app.min.js'
      }
    },
    /*
     * The concat task is used in the compile phase to merge all
     * scripts into one.
     *
     */
    concat: {
      compile_js: {
        src: [
          '<%= compile.vendor_min_js %>',
          '<%= compile_dir %>/src/app/modules.min.js',
          '<%= compile_dir %>/shared-views/views.min.js'
        ],
        dest: '<%= compile_dir %>/src/app/app.min.js',
        options: {
          banner: '<%= meta.banner %>'
        }
      }
    },
    /*
     * The html2js task is used to convert all shared views into a module
     * that adds them to $templateCache. It also minifies HTML
     * markup.
     *
     */
    html2js: {
      options: {
        htmlmin: {
          collapseBooleanAttributes: false,
          collapseWhitespace: true,
          removeAttributeQuotes: false,
          removeComments: true,
          removeEmptyAttributes: false,
          removeRedundantAttributes: true,
          removeScriptTypeAttributes: true,
          removeStyleLinkTypeAttributes: true
        }
      },
      compile_shared_views: {
        src: ['<%= build_dir %>/shared-views/**/*.html'],
        dest: '<%= compile_dir %>/shared-views/views.min.js',
        options: {
          module: 'sharedViewsModule',
          base: './build',
          singleModule: true
        }
      }
    },
    /*
     * The jshint task detects errors and potential problems in
     * the JavaScript source and grunt files.
     *
     */
    jshint: {
      options: {
        jshintrc: true
      },
      src_js: ['./src/**/*.js'],
      gruntfiles: ['./gruntfile.js', 'grunt.conf.js']
    },
    /*
     * Karma is used to run unit tests.
     * It can run continiously (similar to the watch task).
     *
     */
    karma: {
      options: {
        configFile: '<%= build_dir %>/config/karma.conf.js'
      },
      unit: {
        port: 9191,
        background: false
      },
      continuous: {
        singleRun: false,
        autoWatch: true
      }
    },
    /*
     * Protractor is used to run E2E tests. (need a running
     * selenium server).
     *
     * To run locally, 'grunt test', 'grunt test:e2e' and
     * 'grunt protractor:build' will do the job.
     *
     * It also contains configuration for Travis CI.
     * Both '/build' (development) and '/application' (
     * production) paths are covered.
     *
     */
    protractor: {
      options: {
        keepAlive: false, // the grunt process stops when the test fails.
        noColor: false,
        args: {}
      },
      build: {
        options: {
          keepAlive: true,
          configFile: './config/protractor.conf.js', //Local tests
        }
      },
      ci_dev: {
        options: {
          configFile: './config/protractor-ci.conf.js'
        }
      },
      ci_prod: {
        options: {
          configFile: './config/protractor-ci-prod.conf.js'
        }
      }
    },
    /*
     * The `watch` task builds JavaScript, HTML and all assets
     * (including tests and the gruntfiles themselves) when any
     * of them are changed.
     *
     */
    watch: {
      options: {
        livereload: true
      },
      src_js: {
        files: ['./src/**/*.js', './src/**/*.json'],
        tasks: ['newer:jshint:src_js', 'newer:copy:build_app_js', 'copy:build_app_data']
      },
      src_html: {
        files: ['./src/**/*.html', '!./src/index.html'],
        tasks: ['newer:copy:build_views', 'copy:build_shared_views']
      },
      src_index: {
        files: ['./src/index.html'],
        tasks: ['copy:build_index']
      },
      src_stylesheets: {
        files: ['./src/assets/**/*.less', './src/assets/**/*.css'],
        tasks: ['newer:less:build_less', 'cssmin:build_css', 'clean:build_css_clean', 'autoprefixer:build_autoprefix_css']
      },
      src_assets: {
        files: ['./src/assets/**/*', '!./src/assets/**/*.less', '!./src/assets/**/*.css'],
        tasks: ['newer:copy:build_assets']
      },
      gruntfiles: {
        files: ['./gruntfile.js', 'grunt.conf.js'],
        tasks: ['newer:jshint:gruntfiles'],
        options: {
          livereload: false
        }
      },
      tests: {
        files: ['src/**/*.spec.js', 'src/**/*.e2e.js', 'src/**/*.protractor.js'],
        tasks: ['newer:copy:build_karma', 'newer:copy:build_unit', 'newer:copy:build_protractor']
      }
    }
  };

  /*
   * When initializing Grunt, it needs to be extended with the
   * 'gruntConfig' object (imported from './config/grunt.conf.js')
   *
   */
  grunt.initConfig(
    grunt.util._.extend(gruntTasks, gruntConfig)
  );

  /*
   * Tasks.
   * Running 'grunt' should be enough for development.
   * When ready for production, 'grunt compile' will compile the
   * application into the './application' directory.
   *
   */
  grunt.registerTask('default', ['build', 'karma:unit', 'watch']);
  grunt.registerTask('build', [
    'jshint',
    'copy:build_app_js',
    'copy:build_app_data',
    'copy:build_vendor_js',
    'copy:build_views',
    'copy:build_shared_views',
    'copy:build_fonts',
    'copy:build_index',
    'copy:build_unit',
    'copy:build_protractor',
    'copy:build_karma',
    'copy:build_assets',
    'less:build_less',
    'cssmin:build_css',
    'clean:build_css_clean',
    'autoprefixer:build_autoprefix_css'
  ]);
  grunt.registerTask('compile', [
    'jshint',
    'clean:build_clean',
    'build',
    'copy:compile_views',
    'html2js:compile_shared_views',
    'uglify:compile_module_js',
    'concat:compile_js',
    'uglify:compile_soft_uglify',
    'clean:compile_js_clean',
    'clean:compile_shared_views_clean',
    'copy:compile_app_data',
    'copy:compile_app_assets',
    'copy:compile_index'
  ]);
  grunt.registerTask('test', ['karma:unit', 'protractor:build']);
  grunt.registerTask('test:unit', ['karma:unit']);
  grunt.registerTask('test:ci', ['karma:unit', 'protractor:ci_dev', 'protractor:ci_prod']);
  grunt.registerTask('test:e2e', ['protractor:build']);
  grunt.registerTask('dev', ['shell:dev_server']);
  grunt.registerTask('prod', ['shell:prod_server']);

  /*
   * Helper methods.
   * The methods below use the 'module_file_order' Array in
   * 'grunt.conf.js' to easily load modules in a consistent
   * manner, generate globbing patterns or process files.
   *
   * (i.e. the 'index.html' is processed in order to
   * automatically write <script> tags)
   *
   */

  /*
   * Generates a globbing pattern that matches the
   * 'module_file_order' Array in 'grunt.conf.js'.
   *
   * @param {String} modulePath - The module path
   * @returns {Array} output - An Array of globbing patterns
   *
   * (i.e. findModuleFilesIn('./src/app/') loads all module
   * files in './src/app' in the defined order)
   *
   */
  function findModuleFilesIn(modulePath){
    var output = [],
        i;

    for(i = 0; i <= gruntConfig.module_file_order.length-1; i++){
      output.push(modulePath + gruntConfig.module_file_order[i]);
    }

    return output;
  }

  /*
   * 'Manually globs (using 'node-glob')' for all JavaScript files
   * that should be included in the build phase, then stores them
   * into the 'buildScripts' Array. 'buildScripts' is then passed * as 'scripts' to the template.
   * The template is processed (usually loops through 'scripts').
   * (See 'index.html')
   *
   * @param {String} content - File content
   * @returns {String} - Processed content
   *
   */
  function processBuildScripts(content){
    var buildScripts = [],
        globFiles,
        customModules = gruntConfig.build.vendor_js
                        .concat(findModuleFilesIn('./src/app/'))
                        .concat(findModuleFilesIn('./src/common/'));

    customModules.forEach(function(module){
      globFiles = glob.sync(module);

      if(globFiles.length > 0){
        buildScripts = buildScripts.concat(globFiles);
      }
    });

    return grunt.template.process(content, {
      data: {
        scripts: buildScripts
      }
    });
  }
};<|endoftext|>


<commit_before>'use strict';

var expect = require('chai').expect;

describe('ScatterChart', function() {
  it('renders scatterchart', function() {
    var React = require('react/addons');
    var ScatterChart = require('../src/scatterchart').ScatterChart;
    var generate = require('../utils/datagen').generateArrayOfPoints;
    var TestUtils = React.addons.TestUtils;
    var points = 5;

    // Render a scatterchart using array data
    var data = {
      series1: generate(points),
      series2: generate(points)
    };
    var scatterchart = TestUtils.renderIntoDocument(
      <ScatterChart data={data} width={400} height={200} />
    );

    var circles = TestUtils.scryRenderedDOMComponentsWithTag(
      scatterchart, 'circle');
    expect(circles).to.have.length(2 * points);
  });
});
<commit_msg>Add test for scatter chart animation<commit_after>'use strict';

var expect = require('chai').expect;

describe('ScatterChart', function() {
  it('renders scatterchart', function() {
    var React = require('react/addons');
    var ScatterChart = require('../src/scatterchart').ScatterChart;
    var generate = require('../utils/datagen').generateArrayOfPoints;
    var TestUtils = React.addons.TestUtils;
    var points = 5;
    var pointRadius = 5;

    // Render a scatterchart using array data
    var data = {
      series1: generate(points),
      series2: generate(points)
    };
    var scatterchart = TestUtils.renderIntoDocument(
      <ScatterChart data={data} width={400} height={200} pointRadius={pointRadius} />
    );

    var circles = TestUtils.scryRenderedDOMComponentsWithTag(
      scatterchart, 'circle');
    expect(circles).to.have.length(2 * points);

    var circleOne = circles[0];
    expect(circleOne.getDOMNode().r.baseVal.value).to.equal(pointRadius);
    TestUtils.Simulate.mouseOver(circleOne);
    expect(circleOne.getDOMNode().r.baseVal.value).to.be.above(pointRadius);
    TestUtils.Simulate.mouseOut(circleOne);
    expect(circleOne.getDOMNode().r.baseVal.value).to.equal(pointRadius);
  });
});
<|endoftext|>


<commit_before>Seed({
		name : "Mold.Tools.Dev.CodeInclude",
		dna : "action"
	},
	function(){

		Mold.addPreProcessor("codeinclude", function(code){
			code = code.replace(/\/\/°include(.*?)$/gm, function(match, contents){
				var before = "";
				var as = false;
				if(contents && contents.indexOf(" as ")){
					var parts = contents.split(" as ");
					contents = parts[0];
					as = Mold.trim(parts[1]);
					if(as && ~as.indexOf(".")){
						var asParts = as.split(".");
						var collected = "";
						before = "var "
						for(var i = 0; i < asParts.length - 1; i++){
							before += collected + asParts[i] + " = " + collected + asParts[i] + " || {};\n";
							collected += collected + asParts[i] + ".";
						}
					}

				}
				var seed = Mold.getRawSeed(Mold.trim(contents)).func;

				if(typeof seed === "function"){
					if(as){
						if(before){
							var output =  before + as + " = " + seed.toString() + ";\n";
						}else{
							var output =  "var " + as + " = " + seed.toString() + ";\n";
						}
					}else{
						var output = seed.toString();
						output = output.substring(output.indexOf("{") + 1, output.lastIndexOf("}"));
					}
					return output;
				}else{
					return 'console.log("' + contents+ ' is not a function!");'
				}

			});

			return code;
		});

	}
)<commit_msg>bugfix, check include<commit_after>Seed({
		name : "Mold.Tools.Dev.CodeInclude",
		dna : "action"
	},
	function(){

		Mold.addPreProcessor("codeinclude", function(code){
			code = code.replace(/\/\/°include(.*?)$/gm, function(match, contents){
				var before = "";
				var as = false;
				if(contents && contents.indexOf(" as ")){
					var parts = contents.split(" as ");
					contents = parts[0];
					as = Mold.trim(parts[1]);
					if(as && ~as.indexOf(".")){
						var asParts = as.split(".");
						var collected = "";
						before = "var ";
						for(var i = 0; i < asParts.length - 1; i++){
							before += collected + asParts[i] + " = " + collected + asParts[i] + " || {};\n";
							collected += collected + asParts[i] + ".";
						}
					}

				}

				var seed;
				if(Mold.getRawSeed(Mold.trim(contents))){
					seed = Mold.getRawSeed(Mold.trim(contents)).func;
				}

				if(typeof seed === "function"){
					if(as){
						if(before){
							var output =  before + as + " = " + seed.toString() + ";\n";
						}else{
							var output =  "var " + as + " = " + seed.toString() + ";\n";
						}
					}else{
						var output = seed.toString();
						output = output.substring(output.indexOf("{") + 1, output.lastIndexOf("}"));
					}
					return output;
				}else{
					return 'console.log("' + contents+ ' is not a function!");'
				}

			});

			return code;
		});

	}
)<|endoftext|>


<commit_before>"use strict";
var config = require("./config.js");
module.exports = function (grunt) {
    // load all grunt task libraries
    require("load-grunt-tasks")(grunt);
    grunt.loadNpmTasks("grunt-contrib-watch");

    // run app
    grunt.registerTask("server:dev", ["express:dev", "watch:express"]);
    grunt.registerTask("server:test", ["express:test"]);
    grunt.registerTask("dev", ["eslint", "server:dev"]);

    // clean up code and run tests
    grunt.registerTask("default", ["eslint", "test"]);
    grunt.registerTask("test", ["env:test", "dropDatabase", "server:test", "mochaTest:all"]);

    // watch for changes and regenerate test PDF each time (for pdf development testing)
    grunt.registerTask("generateTestPdf", ["express:dev", "exec:testPdf"]);
    grunt.registerTask("report", ["generateTestPdf", "watch:pdf"]);

    // generate code coverage using bash istanbul wrapper
    grunt.registerTask("coverage", ["exec:coverage"]);
    // push code coverage to coveralls.io
    grunt.registerTask("coverage:push", ["exec:coverage", "coveralls"]);

    // generate documentation locally
    grunt.registerTask("docs", ["exec:docs"]);
    // generate documentation and push it to github
    grunt.registerTask("docs:push", ["docs", "gh-pages"]);

    var mongoose = require("mongoose");
    grunt.registerTask("dropDatabase", function () {
        // force grunt into async
        var done = this.async();
        mongoose.connect(config.mongo, function (err) {
            if (err) return done(err);
            mongoose.connection.db.dropDatabase(function(err) {
                if (err) return done(err);
                console.log("Database dropped");
                mongoose.connection.close(done);
            });
        });
    });

    grunt.initConfig({
        // detect code smells and particularly bad formatting
        eslint: {
            target: ["Gruntfile.js", "app.js", "lib/*.js", "lib/**/*.js", "test/*.js", "test/**/*.js"]
        },

        // run tests: make sure to close all express/db/sinon/etc connections or this
        // will hang
        mochaTest: {
            all: {
                options: {
                    reporter: "spec",
                    // bail: true,
                    timeout: "10000"
                },
                src: ["test/common/db_helper.js", "test/common/*.js", "test/*.js", "test/*/common.js", "test/users/avatar_test.js"]
            },
            unit: {
                options: {
                    reporter: "spec",
                    timeout: "10000"
                },
                src: ["test/common/db_helper.js", "test/common/*.js", "test/*/common.js", "test/*/unit/*.js",
                      "test/*/unit/**/*.js"]
            }
        },

        // set NODE_ENV so we don't send real emails or SMS' from tests
        env: {
            test: {
                NODE_ENV: "test"
            }
        },

        // run test server
        express: {
            test: {
                options: {
                    script: "run.js"
                }
            },
            dev: {
                options: {
                    script: "run.js",
                    backround: false
                }
            }
        },

        watch: {
            express: {
                files: [ "app.js", "lib/*.js", "lib/**/*.js"],
                tasks: [ "eslint", "express:dev" ],
                options: {
                    livereload: true,
                    spawn: false
                }
            },
            pdf: {
                // reloading broken on OSX because of EMFILE hence this hack
                files: [
                    "lib/controllers/patients/report.js",
                    "test_pdf.js",
                    "gruntfile.js",
                    "lib/models/schedule/schedule.js"
                ],
                tasks: [ "generateTestPdf" ],
                options: {
                    spawn: false
                }
            }
        },

        exec: {
            // build documentation locally: latest version of aglio doesn't play well with grunt-aglio
            // so we use a shell script instead
            docs: {
                cwd: "docs",
                cmd: "./build.sh"
            },
            // generate code coverage: bash wrapper around istanbul as their cli makes things a lot easier
            // than playing around with js hooks
            coverage: "./cover.sh",
            // generate test PDF (assuming running servers)
            testPdf: {
                cmd: "node test_pdf.js"
            }
        },

        // coveralls.io code coverage service
        coveralls: {
            options: {
                force: false
            },
            src: ["./coverage/lcov.info"]
        },

        // push generated documentation straight to gh pages (with fixed commit message, but that's not
        // the end of the world)
        "gh-pages": {
            options: {
                base: "docs/output",
                message: "Documentation updates (gh-pages commit message autogenerated by grunt)"
            },
            src: ["**"]
        }
    });
};
<commit_msg>fixing gruntfile test command<commit_after>"use strict";
var config = require("./config.js");
module.exports = function (grunt) {
    // load all grunt task libraries
    require("load-grunt-tasks")(grunt);
    grunt.loadNpmTasks("grunt-contrib-watch");

    // run app
    grunt.registerTask("server:dev", ["express:dev", "watch:express"]);
    grunt.registerTask("server:test", ["express:test"]);
    grunt.registerTask("dev", ["eslint", "server:dev"]);

    // clean up code and run tests
    grunt.registerTask("default", ["eslint", "test"]);
    grunt.registerTask("test", ["env:test", "dropDatabase", "server:test", "mochaTest:all"]);

    // watch for changes and regenerate test PDF each time (for pdf development testing)
    grunt.registerTask("generateTestPdf", ["express:dev", "exec:testPdf"]);
    grunt.registerTask("report", ["generateTestPdf", "watch:pdf"]);

    // generate code coverage using bash istanbul wrapper
    grunt.registerTask("coverage", ["exec:coverage"]);
    // push code coverage to coveralls.io
    grunt.registerTask("coverage:push", ["exec:coverage", "coveralls"]);

    // generate documentation locally
    grunt.registerTask("docs", ["exec:docs"]);
    // generate documentation and push it to github
    grunt.registerTask("docs:push", ["docs", "gh-pages"]);

    var mongoose = require("mongoose");
    grunt.registerTask("dropDatabase", function () {
        // force grunt into async
        var done = this.async();
        mongoose.connect(config.mongo, function (err) {
            if (err) return done(err);
            mongoose.connection.db.dropDatabase(function(err) {
                if (err) return done(err);
                console.log("Database dropped");
                mongoose.connection.close(done);
            });
        });
    });

    grunt.initConfig({
        // detect code smells and particularly bad formatting
        eslint: {
            target: ["Gruntfile.js", "app.js", "lib/*.js", "lib/**/*.js", "test/*.js", "test/**/*.js"]
        },

        // run tests: make sure to close all express/db/sinon/etc connections or this
        // will hang
        mochaTest: {
            all: {
                options: {
                    reporter: "spec",
                    // bail: true,
                    timeout: "10000"
                },
                src: ["test/common/db_helper.js", "test/common/*.js", "test/*.js", "test/*/common.js", "test/**/*.js"]
            },
            unit: {
                options: {
                    reporter: "spec",
                    timeout: "10000"
                },
                src: ["test/common/db_helper.js", "test/common/*.js", "test/*/common.js", "test/*/unit/*.js",
                      "test/*/unit/**/*.js"]
            }
        },

        // set NODE_ENV so we don't send real emails or SMS' from tests
        env: {
            test: {
                NODE_ENV: "test"
            }
        },

        // run test server
        express: {
            test: {
                options: {
                    script: "run.js"
                }
            },
            dev: {
                options: {
                    script: "run.js",
                    backround: false
                }
            }
        },

        watch: {
            express: {
                files: [ "app.js", "lib/*.js", "lib/**/*.js"],
                tasks: [ "eslint", "express:dev" ],
                options: {
                    livereload: true,
                    spawn: false
                }
            },
            pdf: {
                // reloading broken on OSX because of EMFILE hence this hack
                files: [
                    "lib/controllers/patients/report.js",
                    "test_pdf.js",
                    "gruntfile.js",
                    "lib/models/schedule/schedule.js"
                ],
                tasks: [ "generateTestPdf" ],
                options: {
                    spawn: false
                }
            }
        },

        exec: {
            // build documentation locally: latest version of aglio doesn't play well with grunt-aglio
            // so we use a shell script instead
            docs: {
                cwd: "docs",
                cmd: "./build.sh"
            },
            // generate code coverage: bash wrapper around istanbul as their cli makes things a lot easier
            // than playing around with js hooks
            coverage: "./cover.sh",
            // generate test PDF (assuming running servers)
            testPdf: {
                cmd: "node test_pdf.js"
            }
        },

        // coveralls.io code coverage service
        coveralls: {
            options: {
                force: false
            },
            src: ["./coverage/lcov.info"]
        },

        // push generated documentation straight to gh pages (with fixed commit message, but that's not
        // the end of the world)
        "gh-pages": {
            options: {
                base: "docs/output",
                message: "Documentation updates (gh-pages commit message autogenerated by grunt)"
            },
            src: ["**"]
        }
    });
};
<|endoftext|>


<commit_before>'use strict';

var watchedFiles = [
    "**/*.js",
    "!**/node_modules/**",
    "!dist/*.js"
];

module.exports = function (grunt) {
    var fileList = [];
    grunt.initConfig({
    concat: {
      options: {
        // define a string to put between each file in the concatenated output
        separator: '\n'
      },
      dist: {
        // the files to concatenate
        src: fileList,
        // the location of the resulting JS file
        dest: 'dist/framework_concat.js'
      }
    },
    // define source files and their destinations
    uglify: {
        files: {
            src: 'dist/framework_concat.js'//'./*.js',  // source files mask
            ,

            dest: 'dist/',    // destination folder
            expand: true,    // allow dynamic building
            flatten: true,   // remove all unnecessary nesting
            ext: '.min.js'   // replace .js to .min.js
        }
    },
    watch: {
        js:  { files: watchedFiles, tasks: [ 'buildFileList', 'concat', 'uglify' ] },

    }
    /*
    ,

    copy: {
      main: {
        src: 'dist/framework_concat.min.js',
        dest: '../spiral/_dev/client/angularSpiral/app/scripts/classes/moebioFramework.min.js'
      },
      onSpiralCanvas: {
        src: 'dist/framework_concat.min.js',
        dest: '../SpiralCanvas/classes/moebioFramework.min.js',
      },
      onGeoProspective: {
        src: 'dist/framework_concat.min.js',
        dest: '../Geoprospective/classes/moebioFramework.min.js',
      },
    }
    */
});

// load plugins
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-copy');

// register at least this one task
grunt.registerTask('default', [ 'buildFileList', 'concat', 'uglify' ]);

// test task
grunt.registerTask('buildFileList', 'My "buildFileList" task description.', function() {
  var done = this.async();
  var fs = require('fs');
  var filename = "IncludeAll.js";
    fs.readFile(filename, 'utf8', function(err, data) {
        if (err) throw err;
        var lines = data.split("\n");
        for (var i = 0; i < lines.length; i++) {
            var line = lines[i];
            if( line.substr(0, 8)=="include("){
                var fileName = line.substr(24, line.length-27);
                fileList.push( fileName );
            }
        };

        console.log( "fileList to concatenate / uglyfy is " + fileList.length + " lines long" );
        done();
    });
});

};

<commit_msg>irrelevant change to test push<commit_after>'use strict';

var watchedFiles = [
    "**/*.js",
    "!**/node_modules/**",
    "!dist/*.js"
];

module.exports = function (grunt) {
    var fileList = [];
    grunt.initConfig({
    concat: {
      options: {
        // define a string to put between each file in the concatenated output
        separator: '\n'
      },
      dist: {
        // the files to concatenate
        src: fileList,
        // the location of the resulting JS file
        dest: 'dist/framework_concat.js'
      }
    },
    // define source files and their destinations
    uglify: {
        files: {
            src: 'dist/framework_concat.js'//'./*.js',  // source files mask
            ,

            dest: 'dist/',    // destination folder
            expand: true,    // allow dynamic building
            flatten: true,   // remove all unnecessary nesting
            ext: '.min.js'   // replace .js to .min.js
        }
    },
    watch: {
        js:  { files: watchedFiles, tasks: [ 'buildFileList', 'concat', 'uglify' ] },

    }
    /*
    ,

    copy: {
      main: {
        src: 'dist/framework_concat.min.js',
        dest: '../spiral/_dev/client/angularSpiral/app/scripts/classes/moebioFramework.min.js'
      },
      onSpiralCanvas: {
        src: 'dist/framework_concat.min.js',
        dest: '../SpiralCanvas/classes/moebioFramework.min.js',
      },
      onGeoProspective: {
        src: 'dist/framework_concat.min.js',
        dest: '../Geoprospective/classes/moebioFramework.min.js',
      },
    }
    */
});

// load plugins
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-copy');

// register at least this one task
grunt.registerTask('default', [ 'buildFileList', 'concat', 'uglify' ]);

// test task
grunt.registerTask('buildFileList', 'My "buildFileList" task description.', function() {
  var done = this.async();
  var fs = require('fs');
  var filename = "IncludeAll.js";
    fs.readFile(filename, 'utf8', function(err, data) {
        if (err) throw err;
        var lines = data.split("\n");
        for (var i = 0; i < lines.length; i++) {
            var line = lines[i];
            if( line.substr(0, 8)=="include("){
                var fileName = line.substr(24, line.length-27);
                fileList.push( fileName );
            }
        };

        console.log( "fileList to concatenate / uglyfy is " + fileList.length + " lines long" );
        done();
    });
});

};

<|endoftext|>