<commit_before>'use strict';

(function (angular, buildfire, window) {
  angular.module('couponPluginWidget')
    .controller('WidgetMapCtrl', ['$scope', 'DataStore', 'TAG_NAMES', 'LAYOUTS', '$sce', '$rootScope', 'Buildfire', 'ViewStack', 'UserData', 'GeoDistance', '$timeout', '$modal',
      function ($scope, DataStore, TAG_NAMES, LAYOUTS, $sce, $rootScope, Buildfire, ViewStack, UserData, GeoDistance, $timeout, $modal) {
        var WidgetMap = this;
        WidgetMap.locationData = {};
        WidgetMap.currentDate = +new Date();
        WidgetMap.refreshData = 1;
        var searchOptions = {
          skip: 0,
          filter: {
            "$and": [{
              "$or": [{
                "$json.expiresOn": {$gte: WidgetMap.currentDate}
              }, {"$json.expiresOn": ""}]
            }, {"$json.location.coordinates": {$exists: true}}]
          }
        };
        var currentDistanceUnit = null;
        $rootScope.$on('FILTER_ITEMS', function (e, view) {
          if (view && view.isFilterApplied) {
            WidgetMap.isFilterApplied = true;
            WidgetMap.getAllItems(view.filter);
          }
        });
        function getGeoLocation() {
          Buildfire.geo.getCurrentPosition(
            null,
            function (err, position) {
              if (err) {
                console.error(err);
              }
              else if (position && position.coords) {
                $scope.$apply(function () {
                  WidgetMap.locationData.currentCoordinates = [position.coords.longitude, position.coords.latitude];
                  localStorage.setItem('user_location', JSON.stringify(WidgetMap.locationData.currentCoordinates));
                  WidgetMap.refreshData += 1;
                });
              }
              else {
                getGeoLocation();
              }
            }
          );
        }

        /**
         * Method to open buildfire auth login pop up and allow user to login using credentials.
         */
        WidgetMap.openLogin = function () {
          buildfire.auth.login({}, function () {

          });
        };

        WidgetMap.getAllItems = function (filter) {
          Buildfire.spinner.show();
          var successAll = function (resultAll) {
              Buildfire.spinner.hide();
              if (resultAll) {
                resultAll.forEach(function (_item) {
                  _item.data.distance = 0; // default distance value
                });
              }

              WidgetMap.locationData.items = resultAll;
              if (WidgetMap.currentLoggedInUser)
                WidgetMap.getSavedItems();
            },
            errorAll = function (error) {
              Buildfire.spinner.hide();
              console.log("error getting items", error)
            };
          console.log("***********", WidgetMap.data.content);

          if(filter){
            var itemFilter;
            if(filter.categories && filter.categories.length){
              searchOptions= {
                skip: 0,
                    filter: {
                "$and": [{
                  "$or": [{
                    "$json.expiresOn": {$gte: WidgetMap.currentDate}
                  }, {"$json.expiresOn": ""}]
                }, {"$json.location.coordinates": {$exists: true}}]
              }
              }
              itemFilter = {'$json.SelectedCategories': {'$in': filter.categories}};
              searchOptions.filter.$and.push(itemFilter);
            }
            DataStore.search(searchOptions  , TAG_NAMES.COUPON_ITEMS).then(successAll, errorAll);
          }else{
            DataStore.search(searchOptions, TAG_NAMES.COUPON_ITEMS).then(successAll, errorAll);
          }

        };

        WidgetMap.showSavedItems = function () {
          if (WidgetMap.currentLoggedInUser) {
            ViewStack.push({
              template: 'Saved',
              params: {
                controller: "WidgetSavedCtrl as WidgetSaved"
              }
            });
          } else {
            WidgetMap.openLogin();
          }
        };

        WidgetMap.showFilter = function () {
          ViewStack.push({
            template: 'Filter',
            params: {
              controller: "WidgetFilterCtrl as WidgetFilter"
            }
          });
        };

        WidgetMap.showListItems = function () {
          console.log("============", WidgetMap.data.design.itemListLayout);
          if (WidgetMap.data.settings.defaultView == 'list')
            ViewStack.popAllViews();
          else
            ViewStack.push({
              template: WidgetMap.data.design.itemListLayout,
              params: {
                controller: "WidgetHomeCtrl as WidgetHome"
              }
            });
        };

        WidgetMap.setSavedItem = function () {
          var isChanged = false;
          for (var item = 0; item < WidgetMap.locationData.items.length; item++) {
            WidgetMap.locationData.items[item].isSaved = false;
            for (var save in WidgetMap.saved) {
              if (WidgetMap.locationData.items[item].id == WidgetMap.saved[save].data.itemId) {
                isChanged = true;
                WidgetMap.locationData.items[item].isSaved = true;
                WidgetMap.locationData.items[item].savedId = WidgetMap.saved[save].id;
              }
            }
          }
          if (isChanged)
            WidgetMap.refreshData += 1;
        };

        WidgetMap.getSavedItems = function () {
          Buildfire.spinner.show();
          var err = function (error) {
            Buildfire.spinner.hide();
            console.log("============ There is an error in getting saved items data", error);
          }, result = function (result) {
            Buildfire.spinner.hide();
            WidgetMap.saved = result;
            console.log("...................", WidgetMap.saved);
            WidgetMap.setSavedItem();
          };
          UserData.search({}, TAG_NAMES.COUPON_SAVED).then(result, err);
        };

        WidgetMap.init = function () {
          Buildfire.spinner.show();
          var success = function (result) {
              Buildfire.spinner.hide();
              if (result && result.data) {
                WidgetMap.data = result.data;
              }
              else {
                WidgetMap.data = {
                  design: {
                    itemListLayout: LAYOUTS.itemListLayout[0].name
                  },
                  "settings": {
                    defaultView: "list",
                    distanceIn: "mi",
                    mapView: "show",
                    filterPage: "show"
                  }
                };
              }
              if (WidgetMap.data && !WidgetMap.data.design) {
                WidgetMap.data.design = {
                  itemListLayout: LAYOUTS.itemListLayout[0].name
                };
              }
              if (WidgetMap.data && !WidgetMap.data.settings) {
                WidgetMap.data.settings = {
                  defaultView: "list",
                  distanceIn: "mi",
                  mapView: "show",
                  filterPage: "show"
                };
              }
              if (!WidgetMap.data.design.itemListLayout) {
                WidgetMap.data.design.itemListLayout = LAYOUTS.itemListLayout[0].name;
              }
              if (!WidgetMap.data.content)
                WidgetMap.data.content = {};
              if (WidgetMap.data.settings.distanceIn)
                currentDistanceUnit = WidgetMap.data.settings.distanceIn;
              WidgetMap.getAllItems();
            }
            , error = function (err) {
              Buildfire.spinner.hide();
              WidgetMap.data = {design: {itemListLayout: LAYOUTS.itemListLayout[0].name}};
              console.error('Error while getting data', err);
            };
          DataStore.get(TAG_NAMES.COUPON_INFO).then(success, error);

          // Fetch user location

          if (typeof(Storage) !== "undefined") {
            var userLocation = localStorage.getItem('user_location');
            if (userLocation) {
              WidgetMap.locationData.currentCoordinates = JSON.parse(userLocation);
            }
            else
              getGeoLocation(); // get data if not in cache
          }
          else {
            getGeoLocation(); // get data if localStorage is not supported
          }
        };

        /* Onclick event of items on the map view*/
        WidgetMap.selectedMarker = function (itemIndex) {
          if (itemIndex === null) {
            WidgetMap.selectedItem = null;
            $scope.$digest();
            return;
          }
          WidgetMap.selectedItem = WidgetMap.locationData.items[itemIndex];
          console.log("...................", WidgetMap.selectedItem);

          GeoDistance.getDistance(WidgetMap.locationData.currentCoordinates, [WidgetMap.selectedItem], WidgetMap.data.settings.distanceIn).then(function (result) {
            console.log('Distance---------------------', result);
            if (result.rows.length && result.rows[0].elements.length && result.rows[0].elements[0].distance && result.rows[0].elements[0].distance.text) {
              WidgetMap.selectedItemDistance = result.rows[0].elements[0].distance.text;
            } else {
              WidgetMap.selectedItemDistance = null;
            }
          }, function (err) {
            WidgetMap.selectedItemDistance = null;
          });
        };

        WidgetMap.addRemoveSavedItem = function (item) {
          if (item.isSaved && item.savedId) {
            Buildfire.spinner.show();
            var successRemove = function (result) {
              Buildfire.spinner.hide();
              WidgetMap.selectedItem.isSaved = false;
              WidgetMap.selectedItem.savedId = null;
              if (!$scope.$$phase)
                $scope.$digest();
              var removeSavedModal = $modal.open({
                templateUrl: 'templates/Saved_Removed.html',
                size: 'sm',
                backdropClass: "ng-hide"
              });
              $timeout(function () {
                removeSavedModal.close();
              }, 3000);
              $rootScope.$broadcast("ITEM_SAVED_UPDATED");

            }, errorRemove = function () {
              Buildfire.spinner.hide();
              return console.error('There was a problem removing your data');
            };
            UserData.delete(item.savedId, TAG_NAMES.COUPON_SAVED, WidgetMap.currentLoggedInUser._id).then(successRemove, errorRemove)
          }
          else {
            Buildfire.spinner.show();
            WidgetMap.savedItem = {
              data: {
                itemId: item.id
              }
            };
            var successItem = function (result) {
              Buildfire.spinner.hide();
              console.log("Inserted", result);
              WidgetMap.selectedItem.isSaved = true;
              WidgetMap.selectedItem.savedId = result.id;
              if (!$scope.$$phase)
                $scope.$digest();

              var addedCouponModal = $modal.open({
                templateUrl: 'templates/Saved_Confirmation.html',
                size: 'sm',
                backdropClass: "ng-hide"
              });
              $timeout(function () {
                addedCouponModal.close();
              }, 3000);
              $rootScope.$broadcast("ITEM_SAVED_UPDATED");

            }, errorItem = function () {
              Buildfire.spinner.hide();
              return console.error('There was a problem saving your data');
            };
            UserData.insert(WidgetMap.savedItem.data, TAG_NAMES.COUPON_SAVED).then(successItem, errorItem);
          }
        };

        WidgetMap.refreshLocation = function () {
          getGeoLocation();
        };

        WidgetMap.init();

        var onUpdateCallback = function (event) {
          setTimeout(function () {
            $scope.$digest();
            if (event && event.tag === TAG_NAMES.COUPON_INFO) {
              WidgetMap.data = event.data;
              if (!WidgetMap.data.design)
                WidgetMap.data.design = {};
              if (!WidgetMap.data.content)
                WidgetMap.data.content = {};
              if (!WidgetMap.data.settings)
                WidgetMap.data.settings = {};
              if (currentDistanceUnit && WidgetMap.data.settings.distanceIn) {
                if (currentDistanceUnit != WidgetMap.data.settings.distanceIn) {
                  getItemsDistance(WidgetMap.locationData.items);
                  currentDistanceUnit = WidgetMap.data.settings.distanceIn;
                }
              }
            }
            else if (event && event.tag === TAG_NAMES.COUPON_ITEMS) {
              WidgetMap.getAllItems();
            }
            $scope.$digest();
          }, 0);
        };

        DataStore.onUpdate().then(null, null, onUpdateCallback);

        /**
         * Check for current logged in user, if not show login screen
         */
        buildfire.auth.getCurrentUser(function (err, user) {
          if (user) {
            WidgetMap.currentLoggedInUser = user;
            $scope.$apply();
          }
        });

        function getItemsDistance(_items) {
          if (WidgetMap.locationData.currentCoordinates == null) {
            return;
          }
          if (_items && _items.length) {
            GeoDistance.getDistance(WidgetMap.locationData.currentCoordinates, _items, WidgetMap.data.settings.distanceIn).then(function (result) {
              console.log('WidgetMap.locationData.currentCoordinates', WidgetMap.locationData.currentCoordinates);
              for (var _ind = 0; _ind < WidgetMap.locationData.items.length; _ind++) {
                if (_items && _items[_ind]) {
                  _items[_ind].data.distance = (result.rows[0].elements[_ind].status != 'OK') ? -1 : result.rows[0].elements[_ind].distance.value;
                }
              }

            }, function (err) {
              console.error('distance err', err);
            });
          }
        }

        $scope.$watch(function () {
          return WidgetMap.locationData.items;
        }, getItemsDistance);

      }]);
})(window.angular, window.buildfire, window);
<commit_msg>on applying filter map initialised fixes<commit_after>'use strict';

(function (angular, buildfire, window) {
  angular.module('couponPluginWidget')
    .controller('WidgetMapCtrl', ['$scope', 'DataStore', 'TAG_NAMES', 'LAYOUTS', '$sce', '$rootScope', 'Buildfire', 'ViewStack', 'UserData', 'GeoDistance', '$timeout', '$modal',
      function ($scope, DataStore, TAG_NAMES, LAYOUTS, $sce, $rootScope, Buildfire, ViewStack, UserData, GeoDistance, $timeout, $modal) {
        var WidgetMap = this;
        WidgetMap.locationData = {};
        WidgetMap.currentDate = +new Date();
        WidgetMap.refreshData = 1;
        var searchOptions = {
          skip: 0,
          filter: {
            "$and": [{
              "$or": [{
                "$json.expiresOn": {$gte: WidgetMap.currentDate}
              }, {"$json.expiresOn": ""}]
            }, {"$json.location.coordinates": {$exists: true}}]
          }
        };
        var currentDistanceUnit = null;
        $rootScope.$on('FILTER_ITEMS', function (e, view) {
          if (view && view.isFilterApplied) {
            WidgetMap.isFilterApplied = true;
            WidgetMap.getAllItems(view.filter);
          }
        });
        function getGeoLocation() {
          Buildfire.geo.getCurrentPosition(
            null,
            function (err, position) {
              if (err) {
                console.error(err);
              }
              else if (position && position.coords) {
                $scope.$apply(function () {
                  WidgetMap.locationData.currentCoordinates = [position.coords.longitude, position.coords.latitude];
                  localStorage.setItem('user_location', JSON.stringify(WidgetMap.locationData.currentCoordinates));
                  WidgetMap.refreshData += 1;
                });
              }
              else {
                getGeoLocation();
              }
            }
          );
        }

        /**
         * Method to open buildfire auth login pop up and allow user to login using credentials.
         */
        WidgetMap.openLogin = function () {
          buildfire.auth.login({}, function () {

          });
        };

        WidgetMap.getAllItems = function (filter) {
          Buildfire.spinner.show();
          var successAll = function (resultAll) {
              Buildfire.spinner.hide();
              if (resultAll) {
                resultAll.forEach(function (_item) {
                  _item.data.distance = 0; // default distance value
                });
              }

              WidgetMap.locationData.items = resultAll;
                WidgetMap.refreshData += 1;
              if (WidgetMap.currentLoggedInUser)
                WidgetMap.getSavedItems();
            },
            errorAll = function (error) {
              Buildfire.spinner.hide();
              console.log("error getting items", error)
            };
          console.log("***********", WidgetMap.data.content);

          if(filter){
            var itemFilter;
            if(filter.categories && filter.categories.length){
              searchOptions= {
                skip: 0,
                    filter: {
                "$and": [{
                  "$or": [{
                    "$json.expiresOn": {$gte: WidgetMap.currentDate}
                  }, {"$json.expiresOn": ""}]
                }, {"$json.location.coordinates": {$exists: true}}]
              }
              }
              itemFilter = {'$json.SelectedCategories': {'$in': filter.categories}};
              searchOptions.filter.$and.push(itemFilter);
            }
            DataStore.search(searchOptions  , TAG_NAMES.COUPON_ITEMS).then(successAll, errorAll);
          }else{
            DataStore.search(searchOptions, TAG_NAMES.COUPON_ITEMS).then(successAll, errorAll);
          }

        };

        WidgetMap.showSavedItems = function () {
          if (WidgetMap.currentLoggedInUser) {
            ViewStack.push({
              template: 'Saved',
              params: {
                controller: "WidgetSavedCtrl as WidgetSaved"
              }
            });
          } else {
            WidgetMap.openLogin();
          }
        };

        WidgetMap.showFilter = function () {
          ViewStack.push({
            template: 'Filter',
            params: {
              controller: "WidgetFilterCtrl as WidgetFilter"
            }
          });
        };

        WidgetMap.showListItems = function () {
          console.log("============", WidgetMap.data.design.itemListLayout);
          if (WidgetMap.data.settings.defaultView == 'list')
            ViewStack.popAllViews();
          else
            ViewStack.push({
              template: WidgetMap.data.design.itemListLayout,
              params: {
                controller: "WidgetHomeCtrl as WidgetHome"
              }
            });
        };

        WidgetMap.setSavedItem = function () {
          var isChanged = false;
          for (var item = 0; item < WidgetMap.locationData.items.length; item++) {
            WidgetMap.locationData.items[item].isSaved = false;
            for (var save in WidgetMap.saved) {
              if (WidgetMap.locationData.items[item].id == WidgetMap.saved[save].data.itemId) {
                isChanged = true;
                WidgetMap.locationData.items[item].isSaved = true;
                WidgetMap.locationData.items[item].savedId = WidgetMap.saved[save].id;
              }
            }
          }
          if (isChanged)
            WidgetMap.refreshData += 1;
        };

        WidgetMap.getSavedItems = function () {
          Buildfire.spinner.show();
          var err = function (error) {
            Buildfire.spinner.hide();
            console.log("============ There is an error in getting saved items data", error);
          }, result = function (result) {
            Buildfire.spinner.hide();
            WidgetMap.saved = result;
            console.log("...................", WidgetMap.saved);
            WidgetMap.setSavedItem();
          };
          UserData.search({}, TAG_NAMES.COUPON_SAVED).then(result, err);
        };

        WidgetMap.init = function () {
          Buildfire.spinner.show();
          var success = function (result) {
              Buildfire.spinner.hide();
              if (result && result.data) {
                WidgetMap.data = result.data;
              }
              else {
                WidgetMap.data = {
                  design: {
                    itemListLayout: LAYOUTS.itemListLayout[0].name
                  },
                  "settings": {
                    defaultView: "list",
                    distanceIn: "mi",
                    mapView: "show",
                    filterPage: "show"
                  }
                };
              }
              if (WidgetMap.data && !WidgetMap.data.design) {
                WidgetMap.data.design = {
                  itemListLayout: LAYOUTS.itemListLayout[0].name
                };
              }
              if (WidgetMap.data && !WidgetMap.data.settings) {
                WidgetMap.data.settings = {
                  defaultView: "list",
                  distanceIn: "mi",
                  mapView: "show",
                  filterPage: "show"
                };
              }
              if (!WidgetMap.data.design.itemListLayout) {
                WidgetMap.data.design.itemListLayout = LAYOUTS.itemListLayout[0].name;
              }
              if (!WidgetMap.data.content)
                WidgetMap.data.content = {};
              if (WidgetMap.data.settings.distanceIn)
                currentDistanceUnit = WidgetMap.data.settings.distanceIn;
              WidgetMap.getAllItems();
            }
            , error = function (err) {
              Buildfire.spinner.hide();
              WidgetMap.data = {design: {itemListLayout: LAYOUTS.itemListLayout[0].name}};
              console.error('Error while getting data', err);
            };
          DataStore.get(TAG_NAMES.COUPON_INFO).then(success, error);

          // Fetch user location

          if (typeof(Storage) !== "undefined") {
            var userLocation = localStorage.getItem('user_location');
            if (userLocation) {
              WidgetMap.locationData.currentCoordinates = JSON.parse(userLocation);
            }
            else
              getGeoLocation(); // get data if not in cache
          }
          else {
            getGeoLocation(); // get data if localStorage is not supported
          }
        };

        /* Onclick event of items on the map view*/
        WidgetMap.selectedMarker = function (itemIndex) {
          if (itemIndex === null) {
            WidgetMap.selectedItem = null;
            $scope.$digest();
            return;
          }
          WidgetMap.selectedItem = WidgetMap.locationData.items[itemIndex];
          console.log("...................", WidgetMap.selectedItem);

          GeoDistance.getDistance(WidgetMap.locationData.currentCoordinates, [WidgetMap.selectedItem], WidgetMap.data.settings.distanceIn).then(function (result) {
            console.log('Distance---------------------', result);
            if (result.rows.length && result.rows[0].elements.length && result.rows[0].elements[0].distance && result.rows[0].elements[0].distance.text) {
              WidgetMap.selectedItemDistance = result.rows[0].elements[0].distance.text;
            } else {
              WidgetMap.selectedItemDistance = null;
            }
          }, function (err) {
            WidgetMap.selectedItemDistance = null;
          });
        };

        WidgetMap.addRemoveSavedItem = function (item) {
          if (item.isSaved && item.savedId) {
            Buildfire.spinner.show();
            var successRemove = function (result) {
              Buildfire.spinner.hide();
              WidgetMap.selectedItem.isSaved = false;
              WidgetMap.selectedItem.savedId = null;
              if (!$scope.$$phase)
                $scope.$digest();
              var removeSavedModal = $modal.open({
                templateUrl: 'templates/Saved_Removed.html',
                size: 'sm',
                backdropClass: "ng-hide"
              });
              $timeout(function () {
                removeSavedModal.close();
              }, 3000);
              $rootScope.$broadcast("ITEM_SAVED_UPDATED");

            }, errorRemove = function () {
              Buildfire.spinner.hide();
              return console.error('There was a problem removing your data');
            };
            UserData.delete(item.savedId, TAG_NAMES.COUPON_SAVED, WidgetMap.currentLoggedInUser._id).then(successRemove, errorRemove)
          }
          else {
            Buildfire.spinner.show();
            WidgetMap.savedItem = {
              data: {
                itemId: item.id
              }
            };
            var successItem = function (result) {
              Buildfire.spinner.hide();
              console.log("Inserted", result);
              WidgetMap.selectedItem.isSaved = true;
              WidgetMap.selectedItem.savedId = result.id;
              if (!$scope.$$phase)
                $scope.$digest();

              var addedCouponModal = $modal.open({
                templateUrl: 'templates/Saved_Confirmation.html',
                size: 'sm',
                backdropClass: "ng-hide"
              });
              $timeout(function () {
                addedCouponModal.close();
              }, 3000);
              $rootScope.$broadcast("ITEM_SAVED_UPDATED");

            }, errorItem = function () {
              Buildfire.spinner.hide();
              return console.error('There was a problem saving your data');
            };
            UserData.insert(WidgetMap.savedItem.data, TAG_NAMES.COUPON_SAVED).then(successItem, errorItem);
          }
        };

        WidgetMap.refreshLocation = function () {
          getGeoLocation();
        };

        WidgetMap.init();

        var onUpdateCallback = function (event) {
          setTimeout(function () {
            $scope.$digest();
            if (event && event.tag === TAG_NAMES.COUPON_INFO) {
              WidgetMap.data = event.data;
              if (!WidgetMap.data.design)
                WidgetMap.data.design = {};
              if (!WidgetMap.data.content)
                WidgetMap.data.content = {};
              if (!WidgetMap.data.settings)
                WidgetMap.data.settings = {};
              if (currentDistanceUnit && WidgetMap.data.settings.distanceIn) {
                if (currentDistanceUnit != WidgetMap.data.settings.distanceIn) {
                  getItemsDistance(WidgetMap.locationData.items);
                  currentDistanceUnit = WidgetMap.data.settings.distanceIn;
                }
              }
            }
            else if (event && event.tag === TAG_NAMES.COUPON_ITEMS) {
              WidgetMap.getAllItems();
            }
            $scope.$digest();
          }, 0);
        };

        DataStore.onUpdate().then(null, null, onUpdateCallback);

        /**
         * Check for current logged in user, if not show login screen
         */
        buildfire.auth.getCurrentUser(function (err, user) {
          if (user) {
            WidgetMap.currentLoggedInUser = user;
            $scope.$apply();
          }
        });

        function getItemsDistance(_items) {
          if (WidgetMap.locationData.currentCoordinates == null) {
            return;
          }
          if (_items && _items.length) {
            GeoDistance.getDistance(WidgetMap.locationData.currentCoordinates, _items, WidgetMap.data.settings.distanceIn).then(function (result) {
              console.log('WidgetMap.locationData.currentCoordinates', WidgetMap.locationData.currentCoordinates);
              for (var _ind = 0; _ind < WidgetMap.locationData.items.length; _ind++) {
                if (_items && _items[_ind]) {
                  _items[_ind].data.distance = (result.rows[0].elements[_ind].status != 'OK') ? -1 : result.rows[0].elements[_ind].distance.value;
                }
              }

            }, function (err) {
              console.error('distance err', err);
            });
          }
        }

        $scope.$watch(function () {
          return WidgetMap.locationData.items;
        }, getItemsDistance);

      }]);
})(window.angular, window.buildfire, window);
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2014 YY Digital Pty Ltd. All Rights Reserved.
 * Please see the LICENSE file included with this distribution for details.
 */

var path   = require("path"),
    fs     = require("fs"),
    spawn  = require("./spawn"),
    async  = require("async"),
    alloy  = require("./alloy"),
    api    = require("./api"),
    bundle = require("./bundle"),
    config = require("./config"),
    uglify = require("./uglify"),
    logger = require("../../server/logger.js"),
    jshint = require("./jshint_runner"),
    wrench = require("wrench"),
    _      = require("underscore");

require("./fs_extension");

// Copies all Resource files and prepares JS files
function prepare(src, dst, callback) {
  var app_name = config.app_name;
  if (src.match("js$")){
    try {
      var src_text = uglify.toString(fs.readFileSync(src).toString(),src);
      if (src.match("_spec.js$")) {
        if (config.specType === "jasmine") {
          src_text =  "var __jasmine = require('/lib/jasmine');var methods = ['spyOn','it','xit','expect','runs','waits','waitsFor','beforeEach','afterEach','describe','xdescribe','jasmine'];methods.forEach(function(method) {this[method] = __jasmine[method];});"
          +src_text;
        } else if (config.specType === "mocha-should") {
          src_text =  "var should = require('/lib/should');\n"
            + "var sinon = require('/lib/sinon');\n"
          +src_text;
        } else if (config.specType === "mocha-chai") {
          src_text =  "var chai = require('/lib/chai'); var expect = chai.expect; var assert = chai.assert;\n"
            + "var sinon = require('/lib/sinon');\n"
          +src_text;
        }
      }
      else if(config.runCoverage && !src.match("spec/")) { //Instrumenting the application code with istanbul for code coverage
		var instrumentedCode = require("./coverage").instrumentCode(src_text, src);
		src_text = instrumentedCode;

		config.instrumentedfiles[src] = dst;// storing all instrumented file
      }

      fs.writeFile(dst,src_text, callback);
    } catch (e) {
      logger.error(e.message + "\nFile   : " + src + "\nLine   : " + e.line + "\nColumn : " + e.col);
      config.isWatching || process.exit(1);
    }
  } else { // Non-JS file - just pump it
    var  is = fs.createReadStream(src);
    var  os = fs.createWriteStream(dst);
    is.on("end", callback).pipe(os);
  }
}

function copyI18n(file, callback) {
  var read = fs.createReadStream(path.join(config.i18n_path,file));
  var write = fs.createWriteStream(path.join(config.tishadow_src, file));
  write.on('close',callback);
  read.pipe(write);
};

function finalise(file_list,callback) {
  // Bundle up to go
  var total = file_list.files.length;
  bundle.pack(file_list.files,function(written) {
    logger.info(total+ " file(s) bundled.");
    if (config.isAlloy) {
      alloy.writeMap();
    }
    fs.touch(config.last_updated_file);
    if (config.isBundle) {
      logger.info("Bundle Ready: " + config.bundle_file);
      if (callback) {
        callback();
      }
    } else {
      api.newBundle(config.isPatch?_.filter(file_list.files, function(f) { return f.match(".js$");}):null );
    }
  });
}

module.exports = function(env, callback) {
  config.buildPaths(env, function() {
    if (env.jshint) {
      logger.info("Running JSHint");
      jshint.checkPath(config.jshint_path);
    }

    logger.info("Beginning Build Process");
    // a js map of hashes must be built whether or not it is an update.
    if (config.isAlloy && !config.skipAlloyCompile) {
      if (config.platform === undefined) {
        logger.error("You need to use the --platform (android|ios) flag or have deployment-targets in tiapp.xml with an alloy project.");
        process.exit();
      }
      async.detectSeries(config.platform, function(platform, callback) {
        logger.info("Compiling Alloy for " + platform);
        var alloy_command = spawn('alloy', ['compile', '-b','-l', '1', '--platform', platform, '--config', 'sourcemap=false']);
        alloy_command.stderr.pipe(process.stderr);
        alloy_command.on("exit", function(code) {
          if (code !== 0) {
            logger.error("Alloy Compile Error\n");
            callback(true);
          }
          if (fs.existsSync(config.res_alloy_path)) {
            wrench.copyDirSyncRecursive(
              config.res_alloy_path,
              path.join(config.resources_path,(platform === 'ios' ? 'iphone' : platform),'alloy'),
              {preserve:true,preserveFiles:true}
            );
          }
          callback(false);
        });
        alloy_command.on("error", function() {
          logger.error("Alloy Compile Error\n");
          callback(true);
        });
      },function(failed){
        if (failed) return;
        if (fs.existsSync(config.res_alloy_path)) {
          fs.rm_rf(config.res_alloy_path);
        }
        fs.touch(path.join(config.resources_path,'app.js'));
        alloy.buildMap();
        beginCompile(callback);
      });;
      //Remove non-specific
    } else {
      beginCompile(callback);
    }
  });
};

function beginCompile(callback) {
  var file_list,i18n_list,spec_list,assets_list;
  if( config.isUpdate) {
    var last_stat = fs.statSync(config.last_updated_file);
    file_list = config.isAlloy ? alloy.mapFiles(last_stat) : fs.getList(config.resources_path,last_stat.mtime);
    if (config.isModule)  {
      assets_list = fs.getList(config.assets_path,last_stat.mtime);
    }
    i18n_list = fs.getList(config.i18n_path,last_stat.mtime);
    spec_list = fs.getList(config.spec_path,last_stat.mtime);

    if (file_list.files.length === 0 && assets_list.files.length === 0 && i18n_list.files.length === 0 && spec_list.files.length === 0) {
      logger.warn("Nothing to update.");
      return;
    }
  } else {
    if (!fs.existsSync(config.build_path)){
      fs.mkdirSync(config.build_path, 0755);
    }
    //Clean Build Directory
    if (fs.existsSync(config.tishadow_build)) {
      fs.rm_rf(config.tishadow_build);
    }
    // Create the tishadow build paths
    fs.mkdirs([config.tishadow_build, config.tishadow_src, config.tishadow_dist]);
    file_list = fs.getList(config.resources_path);
    if (config.isModule) {
      assets_list = fs.getList(config.assets_path);
    }
    i18n_list = fs.getList(config.i18n_path);
    spec_list = fs.getList(config.spec_path);
  }

  // Build the required directory structure
  fs.mkdirs(file_list.dirs, config.tishadow_src);
  fs.mkdirs(i18n_list.dirs, config.tishadow_src);
  if(spec_list.files.length > 0) {
    if (!fs.existsSync(config.tishadow_spec)) {
      fs.mkdirSync(config.tishadow_spec, 0755);
    }
    fs.mkdirs(spec_list.dirs, config.tishadow_spec);
    spec_list.files = spec_list.files.map(function(file) { return "spec/" + file;});
    spec_list.dirs = ["spec"].concat(spec_list.dirs.map(function(dir) {return "spec/" + dir;}));
  }

  // prepare tasks to process files
  var process_tasks = file_list.files.map(function(file) {
    return _.bind(prepare, null, path.join(config.resources_path,file), path.join(config.tishadow_src,file));
  }).concat(spec_list.files.map(function(file) {
    return _.bind(prepare, null, path.join(config.base,file), path.join(config.tishadow_src,file));
  }));

  // if acting on a native module - see https://github.com/dbankier/TiShadow/commit/bec799b3d03660704ce6ab303f5e28110b86f051
  if (config.isModule && assets_list.files.length > 0) {
    // create paths
  	fs.mkdirs([config.module_name], config.tishadow_src);
  	fs.mkdirs(assets_list.dirs, config.module_path);
    // add files for processing
    process_tasks = process_tasks.concat(assets_list.files.map(function(file) {
      return _.bind(prepare, null, path.join(config.assets_path,file), path.join(config.tishadow_src, config.module_name, file));
    }));
    console.log(process_tasks);
    // modify for bundling
    assets_list.files = assets_list.files.map(function(file) { return config.module_name + "/" + file;});
  }

  async.series([
    _.bind(async.eachLimit, null, i18n_list.files, 100, copyI18n), //localisation filed
    _.bind(async.parallelLimit, null, process_tasks, 100), // source, assets, specs
    function() {
      file_list.files = file_list.files.concat(i18n_list.files).concat(spec_list.files);
      if (config.isModule) {
        file_list.files = file_list.files.concat(assets_list.files);
      }
      finalise(file_list,callback);
    }
  ]);
};
<commit_msg>another fix for the assests_list pr<commit_after>/*
 * Copyright (c) 2011-2014 YY Digital Pty Ltd. All Rights Reserved.
 * Please see the LICENSE file included with this distribution for details.
 */

var path   = require("path"),
    fs     = require("fs"),
    spawn  = require("./spawn"),
    async  = require("async"),
    alloy  = require("./alloy"),
    api    = require("./api"),
    bundle = require("./bundle"),
    config = require("./config"),
    uglify = require("./uglify"),
    logger = require("../../server/logger.js"),
    jshint = require("./jshint_runner"),
    wrench = require("wrench"),
    _      = require("underscore");

require("./fs_extension");

// Copies all Resource files and prepares JS files
function prepare(src, dst, callback) {
  var app_name = config.app_name;
  if (src.match("js$")){
    try {
      var src_text = uglify.toString(fs.readFileSync(src).toString(),src);
      if (src.match("_spec.js$")) {
        if (config.specType === "jasmine") {
          src_text =  "var __jasmine = require('/lib/jasmine');var methods = ['spyOn','it','xit','expect','runs','waits','waitsFor','beforeEach','afterEach','describe','xdescribe','jasmine'];methods.forEach(function(method) {this[method] = __jasmine[method];});"
          +src_text;
        } else if (config.specType === "mocha-should") {
          src_text =  "var should = require('/lib/should');\n"
            + "var sinon = require('/lib/sinon');\n"
          +src_text;
        } else if (config.specType === "mocha-chai") {
          src_text =  "var chai = require('/lib/chai'); var expect = chai.expect; var assert = chai.assert;\n"
            + "var sinon = require('/lib/sinon');\n"
          +src_text;
        }
      }
      else if(config.runCoverage && !src.match("spec/")) { //Instrumenting the application code with istanbul for code coverage
		var instrumentedCode = require("./coverage").instrumentCode(src_text, src);
		src_text = instrumentedCode;

		config.instrumentedfiles[src] = dst;// storing all instrumented file
      }

      fs.writeFile(dst,src_text, callback);
    } catch (e) {
      logger.error(e.message + "\nFile   : " + src + "\nLine   : " + e.line + "\nColumn : " + e.col);
      config.isWatching || process.exit(1);
    }
  } else { // Non-JS file - just pump it
    var  is = fs.createReadStream(src);
    var  os = fs.createWriteStream(dst);
    is.on("end", callback).pipe(os);
  }
}

function copyI18n(file, callback) {
  var read = fs.createReadStream(path.join(config.i18n_path,file));
  var write = fs.createWriteStream(path.join(config.tishadow_src, file));
  write.on('close',callback);
  read.pipe(write);
};

function finalise(file_list,callback) {
  // Bundle up to go
  var total = file_list.files.length;
  bundle.pack(file_list.files,function(written) {
    logger.info(total+ " file(s) bundled.");
    if (config.isAlloy) {
      alloy.writeMap();
    }
    fs.touch(config.last_updated_file);
    if (config.isBundle) {
      logger.info("Bundle Ready: " + config.bundle_file);
      if (callback) {
        callback();
      }
    } else {
      api.newBundle(config.isPatch?_.filter(file_list.files, function(f) { return f.match(".js$");}):null );
    }
  });
}

module.exports = function(env, callback) {
  config.buildPaths(env, function() {
    if (env.jshint) {
      logger.info("Running JSHint");
      jshint.checkPath(config.jshint_path);
    }

    logger.info("Beginning Build Process");
    // a js map of hashes must be built whether or not it is an update.
    if (config.isAlloy && !config.skipAlloyCompile) {
      if (config.platform === undefined) {
        logger.error("You need to use the --platform (android|ios) flag or have deployment-targets in tiapp.xml with an alloy project.");
        process.exit();
      }
      async.detectSeries(config.platform, function(platform, callback) {
        logger.info("Compiling Alloy for " + platform);
        var alloy_command = spawn('alloy', ['compile', '-b','-l', '1', '--platform', platform, '--config', 'sourcemap=false']);
        alloy_command.stderr.pipe(process.stderr);
        alloy_command.on("exit", function(code) {
          if (code !== 0) {
            logger.error("Alloy Compile Error\n");
            callback(true);
          }
          if (fs.existsSync(config.res_alloy_path)) {
            wrench.copyDirSyncRecursive(
              config.res_alloy_path,
              path.join(config.resources_path,(platform === 'ios' ? 'iphone' : platform),'alloy'),
              {preserve:true,preserveFiles:true}
            );
          }
          callback(false);
        });
        alloy_command.on("error", function() {
          logger.error("Alloy Compile Error\n");
          callback(true);
        });
      },function(failed){
        if (failed) return;
        if (fs.existsSync(config.res_alloy_path)) {
          fs.rm_rf(config.res_alloy_path);
        }
        fs.touch(path.join(config.resources_path,'app.js'));
        alloy.buildMap();
        beginCompile(callback);
      });;
      //Remove non-specific
    } else {
      beginCompile(callback);
    }
  });
};

function beginCompile(callback) {
  var file_list,i18n_list,spec_list,assets_list;
  if( config.isUpdate) {
    var last_stat = fs.statSync(config.last_updated_file);
    file_list = config.isAlloy ? alloy.mapFiles(last_stat) : fs.getList(config.resources_path,last_stat.mtime);
    if (config.isModule)  {
      assets_list = fs.getList(config.assets_path,last_stat.mtime);
    }
    i18n_list = fs.getList(config.i18n_path,last_stat.mtime);
    spec_list = fs.getList(config.spec_path,last_stat.mtime);

    if (file_list.files.length === 0 && (!config.isModule || assets_list.files.length === 0) && i18n_list.files.length === 0 && spec_list.files.length === 0) {
      logger.warn("Nothing to update.");
      return;
    }
  } else {
    if (!fs.existsSync(config.build_path)){
      fs.mkdirSync(config.build_path, 0755);
    }
    //Clean Build Directory
    if (fs.existsSync(config.tishadow_build)) {
      fs.rm_rf(config.tishadow_build);
    }
    // Create the tishadow build paths
    fs.mkdirs([config.tishadow_build, config.tishadow_src, config.tishadow_dist]);
    file_list = fs.getList(config.resources_path);
    if (config.isModule) {
      assets_list = fs.getList(config.assets_path);
    }
    i18n_list = fs.getList(config.i18n_path);
    spec_list = fs.getList(config.spec_path);
  }

  // Build the required directory structure
  fs.mkdirs(file_list.dirs, config.tishadow_src);
  fs.mkdirs(i18n_list.dirs, config.tishadow_src);
  if(spec_list.files.length > 0) {
    if (!fs.existsSync(config.tishadow_spec)) {
      fs.mkdirSync(config.tishadow_spec, 0755);
    }
    fs.mkdirs(spec_list.dirs, config.tishadow_spec);
    spec_list.files = spec_list.files.map(function(file) { return "spec/" + file;});
    spec_list.dirs = ["spec"].concat(spec_list.dirs.map(function(dir) {return "spec/" + dir;}));
  }

  // prepare tasks to process files
  var process_tasks = file_list.files.map(function(file) {
    return _.bind(prepare, null, path.join(config.resources_path,file), path.join(config.tishadow_src,file));
  }).concat(spec_list.files.map(function(file) {
    return _.bind(prepare, null, path.join(config.base,file), path.join(config.tishadow_src,file));
  }));

  // if acting on a native module - see https://github.com/dbankier/TiShadow/commit/bec799b3d03660704ce6ab303f5e28110b86f051
  if (config.isModule && assets_list.files.length > 0) {
    // create paths
  	fs.mkdirs([config.module_name], config.tishadow_src);
  	fs.mkdirs(assets_list.dirs, config.module_path);
    // add files for processing
    process_tasks = process_tasks.concat(assets_list.files.map(function(file) {
      return _.bind(prepare, null, path.join(config.assets_path,file), path.join(config.tishadow_src, config.module_name, file));
    }));
    console.log(process_tasks);
    // modify for bundling
    assets_list.files = assets_list.files.map(function(file) { return config.module_name + "/" + file;});
  }

  async.series([
    _.bind(async.eachLimit, null, i18n_list.files, 100, copyI18n), //localisation filed
    _.bind(async.parallelLimit, null, process_tasks, 100), // source, assets, specs
    function() {
      file_list.files = file_list.files.concat(i18n_list.files).concat(spec_list.files);
      if (config.isModule) {
        file_list.files = file_list.files.concat(assets_list.files);
      }
      finalise(file_list,callback);
    }
  ]);
};
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { Field } from 'redux-form';
import { Row, Col } from '@folio/stripes-components/lib/LayoutGrid';
import Select from '@folio/stripes-components/lib/Select';
import Datepicker from '@folio/stripes-components/lib/Datepicker';
import TextField from '@folio/stripes-components/lib/TextField';
import { Accordion } from '@folio/stripes-components/lib/Accordion';

const EditUserInfo = ({ parentResources, initialValues, expanded, onToggle, accordionId }) => {
  const patronGroups = (parentResources.patronGroups || {}).records || [];
  const patronGroupOptions = (patronGroups || []).map(g => ({ label: `${g.group} (${g.desc})`, value: g.id, selected: initialValues.patronGroup === g.id }));
  const statusOptions = [
    { label: 'Active', value: true },
    { label: 'Inactive', value: false },
  ].map(s => ({ ...s, selected: (initialValues.active === s.value || s.value === true) }));

  return (
    <Accordion
      label="User information"
      open={expanded}
      id={accordionId}
      onToggle={onToggle}
    >
      <Row>
        <Col xs={8}>
          <Row>
            <Col xs={3}>
              <Field label="Last name *" name="personal.lastName" id="adduser_lastname" component={TextField} required fullWidth />
            </Col>
            <Col xs={3}>
              <Field label="First name" name="personal.firstName" id="adduser_firstname" component={TextField} fullWidth />
            </Col>
            <Col xs={3}>
              <Field label="Middle name" name="personal.middleName" id="adduser_middlename" component={TextField} fullWidth />
            </Col>
            <Col xs={3}>
              <Field label="Barcode" name="barcode" id="adduser_barcode" component={TextField} fullWidth />
            </Col>
          </Row>

          <Row>
            <Col xs={3}>
              <Field
                label="Patron group *"
                name="patronGroup"
                id="adduser_group"
                component={Select}
                fullWidth
                dataOptions={[{ label: 'Select patron group', value: '' }, ...patronGroupOptions]}
              />
            </Col>
            <Col xs={3}>
              <Field
                label="Status *"
                name="active"
                id="useractive"
                component={Select}
                fullWidth
                dataOptions={statusOptions}
              />
            </Col>
            <Col xs={3}>
              <Field
                component={Datepicker}
                label="Expiration date"
                dateFormat="YYYY-MM-DD"
                name="expirationDate"
                id="adduser_expirationdate"
              />
            </Col>
            <Col xs={3}>
              <Field label="Username *" name="username" id="adduser_username" component={TextField} required fullWidth />
            </Col>
          </Row>
        </Col>
      </Row>
    </Accordion>
  );
};

EditUserInfo.propTypes = {
  parentResources: PropTypes.object,
  initialValues: PropTypes.object,
  expanded: PropTypes.bool,
  onToggle: PropTypes.func,
  accordionId: PropTypes.string.isRequired,
};

export default EditUserInfo;
<commit_msg>Added 'validStylesEnabled' prop to username field in EditUserInfo<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { Field } from 'redux-form';
import { Row, Col } from '@folio/stripes-components/lib/LayoutGrid';
import Select from '@folio/stripes-components/lib/Select';
import Datepicker from '@folio/stripes-components/lib/Datepicker';
import TextField from '@folio/stripes-components/lib/TextField';
import { Accordion } from '@folio/stripes-components/lib/Accordion';

const EditUserInfo = ({ parentResources, initialValues, expanded, onToggle, accordionId }) => {
  const patronGroups = (parentResources.patronGroups || {}).records || [];
  const patronGroupOptions = (patronGroups || []).map(g => ({ label: `${g.group} (${g.desc})`, value: g.id, selected: initialValues.patronGroup === g.id }));
  const statusOptions = [
    { label: 'Active', value: true },
    { label: 'Inactive', value: false },
  ].map(s => ({ ...s, selected: (initialValues.active === s.value || s.value === true) }));

  return (
    <Accordion
      label="User information"
      open={expanded}
      id={accordionId}
      onToggle={onToggle}
    >
      <Row>
        <Col xs={8}>
          <Row>
            <Col xs={3}>
              <Field label="Last name *" name="personal.lastName" id="adduser_lastname" component={TextField} required fullWidth />
            </Col>
            <Col xs={3}>
              <Field label="First name" name="personal.firstName" id="adduser_firstname" component={TextField} fullWidth />
            </Col>
            <Col xs={3}>
              <Field label="Middle name" name="personal.middleName" id="adduser_middlename" component={TextField} fullWidth />
            </Col>
            <Col xs={3}>
              <Field label="Barcode" name="barcode" id="adduser_barcode" component={TextField} fullWidth />
            </Col>
          </Row>

          <Row>
            <Col xs={3}>
              <Field
                label="Patron group *"
                name="patronGroup"
                id="adduser_group"
                component={Select}
                fullWidth
                dataOptions={[{ label: 'Select patron group', value: '' }, ...patronGroupOptions]}
              />
            </Col>
            <Col xs={3}>
              <Field
                label="Status *"
                name="active"
                id="useractive"
                component={Select}
                fullWidth
                dataOptions={statusOptions}
              />
            </Col>
            <Col xs={3}>
              <Field
                component={Datepicker}
                label="Expiration date"
                dateFormat="YYYY-MM-DD"
                name="expirationDate"
                id="adduser_expirationdate"
              />
            </Col>
            <Col xs={3}>
              <Field label="Username *" name="username" id="adduser_username" component={TextField} validStylesEnabled required fullWidth />
            </Col>
          </Row>
        </Col>
      </Row>
    </Accordion>
  );
};

EditUserInfo.propTypes = {
  parentResources: PropTypes.object,
  initialValues: PropTypes.object,
  expanded: PropTypes.bool,
  onToggle: PropTypes.func,
  accordionId: PropTypes.string.isRequired,
};

export default EditUserInfo;
<|endoftext|>


<commit_before>/// We store a "warehouse" of engines, releases and packages on
/// disk. This warehouse is populated from our servers, as needed.
///
/// Directory structure:
///
///     meteor (relative path symlink to engines/latest/bin/meteor)
///     engines/ (not in checkout, since we run against checked-out code)
///       latest/ (relative path symlink to latest x.y.z/ engine directory)
///       x.y.z/
///     releases/
///       x.y.z.json
///     packages/
///       foo/
///         x.y.z/
///
/// When running from a checkout, there is only one acceptable release - 'none', which
/// has an empty manifest, ensuring that we only load local packages (in CHECKOUT/packages
/// or within a directory in the PACKAGE_DIRS environment variable)

var path = require("path");
var fs = require("fs");
var os = require("os");
var Future = require("fibers/future");
var _ = require("underscore");

// XXX do we really need this __dirname dance? Doesn't
// require("./files.js") work?
var files = require(path.join(__dirname, "files.js"));
var project = require(path.join(__dirname, "project.js"));

var PACKAGES_URLBASE = 'https://packages.meteor.com';

var warehouse = module.exports = {
  // Return our loaded collection of engines, releases and
  // packages. If we're running an installed version, found at
  // $HOME/.meteor.
  getWarehouseDir: function () {
    // a hook for tests
    if (process.env.TEST_WAREHOUSE_DIR)
      return process.env.TEST_WAREHOUSE_DIR;

    // This function should never be called if we're in a checkout.
    if (!files.usesWarehouse())
      throw new Error("There's no warehouse in a git checkout");

    return path.join(process.env.HOME, '.meteor');
  },

  getEngineDir: function (version) {
    return path.join(warehouse.getWarehouseDir(), 'engines', version);
  },

  // If you're running from a git checkout, only accept 'none' and
  // return an empty manifest.  Otherwise, ensure the passed release
  // version is stored in the local warehouse and return its parsed
  // manifest.
  releaseManifestByVersion: function(release) {
    if (release === 'none') {
      if (!files.usesWarehouse()) {
        // safety belt so that we ensure we're running all packages locally,
        // and that we don't springboard
        return {
          engine: 'none',
          packages: {}
        };
      } else {
        throw new Error("The 'none' release can only be used from a git checkout of Meteor");
      }
    }

    var manifestPath = path.join(
      warehouse.getWarehouseDir(), 'releases', release + '.json');

    if (fs.existsSync(manifestPath)) {
      // read from warehouse
      return JSON.parse(fs.readFileSync(manifestPath));
    } else {
      // grow warehouse with new manifest and packages
      return warehouse._populateWarehouseForRelease(release);
    }
  },

  // look in the warehouse for the latest release version. if no
  // releases are found, return null.
  latestRelease: function() {
    var releasesDir = path.join(warehouse.getWarehouseDir(), 'releases');
    if (!fs.existsSync(releasesDir) || !fs.statSync(releasesDir).isDirectory())
      return null;

    var releases = fs.readdirSync(releasesDir);
    var semver = require('semver');

    var latestReleaseVersion = null;
    _.each(releases, function(file) {
      var match = /^(.*)\.json$/.exec(file);
      if (match) {
        var version = match[1];
        if (semver.valid(version) && (!latestReleaseVersion || semver.gt(version, latestReleaseVersion)))
          latestReleaseVersion = version;
      }
    });

    return latestReleaseVersion;
  },

  existsInWarehouse: function (name, version) {
    // Look for presence of "package.js" file in directory so we don't count
    // an empty dir as a package.  An empty dir could be left by a failed
    // package untarring, for example.
    return fs.existsSync(
      path.join(warehouse.getWarehouseDir(), 'packages', name, version, 'package.js'));
  },

  // fetches the manifest file for the given release version. also fetches
  // all of the missing versioned packages referenced from the release manifest
  // @param releaseVersion {String} eg "0.1"
  // @returns {Object} release manifest
  _populateWarehouseForRelease: function(releaseVersion) {
    var future = new Future;
    var releasesDir = path.join(warehouse.getWarehouseDir(), 'releases');
    files.mkdir_p(releasesDir, 0755);
    var releaseManifestPath = path.join(releasesDir, releaseVersion + '.json');

    // get release manifest, but only write it after we're done
    // writing packages
    var releaseManifest;
    try {
      releaseManifest = JSON.parse(Future.wrap(files.getUrl)(
        PACKAGES_URLBASE + "/releases/" + releaseVersion + ".json").wait());
    } catch (e) {
      console.error("Release hasn't been published to Meteor's servers: " + releaseVersion);
      process.exit(1);
    }

    // populate warehouse with engine version for this release
    var engineVersion = releaseManifest.engine;
    if (engineVersion !== files.getEngineVersion()) {
      try {
        // XXX this sucks. We store both the engine tarball *and* the uncompressed
        // vanilla tar file contents in memory. This is huge (>100MB). We should
        // instead use streams, especially in files.extractTarGz. Since the node
        // stream API is in flux, we should probably wait a bit.
        // http://blog.nodejs.org/2012/12/20/streams2/

        var engineTarballFilename =
            "meteor-engine-" + releaseManifest.engine + "-" +
            warehouse._unameAndArch() + ".tar.gz";
        var engineTarballPath = "/engines/" + releaseManifest.engine + "/"
              + engineTarballFilename;
        var engineTarball = Future.wrap(files.getUrl)({
          url: PACKAGES_URLBASE + engineTarballPath,
          encoding: null
        }).wait();
        var engineDir = warehouse.getEngineDir(engineVersion);
        // use a temp dir to avoid getting a corrupt warehouse
        var tmpEngineDir = warehouse.getEngineDir(
          ".tmp" + warehouse._randomToken());
        files.mkdir_p(tmpEngineDir);
        files.extractTarGz(engineTarball, tmpEngineDir);
        fs.renameSync(path.join(tmpEngineDir, releaseManifest.engine), engineDir);
      } catch (e) {
        console.error("Failed to load engine for release " + releaseVersion);
        throw e;
      }
    }

    // populate warehouse with missing packages
    try {
      var missingPackages = {};
      _.each(releaseManifest.packages, function (version, name) {
        if (!warehouse.existsInWarehouse(name, version)) {
          missingPackages[name] = version;
        }
      });
      warehouse._populateWarehouseWithPackages(missingPackages);
    } catch (e) {
      console.error("Failed to load packages for release " + releaseVersion);
      throw e;
    }

    // now that we have written all packages, it's safe to write the
    // release manifest
    fs.writeFileSync(releaseManifestPath, JSON.stringify(releaseManifest));

    // return manifest
    return releaseManifest;
  },

  // @param packagesToPopulate {Object} eg {"less": "0.5.0"}
  _populateWarehouseWithPackages: function(packagesToPopulate) {
    var Future = require('fibers/future');
    var futures = [];
    _.each(packagesToPopulate, function (version, name) {
      var packageDir = path.join(warehouse.getWarehouseDir(), 'packages', name, version);
      var packageUrl = PACKAGES_URLBASE + "/packages/" + name + "/" +
            name + '-' + version + ".tar.gz";

      console.log("Fetching " + packageUrl + "...");
      futures.push(Future.wrap(function (cb) {
        files.getUrl({url: packageUrl, encoding: null}, function (error, result) {
          if (! error && result)
            result = { buffer: result, packageDir: packageDir, name: name };
          cb(error, result);
        });
      })());
    });

    Future.wait(futures);

    _.each(futures, function (f) {
      var result = f.get();
      // extract to a temporary directory and then rename, to ensure
      // we don't end up with a corrupt warehouse
      var tmpPackageDir = result.packageDir + ".tmp" + warehouse._randomToken();
      files.mkdir_p(tmpPackageDir);
      files.extractTarGz(result.buffer, tmpPackageDir);
      fs.renameSync(tmpPackageDir, result.packageDir);

      // fetch npm dependencies
      var packages = require(path.join(__dirname, "packages.js")); // load late to work around circular require
      var pkg = packages.loadFromDir(result.name, result.packageDir);
      pkg.installNpmDependencies();
    });
  },

  _randomToken: function() {
    return (Math.random() * 0x100000000 + 1).toString(36);
  },

  _unameAndArch: function () {
    // Normalize from Node "os.arch()" to "uname -m".
    var arch = os.arch();
    if (arch === "ia32")
      arch = "i686";
    else if (arch === "x64")
      arch = "x86_64";
    else
      throw new Error("Unsupported architecture " + arch);
    return os.type() + "-" + arch;
  }
};
<commit_msg>Improve error when package not found in PACKAGE_DIRS locally<commit_after>/// We store a "warehouse" of engines, releases and packages on
/// disk. This warehouse is populated from our servers, as needed.
///
/// Directory structure:
///
///     meteor (relative path symlink to engines/latest/bin/meteor)
///     engines/ (not in checkout, since we run against checked-out code)
///       latest/ (relative path symlink to latest x.y.z/ engine directory)
///       x.y.z/
///     releases/
///       x.y.z.json
///     packages/
///       foo/
///         x.y.z/
///
/// When running from a checkout, there is only one acceptable release - 'none', which
/// has an empty manifest, ensuring that we only load local packages (in CHECKOUT/packages
/// or within a directory in the PACKAGE_DIRS environment variable)

var path = require("path");
var fs = require("fs");
var os = require("os");
var Future = require("fibers/future");
var _ = require("underscore");

// XXX do we really need this __dirname dance? Doesn't
// require("./files.js") work?
var files = require(path.join(__dirname, "files.js"));
var project = require(path.join(__dirname, "project.js"));

var PACKAGES_URLBASE = 'https://packages.meteor.com';

var warehouse = module.exports = {
  // Return our loaded collection of engines, releases and
  // packages. If we're running an installed version, found at
  // $HOME/.meteor.
  getWarehouseDir: function () {
    // a hook for tests
    if (process.env.TEST_WAREHOUSE_DIR)
      return process.env.TEST_WAREHOUSE_DIR;

    // This function should never be called if we're in a checkout.
    if (!files.usesWarehouse())
      throw new Error("There's no warehouse in a git checkout");

    return path.join(process.env.HOME, '.meteor');
  },

  getEngineDir: function (version) {
    return path.join(warehouse.getWarehouseDir(), 'engines', version);
  },

  // If you're running from a git checkout, only accept 'none' and
  // return an empty manifest.  Otherwise, ensure the passed release
  // version is stored in the local warehouse and return its parsed
  // manifest.
  releaseManifestByVersion: function(release) {
    if (release === 'none') {
      if (!files.usesWarehouse()) {
        return null;
      } else {
        throw new Error("The 'none' release can only be used from a git checkout of Meteor");
      }
    }

    var manifestPath = path.join(
      warehouse.getWarehouseDir(), 'releases', release + '.json');

    if (fs.existsSync(manifestPath)) {
      // read from warehouse
      return JSON.parse(fs.readFileSync(manifestPath));
    } else {
      // grow warehouse with new manifest and packages
      return warehouse._populateWarehouseForRelease(release);
    }
  },

  // look in the warehouse for the latest release version. if no
  // releases are found, return null.
  latestRelease: function() {
    var releasesDir = path.join(warehouse.getWarehouseDir(), 'releases');
    if (!fs.existsSync(releasesDir) || !fs.statSync(releasesDir).isDirectory())
      return null;

    var releases = fs.readdirSync(releasesDir);
    var semver = require('semver');

    var latestReleaseVersion = null;
    _.each(releases, function(file) {
      var match = /^(.*)\.json$/.exec(file);
      if (match) {
        var version = match[1];
        if (semver.valid(version) && (!latestReleaseVersion || semver.gt(version, latestReleaseVersion)))
          latestReleaseVersion = version;
      }
    });

    return latestReleaseVersion;
  },

  existsInWarehouse: function (name, version) {
    // Look for presence of "package.js" file in directory so we don't count
    // an empty dir as a package.  An empty dir could be left by a failed
    // package untarring, for example.
    return fs.existsSync(
      path.join(warehouse.getWarehouseDir(), 'packages', name, version, 'package.js'));
  },

  // fetches the manifest file for the given release version. also fetches
  // all of the missing versioned packages referenced from the release manifest
  // @param releaseVersion {String} eg "0.1"
  // @returns {Object} release manifest
  _populateWarehouseForRelease: function(releaseVersion) {
    var future = new Future;
    var releasesDir = path.join(warehouse.getWarehouseDir(), 'releases');
    files.mkdir_p(releasesDir, 0755);
    var releaseManifestPath = path.join(releasesDir, releaseVersion + '.json');

    // get release manifest, but only write it after we're done
    // writing packages
    var releaseManifest;
    try {
      releaseManifest = JSON.parse(Future.wrap(files.getUrl)(
        PACKAGES_URLBASE + "/releases/" + releaseVersion + ".json").wait());
    } catch (e) {
      console.error("Release hasn't been published to Meteor's servers: " + releaseVersion);
      process.exit(1);
    }

    // populate warehouse with engine version for this release
    var engineVersion = releaseManifest.engine;
    if (engineVersion !== files.getEngineVersion()) {
      try {
        // XXX this sucks. We store both the engine tarball *and* the uncompressed
        // vanilla tar file contents in memory. This is huge (>100MB). We should
        // instead use streams, especially in files.extractTarGz. Since the node
        // stream API is in flux, we should probably wait a bit.
        // http://blog.nodejs.org/2012/12/20/streams2/

        var engineTarballFilename =
            "meteor-engine-" + releaseManifest.engine + "-" +
            warehouse._unameAndArch() + ".tar.gz";
        var engineTarballPath = "/engines/" + releaseManifest.engine + "/"
              + engineTarballFilename;
        var engineTarball = Future.wrap(files.getUrl)({
          url: PACKAGES_URLBASE + engineTarballPath,
          encoding: null
        }).wait();
        var engineDir = warehouse.getEngineDir(engineVersion);
        // use a temp dir to avoid getting a corrupt warehouse
        var tmpEngineDir = warehouse.getEngineDir(
          ".tmp" + warehouse._randomToken());
        files.mkdir_p(tmpEngineDir);
        files.extractTarGz(engineTarball, tmpEngineDir);
        fs.renameSync(path.join(tmpEngineDir, releaseManifest.engine), engineDir);
      } catch (e) {
        console.error("Failed to load engine for release " + releaseVersion);
        throw e;
      }
    }

    // populate warehouse with missing packages
    try {
      var missingPackages = {};
      _.each(releaseManifest.packages, function (version, name) {
        if (!warehouse.existsInWarehouse(name, version)) {
          missingPackages[name] = version;
        }
      });
      warehouse._populateWarehouseWithPackages(missingPackages);
    } catch (e) {
      console.error("Failed to load packages for release " + releaseVersion);
      throw e;
    }

    // now that we have written all packages, it's safe to write the
    // release manifest
    fs.writeFileSync(releaseManifestPath, JSON.stringify(releaseManifest));

    // return manifest
    return releaseManifest;
  },

  // @param packagesToPopulate {Object} eg {"less": "0.5.0"}
  _populateWarehouseWithPackages: function(packagesToPopulate) {
    var Future = require('fibers/future');
    var futures = [];
    _.each(packagesToPopulate, function (version, name) {
      var packageDir = path.join(warehouse.getWarehouseDir(), 'packages', name, version);
      var packageUrl = PACKAGES_URLBASE + "/packages/" + name + "/" +
            name + '-' + version + ".tar.gz";

      console.log("Fetching " + packageUrl + "...");
      futures.push(Future.wrap(function (cb) {
        files.getUrl({url: packageUrl, encoding: null}, function (error, result) {
          if (! error && result)
            result = { buffer: result, packageDir: packageDir, name: name };
          cb(error, result);
        });
      })());
    });

    Future.wait(futures);

    _.each(futures, function (f) {
      var result = f.get();
      // extract to a temporary directory and then rename, to ensure
      // we don't end up with a corrupt warehouse
      var tmpPackageDir = result.packageDir + ".tmp" + warehouse._randomToken();
      files.mkdir_p(tmpPackageDir);
      files.extractTarGz(result.buffer, tmpPackageDir);
      fs.renameSync(tmpPackageDir, result.packageDir);

      // fetch npm dependencies
      var packages = require(path.join(__dirname, "packages.js")); // load late to work around circular require
      var pkg = packages.loadFromDir(result.name, result.packageDir);
      pkg.installNpmDependencies();
    });
  },

  _randomToken: function() {
    return (Math.random() * 0x100000000 + 1).toString(36);
  },

  _unameAndArch: function () {
    // Normalize from Node "os.arch()" to "uname -m".
    var arch = os.arch();
    if (arch === "ia32")
      arch = "i686";
    else if (arch === "x64")
      arch = "x86_64";
    else
      throw new Error("Unsupported architecture " + arch);
    return os.type() + "-" + arch;
  }
};
<|endoftext|>


<commit_before>/*
 * Copyright 2014 XWiki SAS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
var ChainPad = require('./ChainPad');
var Common = require('./Common');
var Operation = require('./Operation');
var Sha = require('./SHA256');
var nThen = require('nthen');

var startup = function (callback) {
    var rt = ChainPad.create('x','y','abc','abc');
    rt.abort();
    callback();
};

var runOperation = function (realtimeFacade, op) {
    realtimeFacade.patch(op.offset, op.toRemove, op.toInsert);
};

var insert = function (doc, offset, chars) {
    return doc.substring(0,offset) + chars + doc.substring(offset);
};

var remove = function (doc, offset, count) {
    return doc.substring(0,offset) + doc.substring(offset+count);
};

var registerNode = function (name, initialDoc) {
    var rt = ChainPad.create(name,'y','abc',initialDoc);
    onMsg = rt.onMessage;
    var handlers = [];
    onMsg(function (msg) {
        setTimeout(function () {
            if (msg === ('1:y' + name.length + ':' + name + '3:abc3:[0]')) {
                // registration
                rt.message('0:3:abc3:[1]');
            } else {
                msg = msg.substring(3); //replace(/^1:y/, '');
                handlers.forEach(function (handler) { handler(msg); });
            }
        });
    });
    rt.onMessage = function (handler) {
        handlers.push(handler);
    }

    rt.doc = initialDoc;

    rt.onPatch(function () { rt.doc = rt.getUserDoc() });
    return rt;
};

var editing = function (callback) {
    var doc = '';
    var rt = registerNode('editing()', '');
    var messages = 0;
    rt.onMessage(function (msg) {
        messages++;
        rt.message(msg);
    });
    rt.start();

    var i = 0;
    var to = setInterval(function () {
        if (i++ > 10) {
            clearTimeout(to);
            for (var j = 0; j < 100; j++) {
                var m = messages;
                rt.sync();
                if (m === messages) {
                    rt.abort();
                    callback();
                    return;
                }
            }
            throw new Error();
        }
        // fire off another operation
        var op = Operation.random(doc.length);
        doc = Operation.apply(op, doc);
        runOperation(rt, op);
        rt.sync();
    },1);

};

var fakeSetTimeout = function (func, time) {
    var i = time;
    var tick = function () { if (i-- <= 0) { func() } else { setTimeout(tick); } };
    setTimeout(tick);
};

var twoClientsCycle = function (callback, origDocA, origDocB) {
    var rtA = registerNode('twoClients(rtA)', origDocA);
    var rtB = registerNode('twoClients(rtB)', origDocB);
    rtA.queue = [];
    rtB.queue = [];
    var messages = 0;

    var onMsg = function (rt, msg) {
        messages+=2;
        var m = msg.replace(/^1:y/, '');
        fakeSetTimeout(function () {
            messages--;
            rtA.queue.push(m);
            fakeSetTimeout(function () { rtA.message(rtA.queue.shift()); }, Math.random() * 100);
        }, Math.random() * 100);
        fakeSetTimeout(function () {
            messages--;
            rtB.queue.push(m);
            fakeSetTimeout(function () { rtB.message(rtB.queue.shift()); }, Math.random() * 100);
        }, Math.random() * 100);
    };
    [rtA, rtB].forEach(function (rt) {
        rt.onMessage(function (msg) { onMsg(rt, msg) });
        rt.start();
    });

    var i = 0;
    var to = setInterval(function () {
        if (i++ > 100) {
            clearTimeout(to);
            var j = 0;
            var flushCounter = 0;
            var again = function () {
                if (++j > 10000) { throw new Error("never synced"); }
                rtA.sync();
                rtB.sync();
                if (messages === 0 && rtA.queue.length === 0 && rtB.queue.length === 0 && flushCounter++ > 100) {
                    console.log(rtA.doc);
                    console.log(rtB.doc);
                    Common.assert(rtA.doc === rtB.doc);
                    rtA.abort();
                    rtB.abort();
                    callback();
                    return;
                } else {
                    setTimeout(again);
                }
            };
            again();
        }

//console.log(JSON.stringify([rtA.doc, rtB.doc]));

        var rt = (Math.random() > 0.5) ? rtA : rtB;

        var op = Operation.random(rt.doc.length);
        rt.doc = Operation.apply(op, rt.doc);
        runOperation(rt, op);

        if (Math.random() > 0.8) {
            rt.sync();
        }
    },1);

};

var twoClients = function (cycles, callback) {
    var i = 0;
    var again = function () {
        if (++i >= cycles) { again = callback; }
        var docA = Common.randomASCII(Math.floor(Math.random()*20));
        var docB = Common.randomASCII(Math.floor(Math.random()*20));
        twoClientsCycle(again, docA, docB);
    };
    again();
};

var syncCycle = function (messages, finalDoc, name, callback) {
    var rt = registerNode(name, '');
    for (var i = 0; i < messages.length; i++) {
        rt.message(messages[i]);
    }
    setTimeout(function () {
        Common.assert(rt.doc === finalDoc);
        rt.abort();
        callback();
    });
};

var outOfOrderSync = function (callback) {
    var messages = [];
    var rtA = registerNode('outOfOrderSync()', '');
    rtA.onMessage(function (msg) {
        rtA.message(msg);
        messages.push(msg);
    });
    var i = 0;
    rtA.start();

    var finish = function () {
        rtA.abort();
        var i = 0;
        var cycle = function () {
            if (i++ > 10) {
                callback();
                return;
            }
            // first sync is in order
            syncCycle(messages, rtA.doc, 'outOfOrderSync(rt'+i+')', function () {
                for (var j = 0; j < messages.length; j++) {
                    var k = Math.floor(Math.random() * messages.length);
                    var m = messages[k];
                    messages[k] = messages[j];
                    messages[j] = m;
                }
                cycle();
            });
        };
        cycle();
    };

    var again = function () {
        setTimeout( (i++ < 150) ? again : finish );
        if (i < 100) {
            var op = Operation.random(rtA.doc.length);
            rtA.doc = Operation.apply(op, rtA.doc);
            runOperation(rtA, op);
        }
        rtA.sync();
    };
    again();
};

var checkVersionInChain = function (callback) {
    var doc = '';
// create a chainpad
    var rt = registerNode('editing()', '');
    var messages = 0;
    rt.onMessage(function (msg) {
        messages++;
        rt.message(msg);
    });
    rt.start();

    var i = 0;
    var oldUserDoc;
    var to = setInterval(function () {
// on the 51st change, grab the doc
        if (i === 50) {
            oldUserDoc = rt.getUserDoc();
        }
// on the 100th random change, check whether the 50th existed before
        if (i++ > 100) {
            clearTimeout(to);
            Common.assert(rt.getDepthOfState(oldUserDoc) !== -1);
            Common.assert(rt.getDepthOfState(rt.getUserDoc()) !== -1)
            rt.abort();
            callback();
            return;
        }

        // fire off another operation
        var op = Operation.random(doc.length);
        doc = Operation.apply(op, doc);
        runOperation(rt, op);
        rt.sync();
    },1);

};

var whichStateIsDeeper = function (callback) {
// create a chainpad
    var rt = registerNode('editing()', '');
    var messages = 0;
    rt.onMessage(function (msg) {
        messages++;
        rt.message(msg);
    });
    rt.start();

    var doc = '',
        docO = doc,
        docA,
        docB;

    var i = 0;

    var to = setInterval(function () {
        if (i === 25) {
            // grab docO
            docO = rt.getUserDoc();
        } else if (i === 50) {
            // grab docA
            docA = rt.getUserDoc();
            console.log("Got Document A");
            console.log(docA);

            Common.assert(rt.getDepthOfState(docA) === 0);
            Common.assert(rt.getDepthOfState(docO) === 25);
        } else if (i === 75) {
            // grab docB
            docB = rt.getUserDoc();
            console.log("Got Document B");
            console.log(docB);

            // state assertions
            Common.assert(rt.getDepthOfState(docB) === 0);
            Common.assert(rt.getDepthOfState(docA) === 25);
            Common.assert(rt.getDepthOfState(docO) === 50);
        } else if (i >= 100) {
            // finish
            clearTimeout(to);

            Common.assert(rt.getDepthOfState(docB) === 25);
            Common.assert(rt.getDepthOfState(docA) === 50);
            Common.assert(rt.getDepthOfState(docO) === 75);

            rt.abort();
            callback();
            return;
        }

        i++;
        var op = Operation.random(doc.length);
        doc = Operation.apply(op,doc);
        runOperation(rt, op);
        rt.sync();
    },1);
};

var main = module.exports.main = function (cycles, callback) {
    nThen(function (waitFor) {
        startup(waitFor());
    }).nThen(function (waitFor) {
        editing(waitFor());
    }).nThen(function (waitFor) {
        twoClients(cycles, waitFor());
    }).nThen(function (waitFor) {
        outOfOrderSync(waitFor());
    }).nThen(function (waitFor) {
        checkVersionInChain(waitFor);
    }).nThen(function (waitFor) {
        whichStateIsDeeper(waitFor);
    }).nThen(callback);
};
<commit_msg>change tests to use config object for chainpad initialization<commit_after>/*
 * Copyright 2014 XWiki SAS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
var ChainPad = require('./ChainPad');
var Common = require('./Common');
var Operation = require('./Operation');
var Sha = require('./SHA256');
var nThen = require('nthen');

var startup = function (callback) {
    var rt = ChainPad.create({
        userName: 'x',
        channelId: 'abc',
        initialState: 'abc'
    });
    rt.abort();
    callback();
};

var runOperation = function (realtimeFacade, op) {
    realtimeFacade.patch(op.offset, op.toRemove, op.toInsert);
};

var insert = function (doc, offset, chars) {
    return doc.substring(0,offset) + chars + doc.substring(offset);
};

var remove = function (doc, offset, count) {
    return doc.substring(0,offset) + doc.substring(offset+count);
};

var registerNode = function (name, initialDoc) {
    var rt = ChainPad.create({
        userName: name,
        channelId:'abc',
        initialState: initialDoc
    });
    onMsg = rt.onMessage;
    var handlers = [];
    onMsg(function (msg) {
        setTimeout(function () {
                msg = msg.replace(/^0:/,''); //substring(2); //replace(/^1:y/, '');
                handlers.forEach(function (handler) { handler(msg); });
        });
    });
    rt.onMessage = function (handler) {
        handlers.push(handler);
    }

    rt.doc = initialDoc;

    rt.onPatch(function () { rt.doc = rt.getUserDoc() });
    return rt;
};

var editing = function (callback) {
    var doc = '';
    var rt = registerNode('editing()', '');
    var messages = 0;
    rt.onMessage(function (msg) {
        messages++;
        rt.message(msg);
    });
    rt.start();

    var i = 0;
    var to = setInterval(function () {
        if (i++ > 10) {
            clearTimeout(to);
            for (var j = 0; j < 100; j++) {
                var m = messages;
                rt.sync();
                if (m === messages) {
                    rt.abort();
                    callback();
                    return;
                }
            }
            throw new Error();
        }
        // fire off another operation
        var op = Operation.random(doc.length);
        doc = Operation.apply(op, doc);
        runOperation(rt, op);
        rt.sync();
    },1);

};

var fakeSetTimeout = function (func, time) {
    var i = time;
    var tick = function () { if (i-- <= 0) { func() } else { setTimeout(tick); } };
    setTimeout(tick);
};

var twoClientsCycle = function (callback, origDocA, origDocB) {
    var rtA = registerNode('twoClients(rtA)', origDocA);
    var rtB = registerNode('twoClients(rtB)', origDocB);
    rtA.queue = [];
    rtB.queue = [];
    var messages = 0;

    var onMsg = function (rt, msg) {
        messages+=2;
        var m = msg.replace(/^1:y/, '');
        fakeSetTimeout(function () {
            messages--;
            rtA.queue.push(m);
            fakeSetTimeout(function () { rtA.message(rtA.queue.shift()); }, Math.random() * 100);
        }, Math.random() * 100);
        fakeSetTimeout(function () {
            messages--;
            rtB.queue.push(m);
            fakeSetTimeout(function () { rtB.message(rtB.queue.shift()); }, Math.random() * 100);
        }, Math.random() * 100);
    };
    [rtA, rtB].forEach(function (rt) {
        rt.onMessage(function (msg) { onMsg(rt, msg) });
        rt.start();
    });

    var i = 0;
    var to = setInterval(function () {
        if (i++ > 100) {
            clearTimeout(to);
            var j = 0;
            var flushCounter = 0;
            var again = function () {
                if (++j > 10000) { throw new Error("never synced"); }
                rtA.sync();
                rtB.sync();
                if (messages === 0 && rtA.queue.length === 0 && rtB.queue.length === 0 && flushCounter++ > 100) {
                    console.log(rtA.doc);
                    console.log(rtB.doc);
                    Common.assert(rtA.doc === rtB.doc);
                    rtA.abort();
                    rtB.abort();
                    callback();
                    return;
                } else {
                    setTimeout(again);
                }
            };
            again();
        }

//console.log(JSON.stringify([rtA.doc, rtB.doc]));

        var rt = (Math.random() > 0.5) ? rtA : rtB;

        var op = Operation.random(rt.doc.length);
        rt.doc = Operation.apply(op, rt.doc);
        runOperation(rt, op);

        if (Math.random() > 0.8) {
            rt.sync();
        }
    },1);

};

var twoClients = function (cycles, callback) {
    var i = 0;
    var again = function () {
        if (++i >= cycles) { again = callback; }
        var docA = Common.randomASCII(Math.floor(Math.random()*20));
        var docB = Common.randomASCII(Math.floor(Math.random()*20));
        twoClientsCycle(again, docA, docB);
    };
    again();
};

var syncCycle = function (messages, finalDoc, name, callback) {
    var rt = registerNode(name, '');
    for (var i = 0; i < messages.length; i++) {
        rt.message(messages[i]);
    }
    setTimeout(function () {
        Common.assert(rt.doc === finalDoc);
        rt.abort();
        callback();
    });
};

var outOfOrderSync = function (callback) {
    var messages = [];
    var rtA = registerNode('outOfOrderSync()', '');
    rtA.onMessage(function (msg) {
        rtA.message(msg);
        messages.push(msg);
    });
    var i = 0;
    rtA.start();

    var finish = function () {
        rtA.abort();
        var i = 0;
        var cycle = function () {
            if (i++ > 10) {
                callback();
                return;
            }
            // first sync is in order
            syncCycle(messages, rtA.doc, 'outOfOrderSync(rt'+i+')', function () {
                for (var j = 0; j < messages.length; j++) {
                    var k = Math.floor(Math.random() * messages.length);
                    var m = messages[k];
                    messages[k] = messages[j];
                    messages[j] = m;
                }
                cycle();
            });
        };
        cycle();
    };

    var again = function () {
        setTimeout( (i++ < 150) ? again : finish );
        if (i < 100) {
            var op = Operation.random(rtA.doc.length);
            rtA.doc = Operation.apply(op, rtA.doc);
            runOperation(rtA, op);
        }
        rtA.sync();
    };
    again();
};

var checkVersionInChain = function (callback) {
    var doc = '';
// create a chainpad
    var rt = registerNode('editing()', '');
    var messages = 0;
    rt.onMessage(function (msg) {
        messages++;
        rt.message(msg);
    });
    rt.start();

    var i = 0;
    var oldUserDoc;
    var to = setInterval(function () {
// on the 51st change, grab the doc
        if (i === 50) {
            oldUserDoc = rt.getUserDoc();
        }
// on the 100th random change, check whether the 50th existed before
        if (i++ > 100) {
            clearTimeout(to);
            Common.assert(rt.getDepthOfState(oldUserDoc) !== -1);
            Common.assert(rt.getDepthOfState(rt.getUserDoc()) !== -1)
            rt.abort();
            callback();
            return;
        }

        // fire off another operation
        var op = Operation.random(doc.length);
        doc = Operation.apply(op, doc);
        runOperation(rt, op);
        rt.sync();
    },1);

};

var whichStateIsDeeper = function (callback) {
// create a chainpad
    var rt = registerNode('editing()', '');
    var messages = 0;
    rt.onMessage(function (msg) {
        messages++;
        rt.message(msg);
    });
    rt.start();

    var doc = '',
        docO = doc,
        docA,
        docB;

    var i = 0;

    var to = setInterval(function () {
        if (i === 25) {
            // grab docO
            docO = rt.getUserDoc();
        } else if (i === 50) {
            // grab docA
            docA = rt.getUserDoc();
            console.log("Got Document A");
            console.log(docA);

            Common.assert(rt.getDepthOfState(docA) === 0);
            Common.assert(rt.getDepthOfState(docO) === 25);
        } else if (i === 75) {
            // grab docB
            docB = rt.getUserDoc();
            console.log("Got Document B");
            console.log(docB);

            // state assertions
            Common.assert(rt.getDepthOfState(docB) === 0);
            Common.assert(rt.getDepthOfState(docA) === 25);
            Common.assert(rt.getDepthOfState(docO) === 50);
        } else if (i >= 100) {
            // finish
            clearTimeout(to);

            Common.assert(rt.getDepthOfState(docB) === 25);
            Common.assert(rt.getDepthOfState(docA) === 50);
            Common.assert(rt.getDepthOfState(docO) === 75);

            rt.abort();
            callback();
            return;
        }

        i++;
        var op = Operation.random(doc.length);
        doc = Operation.apply(op,doc);
        runOperation(rt, op);
        rt.sync();
    },1);
};

var main = module.exports.main = function (cycles, callback) {
    nThen(function (waitFor) {
        startup(waitFor());
    }).nThen(function (waitFor) {
        editing(waitFor());
    }).nThen(function (waitFor) {
        twoClients(cycles, waitFor());
    }).nThen(function (waitFor) {
        outOfOrderSync(waitFor());
    }).nThen(function (waitFor) {
        checkVersionInChain(waitFor);
    }).nThen(function (waitFor) {
        whichStateIsDeeper(waitFor);
    }).nThen(callback);
};
<|endoftext|>


<commit_before>/*
 * This is a collection of well known SMTP service providers
 */

module.exports = {
    "Gmail":{
        host: "smtp.gmail.com",
        secureConnection: true,
        port: 465,
        requiresAuth: true
    },
    "Yahoo":{
        host: "smtp.mail.yahoo.com",
        secureConnection: true,
        port: 465,
        requiresAuth: true
    },
    "Hotmail":{
        host: "smtp.live.com",
        port: 587,
        requiresAuth: true
    },
    "hot.ee":{
        host: "mail.hot.ee",
        requiresAuth: true
    },
    "mail.ee":{
        host: "smtp.mail.ee",
        requiresAuth: true
    },
    "SES":{
        host: "email-smtp.us-east-1.amazonaws.com",
        secureConnection: true,
        port: 465,
        requiresAuth: true
    },
    "Zoho":{
        host: "smtp.zoho.com",
        secureConnection: true,
        port: 465,
        requiresAuth: true
    },
    "iCloud":{
        host:"smtp.mail.me.com",
        port: 587,
        requiresAuth: true
    },
    "SendGrid":{
        host: "smtp.sendgrid.net",
        secureConnection: true,
        port: 465,
        requiresAuth: true
    },
    "Postmark":{
        host: "smtp.postmarkapp.com",
        port: 25,
        requiresAuth: true
    }
};<commit_msg>SendGrid recommends to use port 587, see http://docs.sendgrid.com/documentation/get-started/integrate/<commit_after>/*
 * This is a collection of well known SMTP service providers
 */

module.exports = {
    "Gmail":{
        host: "smtp.gmail.com",
        secureConnection: true,
        port: 465,
        requiresAuth: true
    },
    "Yahoo":{
        host: "smtp.mail.yahoo.com",
        secureConnection: true,
        port: 465,
        requiresAuth: true
    },
    "Hotmail":{
        host: "smtp.live.com",
        port: 587,
        requiresAuth: true
    },
    "hot.ee":{
        host: "mail.hot.ee",
        requiresAuth: true
    },
    "mail.ee":{
        host: "smtp.mail.ee",
        requiresAuth: true
    },
    "SES":{
        host: "email-smtp.us-east-1.amazonaws.com",
        secureConnection: true,
        port: 465,
        requiresAuth: true
    },
    "Zoho":{
        host: "smtp.zoho.com",
        secureConnection: true,
        port: 465,
        requiresAuth: true
    },
    "iCloud":{
        host:"smtp.mail.me.com",
        port: 587,
        requiresAuth: true
    },
    "SendGrid":{
        host: "smtp.sendgrid.net",
        secureConnection: true,
        port: 587,
        requiresAuth: true
    },
    "Postmark":{
        host: "smtp.postmarkapp.com",
        port: 25,
        requiresAuth: true
    }
};<|endoftext|>