<commit_before>/* global angular */
'use strict';

function HttpClientService($q, $http, $rootScope, HttpRequestQueueService) {

  var methods = {};

  function getRequest(method, url, params, data){
    var request = {
      content: {
        method: method,
        url: url
      },
      params: params
    };
    if (data){
      request.content.data = data;
    }
    return request;
  }

  // Callbacks
  var primaryCallback, secondaryCallback, defaultCallback, onlineCallback;

  // Recursive method to empty the queue
  function executeRequests() {
    var headRequest = HttpRequestQueueService.getHead();
    if (headRequest){
      $http(headRequest.content).
        success(function(data /*, status, headers, config*/) {
          // First, execute callback
          if (headRequest.primary && primaryCallback){
            primaryCallback(headRequest, data);
          }else if (headRequest.secondary && secondaryCallback){
            secondaryCallback(headRequest, data, HttpRequestQueueService.getQueue());
            HttpRequestQueueService.saveQueue();
          }else if (defaultCallback){
            defaultCallback(headRequest, data, HttpRequestQueueService.getQueue());
            HttpRequestQueueService.saveQueue();
          }
          // Then remove the request from queue and release lock
          HttpRequestQueueService.remove(headRequest);

          // Execute online callback
          if (onlineCallback) {
            onlineCallback(true);
          }

          // Try to execute the next request in the queue
          executeRequests();
        }).
        error(function(data, status/*, headers, config*/) {
          if (status === 404 || status === 502){
            // Seems to be offline, stop processing
            HttpRequestQueueService.setOffline(headRequest);
            // Execute callback
            if (onlineCallback) {
              onlineCallback(false);
            }
          }else {
            // Emit unsuspected error to root scope, so that
            // it can be listened on at by the application
            $rootScope.$emit('emException', {type: 'http', status: status, data: data});
          }
        });
    }
  }

  // GET, HEAD and JSONP return chained promises
  angular.forEach(['get', 'head', 'jsonp'], function(name) {
    methods[name] = function(url) {
      return $http({method: name, url: url}).then(function(success) {
        return success;
      });
    };
  });

  // Online alternatives for POST, PUT and DELETE
  methods.postOnline = function(url, data, skipLogStatus) {
    return $http({method: 'post', url: url, data: data}).then(function(success) {
      return success;
    }, function(error) {
      if(!(error.status === skipLogStatus)){
        $rootScope.$emit('emException', {type: 'http', status: error.status, data: error.data});
      }
      return $q.reject(error);
    });
  };

  methods.putOnline = function(url, data) {
    return $http({method: 'put', url: url, data: data}).then(function(success) {
      return success;
    }, function(error) {
      $rootScope.$emit('emException', {type: 'http', status: error.status, data: error.data});
      return error;
    });
  };

  methods.deleteOnline = function(url) {
    return $http({method: 'delete', url: url}).then(function(success) {
      return success;
    }, function(error) {
      $rootScope.$emit('emException', {type: 'http', status: error.status, data: error.data});
      return error;
    });
  };

  // Custom method for a primary POST, i.e. authentication
  methods.postPrimary = function (url, data) {
    var request = getRequest('post', url, undefined, data);
    request.primary = true;
    HttpRequestQueueService.push(request);
    executeRequests();
  };

  // Custom method for secondary GET, i.e. delta getter
  methods.getSecondary = function(url, params) {
    var request = getRequest('get', url, params);
    request.secondary = true;
    HttpRequestQueueService.push(request);
    executeRequests();
  };

  // DELETE, POST and PUT are methods which utilize
  // the offline request queue

  methods.delete = function(url, params) {
    var request = getRequest('delete', url, params);
    HttpRequestQueueService.push(request);
    executeRequests();
  };

  methods.post = function(url, params, data) {
    var request = getRequest('post', url, params, data);
    var pushed = HttpRequestQueueService.push(request);
    executeRequests();
    return pushed;
  };

  methods.put = function(url, params, data) {
    var request = getRequest('put', url, params, data);
    HttpRequestQueueService.push(request);
    executeRequests();
  };

  // Methods to register callbacks when
  methods.registerCallback = function(type, callback){
    if (type === 'primary'){
      primaryCallback = callback;
    }else if (type === 'secondary'){
      secondaryCallback = callback;
    }else if (type === 'default'){
      defaultCallback = callback;
    }else if (type === 'online'){
      onlineCallback = callback;
    }
  };

  return methods;
}

HttpClientService.$inject = ['$q', '$http', '$rootScope', 'HttpRequestQueueService'];
angular.module('em.services').factory('HttpClientService', HttpClientService);
<commit_msg>Fixed rest of rejections<commit_after>/* global angular */
'use strict';

function HttpClientService($q, $http, $rootScope, HttpRequestQueueService) {

  var methods = {};

  function getRequest(method, url, params, data){
    var request = {
      content: {
        method: method,
        url: url
      },
      params: params
    };
    if (data){
      request.content.data = data;
    }
    return request;
  }

  // Callbacks
  var primaryCallback, secondaryCallback, defaultCallback, onlineCallback;

  // Recursive method to empty the queue
  function executeRequests() {
    var headRequest = HttpRequestQueueService.getHead();
    if (headRequest){
      $http(headRequest.content).
        success(function(data /*, status, headers, config*/) {
          // First, execute callback
          if (headRequest.primary && primaryCallback){
            primaryCallback(headRequest, data);
          }else if (headRequest.secondary && secondaryCallback){
            secondaryCallback(headRequest, data, HttpRequestQueueService.getQueue());
            HttpRequestQueueService.saveQueue();
          }else if (defaultCallback){
            defaultCallback(headRequest, data, HttpRequestQueueService.getQueue());
            HttpRequestQueueService.saveQueue();
          }
          // Then remove the request from queue and release lock
          HttpRequestQueueService.remove(headRequest);

          // Execute online callback
          if (onlineCallback) {
            onlineCallback(true);
          }

          // Try to execute the next request in the queue
          executeRequests();
        }).
        error(function(data, status/*, headers, config*/) {
          if (status === 404 || status === 502){
            // Seems to be offline, stop processing
            HttpRequestQueueService.setOffline(headRequest);
            // Execute callback
            if (onlineCallback) {
              onlineCallback(false);
            }
          }else {
            // Emit unsuspected error to root scope, so that
            // it can be listened on at by the application
            $rootScope.$emit('emException', {type: 'http', status: status, data: data});
          }
        });
    }
  }

  // GET, HEAD and JSONP return chained promises
  angular.forEach(['get', 'head', 'jsonp'], function(name) {
    methods[name] = function(url) {
      return $http({method: name, url: url}).then(function(success) {
        return success;
      });
    };
  });

  // Online alternatives for POST, PUT and DELETE
  methods.postOnline = function(url, data, skipLogStatus) {
    return $http({method: 'post', url: url, data: data}).then(function(success) {
      return success;
    }, function(error) {
      if(!(error.status === skipLogStatus)){
        $rootScope.$emit('emException', {type: 'http', status: error.status, data: error.data});
      }
      return $q.reject(error);
    });
  };

  methods.putOnline = function(url, data) {
    return $http({method: 'put', url: url, data: data}).then(function(success) {
      return success;
    }, function(error) {
      $rootScope.$emit('emException', {type: 'http', status: error.status, data: error.data});
      return $q.reject(error);
    });
  };

  methods.deleteOnline = function(url) {
    return $http({method: 'delete', url: url}).then(function(success) {
      return success;
    }, function(error) {
      $rootScope.$emit('emException', {type: 'http', status: error.status, data: error.data});
      return $q.reject(error);
    });
  };

  // Custom method for a primary POST, i.e. authentication
  methods.postPrimary = function (url, data) {
    var request = getRequest('post', url, undefined, data);
    request.primary = true;
    HttpRequestQueueService.push(request);
    executeRequests();
  };

  // Custom method for secondary GET, i.e. delta getter
  methods.getSecondary = function(url, params) {
    var request = getRequest('get', url, params);
    request.secondary = true;
    HttpRequestQueueService.push(request);
    executeRequests();
  };

  // DELETE, POST and PUT are methods which utilize
  // the offline request queue

  methods.delete = function(url, params) {
    var request = getRequest('delete', url, params);
    HttpRequestQueueService.push(request);
    executeRequests();
  };

  methods.post = function(url, params, data) {
    var request = getRequest('post', url, params, data);
    var pushed = HttpRequestQueueService.push(request);
    executeRequests();
    return pushed;
  };

  methods.put = function(url, params, data) {
    var request = getRequest('put', url, params, data);
    HttpRequestQueueService.push(request);
    executeRequests();
  };

  // Methods to register callbacks when
  methods.registerCallback = function(type, callback){
    if (type === 'primary'){
      primaryCallback = callback;
    }else if (type === 'secondary'){
      secondaryCallback = callback;
    }else if (type === 'default'){
      defaultCallback = callback;
    }else if (type === 'online'){
      onlineCallback = callback;
    }
  };

  return methods;
}

HttpClientService.$inject = ['$q', '$http', '$rootScope', 'HttpRequestQueueService'];
angular.module('em.services').factory('HttpClientService', HttpClientService);
<|endoftext|>


<commit_before>//Reference URL for Chai http://chaijs.com/api/bdd/

const expect = require('chai').expect;
import { Korean } from '../src/korean';

describe('Korean', () => {
  describe('Present Tense', () => {
    it('should conjugate 하다 words correctly', () => {
      const kc = new Korean();
      const presentWord = kc.conjugate('하다', {tense: 'present'});
      expect(presentWord).to.equal('해');
    });
    it('should conjugate 오다 words correctly', () => {
      const kc = new Korean();
      const presentWord = kc.conjugate('오다', {tense: 'present'});
      expect(presentWord).to.equal('와');
    });
  });
});

<commit_msg>add Korean tests<commit_after>//Reference URL for Chai http://chaijs.com/api/bdd/

const expect = require('chai').expect;
import { Korean } from '../src/korean';

describe('Korean', () => {
  describe('Present Tense', () => {
    it('should conjugate 하다 words correctly', () => {
      const kc = new Korean();
      let presentWord = kc.conjugate('하다', {tense: 'present'});
      expect(presentWord).to.equal('해');

      presentWord = kc.conjugate('좋아하다', {tense: 'present'});
      expect(presentWord).to.equal('좋아해');

      presentWord = kc.conjugate('필요하다', {tense: 'present'});
      expect(presentWord).to.equal('필요해');

      presentWord = kc.conjugate('약속하다', {tense: 'present'});
      expect(presentWord).to.equal('약속해');

      presentWord = kc.conjugate('걱정하다', {tense: 'present'});
      expect(presentWord).to.equal('걱정해');

      presentWord = kc.conjugate('그만하다', {tense: 'present'});
      expect(presentWord).to.equal('그만해');
    });
    it('should conjugate 오다 words correctly', () => {
      const kc = new Korean();
      let presentWord = kc.conjugate('오다', {tense: 'present'});
      expect(presentWord).to.equal('와');

      presentWord = kc.conjugate('들어오다', {tense: 'present'});
      expect(presentWord).to.equal('들어와');

      presentWord = kc.conjugate('보다', {tense: 'present'});
      expect(presentWord).to.equal('봐');

      presentWord = kc.conjugate('쏘다', {tense: 'present'});
      expect(presentWord).to.equal('쏴');

      presentWord = kc.conjugate('해보다', {tense: 'present'});
      expect(presentWord).to.equal('해봐');
    });
    it('should conjugate 우다 words correctly', () => {
      const kc = new Korean();
      let presentWord = kc.conjugate('우다', {tense: 'present'});
      expect(presentWord).to.equal('워');

      presentWord = kc.conjugate('춤추다', {tense: 'present'});
      expect(presentWord).to.equal('춤춰');

      presentWord = kc.conjugate('도와주다', {tense: 'present'});
      expect(presentWord).to.equal('도와줘');

      presentWord = kc.conjugate('외우다', {tense: 'present'});
      expect(presentWord).to.equal('외워');

      presentWord = kc.conjugate('싸우다', {tense: 'present'});
      expect(presentWord).to.equal('싸워');

      presentWord = kc.conjugate('바꾸다', {tense: 'present'});
      expect(presentWord).to.equal('바꿔');
    });
    it('should conjugate 으다 words correctly', () => {
      const kc = new Korean();
      let presentWord = kc.conjugate('으다', {tense: 'present'});
      expect(presentWord).to.equal('어');

      presentWord = kc.conjugate('부르다', {tense: 'present'});
      expect(presentWord).to.equal('불러');

      presentWord = kc.conjugate('쓰다', {tense: 'present'});
      expect(presentWord).to.equal('써');
  });
    it('should conjugate 이다 words correctly', () => {
      const kc = new Korean();
      let presentWord = kc.conjugate('이다', {tense: 'present'});
      expect(presentWord).to.equal('여');

      presentWord = kc.conjugate('우기다', {tense: 'present'});
      expect(presentWord).to.equal('우겨');

      presentWord = kc.conjugate('지다', {tense: 'present'});
      expect(presentWord).to.equal('져');
});

<|endoftext|>


<commit_before>/**
 * @ngdoc function
 * @name fstepApp.controller:AdminCtrl
 * @description
 * # AdminCtrl
 * Controller of the admin page
 */
'use strict';
define(['../../fstepmodules'], function (fstepmodules) {

    fstepmodules.controller('AdminCtrl', ['$scope', 'UserService', 'MessageService', 'WalletService', 'QuotaService', 'ReportService', 'TabService', function ($scope, UserService, MessageService, WalletService, QuotaService, ReportService, TabService) {

        /* Sidenav & Bottombar */
        $scope.navInfo = TabService.navInfo.admin;
        $scope.bottombarNavInfo = TabService.navInfo.bottombar;

        /* Active session message count */
        $scope.message = {};
        $scope.message.count = MessageService.countMessages();
        $scope.$on('update.messages', function(event, job) {
            $scope.message.count = MessageService.countMessages();
        });

        $scope.toggleBottomView = function(){
            $scope.bottombarNavInfo.bottomViewVisible = !$scope.bottombarNavInfo.bottomViewVisible;
        };
        /* End Sidenav & Bottombar */

        $scope.userParams = UserService.params.admin;
        $scope.roles = ['USER', 'EXPERT_USER', 'CONTENT_AUTHORITY', 'ADMIN'];

        /* Paging */
        $scope.getPage = function(url){
            UserService.getUsersPage('admin', url);
        };

        UserService.getUsersByFilter('admin');

        QuotaService.getUsageTypes().then(function(types) {
            $scope.quotaUsageTypes = types.map(function(type) {
                return type.name;
            });
        })

        $scope.filter = function(){
            UserService.getUsersByFilter('admin');
        };

        $scope.getUserData = function(){
            if($scope.userParams.selectedUser){
                UserService.getUserByLink($scope.userParams.selectedUser._links.self.href).then(function(data){
                    $scope.userParams.userDetails = data;
                });

                WalletService.getUserWallet($scope.userParams.selectedUser).then(function(wallet){
                   $scope.userParams.wallet = wallet;
                });

                QuotaService.getUserQuotas($scope.userParams.selectedUser._links.self.href).then(function(quotas) {
                    $scope.userParams.quotas = {};
                    quotas.forEach(function(quota) {
                        $scope.userParams.quotas[quota.usageType] = quota;
                    })
                });

            }
        };

        $scope.addCoins = function() {
            WalletService.makeTransaction($scope.userParams.selectedUser, $scope.userParams.wallet, $scope.userParams.coins).then(function(){
                $scope.userParams.coins = 0;
                $scope.getUserData();
            });
        };

        $scope.updateRole = function(newRole) {
            $scope.userParams.userDetails.role = newRole;
            UserService.updateUser($scope.userParams.userDetails).then(function(data){
                $scope.getUserData();
            });
        };

        $scope.updateQuotas = function() {
            for (var usageType in $scope.userParams.quotas) {
                let quota = $scope.userParams.quotas[usageType];
                quota.usageType = usageType;
                QuotaService.setQuotaForUser($scope.userParams.selectedUser._links.self.href, quota).then(function(response) {
                    $scope.userParams.quotas[usageType] = response;
                })
            }
        }

        $scope.exportReport = function($event, resource) {
            ReportService.showExportDialog($event, resource, $scope.userParams.selectedUser.id)
        }

        $scope.hideContent = true;
        var navbar, sidenav, management;
        $scope.finishLoading = function(component) {
            switch(component) {
                case 'navbar':
                    navbar = true;
                    break;
                case 'sidenav':
                    sidenav = true;
                    break;
                case 'management':
                    management = true;
                    break;
            }

            if (navbar && sidenav && management) {
                $scope.hideContent = false;
            }
        };

    }]);
});
<commit_msg>portal: fix user quota retrieval<commit_after>/**
 * @ngdoc function
 * @name fstepApp.controller:AdminCtrl
 * @description
 * # AdminCtrl
 * Controller of the admin page
 */
'use strict';
define(['../../fstepmodules'], function (fstepmodules) {

    fstepmodules.controller('AdminCtrl', ['$scope', 'UserService', 'MessageService', 'WalletService', 'QuotaService', 'ReportService', 'TabService', function ($scope, UserService, MessageService, WalletService, QuotaService, ReportService, TabService) {

        /* Sidenav & Bottombar */
        $scope.navInfo = TabService.navInfo.admin;
        $scope.bottombarNavInfo = TabService.navInfo.bottombar;

        /* Active session message count */
        $scope.message = {};
        $scope.message.count = MessageService.countMessages();
        $scope.$on('update.messages', function(event, job) {
            $scope.message.count = MessageService.countMessages();
        });

        $scope.toggleBottomView = function(){
            $scope.bottombarNavInfo.bottomViewVisible = !$scope.bottombarNavInfo.bottomViewVisible;
        };
        /* End Sidenav & Bottombar */

        $scope.userParams = UserService.params.admin;
        $scope.roles = ['USER', 'EXPERT_USER', 'CONTENT_AUTHORITY', 'ADMIN'];

        /* Paging */
        $scope.getPage = function(url){
            UserService.getUsersPage('admin', url);
        };

        UserService.getUsersByFilter('admin');

        QuotaService.getUsageTypes().then(function(types) {
            $scope.quotaUsageTypes = types.map(function(type) {
                return type.name;
            });
        })

        $scope.filter = function(){
            UserService.getUsersByFilter('admin');
        };

        $scope.getUserData = function(){
            if($scope.userParams.selectedUser){
                UserService.getUserByLink($scope.userParams.selectedUser._links.self.href).then(function(data){
                    $scope.userParams.userDetails = data;
                });

                WalletService.getUserWallet($scope.userParams.selectedUser).then(function(wallet){
                   $scope.userParams.wallet = wallet;
                });

                QuotaService.getUserQuotas($scope.userParams.selectedUser._links.self.href).then(function(quotas) {
                    $scope.userParams.quotas = {};
                    quotas.forEach(function(quota) {
                        $scope.userParams.quotas[quota.usageType.name] = quota;
                    })
                });

            }
        };

        $scope.addCoins = function() {
            WalletService.makeTransaction($scope.userParams.selectedUser, $scope.userParams.wallet, $scope.userParams.coins).then(function(){
                $scope.userParams.coins = 0;
                $scope.getUserData();
            });
        };

        $scope.updateRole = function(newRole) {
            $scope.userParams.userDetails.role = newRole;
            UserService.updateUser($scope.userParams.userDetails).then(function(data){
                $scope.getUserData();
            });
        };

        $scope.updateQuotas = function() {
            for (var usageType in $scope.userParams.quotas) {
                let quota = $scope.userParams.quotas[usageType];
                quota.usageType = usageType;
                QuotaService.setQuotaForUser($scope.userParams.selectedUser._links.self.href, quota).then(function(response) {
                    $scope.userParams.quotas[usageType] = response;
                })
            }
        }

        $scope.exportReport = function($event, resource) {
            ReportService.showExportDialog($event, resource, $scope.userParams.selectedUser.id)
        }

        $scope.hideContent = true;
        var navbar, sidenav, management;
        $scope.finishLoading = function(component) {
            switch(component) {
                case 'navbar':
                    navbar = true;
                    break;
                case 'sidenav':
                    sidenav = true;
                    break;
                case 'management':
                    management = true;
                    break;
            }

            if (navbar && sidenav && management) {
                $scope.hideContent = false;
            }
        };

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


<commit_before>var fs = require('fs'),
    path = require('path'),
    os = require('os'),
    win32 = os.platform() === 'win32',
    argv = nw.App.argv,
    bookmarkletOnly = argv.length > 0 && argv[0] === 'b',
    start_str = 'jni rpc: ',
    pdb_started = false,
    hide_backup = false,
    pdb,
    wnd,
    rpc_host

function println(str) {
    process.stdout.write(str + '\n')
}

function getChildArgs(initial_args, extra_args, child_cwd) {
    return initial_args.concat(extra_args, [
        '-Djava.class.path=' + path.join(child_cwd, 'bookmarks-all/target/bookmarks-all-jarjar.jar'),
        'bookmarks.all.Main',
        child_cwd
    ])
}

function isDir(dir) {
    return fs.statSync(dir).isDirectory()
}

function findSubDir(baseDir, subDirPrefix) {
    var dirs = fs.readdirSync(baseDir), dir, f
    for (var i = 0, len = dirs.length; i < len; i++) {
        if (isDir(dir=path.join(baseDir, f=dirs[i])) && 0 === f.indexOf(subDirPrefix))
            return dir
    }
    return null
}

function startProtostuffdb() {
    var spawn = require('child_process').spawn,
        child_cwd = path.join(__dirname, '..'),
        bin = path.join(child_cwd, 'target/protostuffdb'),
        port = fs.readFileSync(path.join(child_cwd, 'PORT.txt'), 'utf8').trim(),
        raw_args = fs.readFileSync(path.join(child_cwd, 'ARGS.txt'), 'utf8').trim(),
        extra_args = raw_args.split(' '),
        child_args = getChildArgs(['127.0.0.1:' + port, path.join(__dirname, 'g/user/UserServices.json')], extra_args, child_cwd),
        target_cwd,
        p

    hide_backup = raw_args.indexOf('-Dprotostuffdb.with_backup=true') === -1
    if (!win32) {
        target_cwd = child_cwd
    } else if (isDir(target_cwd = 'C:/Program Files/Java/jdk1.7.0_79/jre/bin/server')) {
        bin += '.exe'
    } else if (fs.existsSync(p = path.join(child_cwd, 'JDK_DIR.txt'))) {
        if (!isDir(target_cwd = path.join(fs.readFileSync(p, 'utf8').trim(), 'jre/bin/server'))) {
            println('JDK_DIR.txt did not contain a valid jdk path.')
            process.exit(1)
            return
        }
        bin += '.exe'
    } else if (!isDir('C:/Program Files/Java') ||
            !(p = findSubDir('C:/Program Files/Java', 'jdk1.7')) ||
            !isDir(target_cwd = path.join(p, 'jre/bin/server'))) {
        println('Please create JDK_DIR.txt in the root dir of the project that contains the full path to your jdk.')
        println('E.g.\nC:/path/to/jdk1.7.0_79')
        process.exit(1)
        return
    } else {
        bin += '.exe'
    }

    pdb = spawn(bin, child_args, { cwd: target_cwd })
    pdb.stdout.on('data', onChildOut)
    pdb.on('close', onChildClose)
    rpc_host = 'http://127.0.0.1:' + port
}

function isStart(data) {
    return data.length > start_str.length && start_str === String(data.slice(0, start_str.length))
}

function onChildOut(data) {
    if (pdb_started || !isStart(data))
        return
    pdb_started = true
    openWindow()
}

function onChildClose(code) {
    if (code)
        println('child process exited with code ' + code)
    if (wnd)
        wnd.close(true)
    pdb = undefined
}

function onClose() {
    this.hide()
    wnd = undefined
    if (pdb)
        pdb.kill()
    this.close(true)
}

function onOpen(w) {
    wnd = w
    if (bookmarkletOnly) {
        w.resizeTo(340, 100)
    } else {
        w.resizeTo(1005, 710)
    }

    w.moveTo(273, 0)
    if (pdb)
        w.on('close', onClose)
    w.show()
}

function openWindow() {
    global.rpc_host = rpc_host
    global.hide_backup = hide_backup
    nw.Window.open(bookmarkletOnly ? 'nw/bookmarklet.html' : 'index.html', { show: false, show_in_taskbar: !win32 }, onOpen)

    require('./dist/bookmarklet-nw')
}

startProtostuffdb()

<commit_msg>resolveBin<commit_after>var fs = require('fs'),
    path = require('path'),
    os = require('os'),
    win32 = os.platform() === 'win32',
    argv = nw.App.argv,
    bookmarkletOnly = argv.length > 0 && argv[0] === 'b',
    start_str = 'jni rpc: ',
    pdb_started = false,
    hide_backup = false,
    pdb,
    wnd,
    rpc_host

function println(str) {
    process.stdout.write(str + '\n')
}

function getChildArgs(initial_args, extra_args, child_cwd) {
    return initial_args.concat(extra_args, [
        '-Djava.class.path=' + path.join(child_cwd, 'bookmarks-all/target/bookmarks-all-jarjar.jar'),
        'bookmarks.all.Main',
        child_cwd
    ])
}

function isDir(dir) {
    return fs.statSync(dir).isDirectory()
}

function findSubDir(baseDir, subDirPrefix) {
    var dirs = fs.readdirSync(baseDir), dir, f
    for (var i = 0, len = dirs.length; i < len; i++) {
        if (isDir(dir=path.join(baseDir, f=dirs[i])) && 0 === f.indexOf(subDirPrefix))
            return dir
    }
    return null
}

function resolveBin(child_cwd) {
    if (win32) return path.join(child_cwd, 'target/protostuffdb')

    var bin = path.join(child_cwd, 'target/hprotostuffdb-rjre')
    if (fs.existsSync(bin) || fs.existsSync(bin = path.join(child_cwd, 'target/hprotostuffdb')))
        return bin

    return path.join(child_cwd, 'target/protostuffdb')
}

function startProtostuffdb() {
    var spawn = require('child_process').spawn,
        child_cwd = path.join(__dirname, '..'),
        bin = resolveBin(child_cwd),
        port = fs.readFileSync(path.join(child_cwd, 'PORT.txt'), 'utf8').trim(),
        raw_args = fs.readFileSync(path.join(child_cwd, 'ARGS.txt'), 'utf8').trim(),
        extra_args = raw_args.split(' '),
        child_args = getChildArgs(['127.0.0.1:' + port, path.join(__dirname, 'g/user/UserServices.json')], extra_args, child_cwd),
        target_cwd,
        p

    hide_backup = raw_args.indexOf('-Dprotostuffdb.with_backup=true') === -1
    if (!win32) {
        target_cwd = child_cwd
    } else if (isDir(target_cwd = 'C:/opt/jre/bin/server') ||
            isDir(target_cwd = 'C:/Program Files/Java/jdk1.7.0_79/jre/bin/server')) {
        bin += '.exe'
    } else if (fs.existsSync(p = path.join(child_cwd, 'JDK_DIR.txt'))) {
        if (!isDir(target_cwd = path.join(fs.readFileSync(p, 'utf8').trim(), 'jre/bin/server'))) {
            println('JDK_DIR.txt did not contain a valid jdk path.')
            process.exit(1)
            return
        }
        bin += '.exe'
    } else if (!isDir('C:/Program Files/Java') ||
            !(p = findSubDir('C:/Program Files/Java', 'jdk1.7')) ||
            !isDir(target_cwd = path.join(p, 'jre/bin/server'))) {
        println('Please create JDK_DIR.txt in the root dir of the project that contains the full path to your jdk.')
        println('E.g.\nC:/path/to/jdk1.7.0_79')
        process.exit(1)
        return
    } else {
        bin += '.exe'
    }

    pdb = spawn(bin, child_args, { cwd: target_cwd })
    pdb.on('error', onChildError)
    pdb.stdout.on('data', onChildOut)
    pdb.on('close', onChildClose)
    rpc_host = 'http://127.0.0.1:' + port
}

function onChildError(err) {
    println('protostuffdb spawn failed')
    process.exit(1)
}

function isStart(data) {
    return data.length > start_str.length && start_str === String(data.slice(0, start_str.length))
}

function onChildOut(data) {
    if (pdb_started || !isStart(data))
        return
    pdb_started = true
    openWindow()
}

function onChildClose(code) {
    if (code)
        println('child process exited with code ' + code)
    if (wnd)
        wnd.close(true)
    pdb = undefined
}

function onClose() {
    this.hide()
    wnd = undefined
    if (pdb)
        pdb.kill()
    this.close(true)
}

function onOpen(w) {
    wnd = w
    if (bookmarkletOnly) {
        w.resizeTo(340, 100)
    } else {
        w.resizeTo(1005, 710)
    }

    w.moveTo(273, 0)
    if (pdb)
        w.on('close', onClose)
    w.show()
}

function openWindow() {
    global.rpc_host = rpc_host
    global.hide_backup = hide_backup
    nw.Window.open(bookmarkletOnly ? 'nw/bookmarklet.html' : 'index.html', { show: false, show_in_taskbar: !win32 }, onOpen)

    require('./dist/bookmarklet-nw')
}

startProtostuffdb()

<|endoftext|>


<commit_before>var App = angular.module('App', ['ngResource']);

App.config(['$interpolateProvider', '$httpProvider', function($interpolateProvider, $httpProvider) {
  // New symbols to prevent interference with Django templating language
  $interpolateProvider.startSymbol('{$');
  $interpolateProvider.endSymbol('$}');

  $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
  $httpProvider.defaults.xsrfCookieName = 'csrftoken';
}]);

// Browser detection messaging
App.directive('isFirefox', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var KEY = 'texaschoropleth:firefox-message-dismiss';
      if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
        if (localStorage.getItem(KEY)) {
          return;
        }

        var message = $(element);

        message.removeClass('hidden');
        message.on('close.bs.alert', function() {
          localStorage.setItem(KEY, 1);
        });
      }
    }
  };
});

// Bootstrap active tab helper
App.directive('isActive', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      if (attrs.href === window.location.pathname) {
        element.parent().addClass('active');
      }
    }
  };
});

// Choropleth Tour
App.directive('choroplethTour', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
        steps = [
          {
            element: '#map-controls',
            title: 'Map Controls',
            content: 'Use these controls to configure the look and feel. Click the  icon for more information on each form field.',
            placement: 'left'
          },
          {
            element: '#description-tab',
            title: 'Description',
            content: 'Use the text box below to add description of your choropleth and/or dataset. The description is Markdown-enabled. There is a link to the Markdown website and specification on the \'Cheatsheet\' tab. If you prefer not to use Markdown, then simply enter the plain text into the description box.',
            placement: 'top'
          },
          {
            element: '#save',
            title: 'Save',
            content: 'Once you are satisfied with your configuration,  click this \'Save\' to finish.',
            placement: 'left'
          }
        ];
      var tour = new Tour({name:'choropleth', steps:steps});
      tour.init();

      $(element).click(function() {
        localStorage.removeItem('choropleth_end');
        localStorage.removeItem('choropleth_current_step');
        tour.start();
      });
    }
  };
});

// Dataset Tour
App.directive('datasetTour', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
        steps = [
          {
            element: '#csv-template',
            title: 'Dataset Template',
            content: 'In order to upload your dataset, the dataset document must pass strict validation. We created this template so that all you have to do is put in the data for the corresponding county.',
            placement: 'right'
          },
          {
            element: '#datafile-form',
            title: 'Upload',
            content: 'Once your data has been added to the template, add your file to the form and click \'Upload\'.',
            placement: 'left'
          },
        ];
      var tour = new Tour({name: 'dataset', steps:steps});
      tour.init();

      $(element).click(function() {
        localStorage.removeItem('dataset_end');
        localStorage.removeItem('dataset_current_step');
        tour.start();
      });
    }
  };
});

// Still uses the data-content attr for content
App.directive('popover', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      options = {
        trigger: 'hover'
      };
      $(element).popover(options);
    }
  };
});

// Form Validation helper
App.directive('hasError', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var formGroup = $(element);
      var error = formGroup.find('.label.label-danger');
      if (error.length > 0 && !error.is(':empty')) {
        formGroup.addClass('has-error');
      }
    }
  };
});

// Add a Bootstrap tooltip
App.directive('tooltip', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      $(element).tooltip();
    }
  };
});

// Helper for notifications and list-item-icons
App.directive('hasRecords', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      // Compare it with the python bool
      if (attrs.records == 'False') {
        element.addClass('no-records');
      }
    }
  };
});

// Ajax loader
App.directive('loading', function($http) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var loader = $('#loader').clone();

      if (attrs.loading === 'http') {
          element.append(loader.show());
        return;
      }

      scope.isLoading = function() {
        return $http.pendingRequests.length > 0;
      };

      scope.$watch(scope.isLoading, function(v) {
        if (v) {
          element.append(loader.show());
        } else {
          $(element).find('#loader').remove();
        }
      });
    }
  };
});

App.factory('Choropleth', ['$resource', function($resource) {
  return $resource('/api/choropleths/:id/ ', { id: '@id' }, {
    update: {
      method: 'PUT'
    }
  });
}]);

// Resource for Dataset
App.factory('Dataset', ['$resource', function($resource) {
  return $resource('/api/datasets/:id/ ');
}]);

// Resource for Palettes
App.factory('Palettes', ['$resource', function($resource) {
  return $resource('/api/choropleths/palettes/:id/ ');
}]);

App.controller('DatasetTableController', ['$scope', '$http', 'Dataset', function($scope, $http, Dataset) {

  $scope.init = function(id) {
    Dataset.get({id: id}, function(data) {
      $scope.dataset = data;
    });

    $scope.reverse = false;
    $scope.sortOrder='name';
  };

}]);

App.directive('progressBar', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      $(element).width(attrs.progressBar);
    }
  };
});

<commit_msg>Removed the namespace from the localStorage key.<commit_after>var App = angular.module('App', ['ngResource']);

App.config(['$interpolateProvider', '$httpProvider', function($interpolateProvider, $httpProvider) {
  // New symbols to prevent interference with Django templating language
  $interpolateProvider.startSymbol('{$');
  $interpolateProvider.endSymbol('$}');

  $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
  $httpProvider.defaults.xsrfCookieName = 'csrftoken';
}]);

// Browser detection messaging
App.directive('isFirefox', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var KEY = 'firefox-message-dismiss';
      if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
        if (localStorage.getItem(KEY)) {
          return;
        }

        var message = $(element);

        message.removeClass('hidden');
        message.on('close.bs.alert', function() {
          localStorage.setItem(KEY, 1);
        });
      }
    }
  };
});

// Bootstrap active tab helper
App.directive('isActive', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      if (attrs.href === window.location.pathname) {
        element.parent().addClass('active');
      }
    }
  };
});

// Choropleth Tour
App.directive('choroplethTour', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
        steps = [
          {
            element: '#map-controls',
            title: 'Map Controls',
            content: 'Use these controls to configure the look and feel. Click the  icon for more information on each form field.',
            placement: 'left'
          },
          {
            element: '#description-tab',
            title: 'Description',
            content: 'Use the text box below to add description of your choropleth and/or dataset. The description is Markdown-enabled. There is a link to the Markdown website and specification on the \'Cheatsheet\' tab. If you prefer not to use Markdown, then simply enter the plain text into the description box.',
            placement: 'top'
          },
          {
            element: '#save',
            title: 'Save',
            content: 'Once you are satisfied with your configuration,  click this \'Save\' to finish.',
            placement: 'left'
          }
        ];
      var tour = new Tour({name:'choropleth', steps:steps});
      tour.init();

      $(element).click(function() {
        localStorage.removeItem('choropleth_end');
        localStorage.removeItem('choropleth_current_step');
        tour.start();
      });
    }
  };
});

// Dataset Tour
App.directive('datasetTour', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
        steps = [
          {
            element: '#csv-template',
            title: 'Dataset Template',
            content: 'In order to upload your dataset, the dataset document must pass strict validation. We created this template so that all you have to do is put in the data for the corresponding county.',
            placement: 'right'
          },
          {
            element: '#datafile-form',
            title: 'Upload',
            content: 'Once your data has been added to the template, add your file to the form and click \'Upload\'.',
            placement: 'left'
          },
        ];
      var tour = new Tour({name: 'dataset', steps:steps});
      tour.init();

      $(element).click(function() {
        localStorage.removeItem('dataset_end');
        localStorage.removeItem('dataset_current_step');
        tour.start();
      });
    }
  };
});

// Still uses the data-content attr for content
App.directive('popover', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      options = {
        trigger: 'hover'
      };
      $(element).popover(options);
    }
  };
});

// Form Validation helper
App.directive('hasError', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var formGroup = $(element);
      var error = formGroup.find('.label.label-danger');
      if (error.length > 0 && !error.is(':empty')) {
        formGroup.addClass('has-error');
      }
    }
  };
});

// Add a Bootstrap tooltip
App.directive('tooltip', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      $(element).tooltip();
    }
  };
});

// Helper for notifications and list-item-icons
App.directive('hasRecords', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      // Compare it with the python bool
      if (attrs.records == 'False') {
        element.addClass('no-records');
      }
    }
  };
});

// Ajax loader
App.directive('loading', function($http) {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      var loader = $('#loader').clone();

      if (attrs.loading === 'http') {
          element.append(loader.show());
        return;
      }

      scope.isLoading = function() {
        return $http.pendingRequests.length > 0;
      };

      scope.$watch(scope.isLoading, function(v) {
        if (v) {
          element.append(loader.show());
        } else {
          $(element).find('#loader').remove();
        }
      });
    }
  };
});

App.factory('Choropleth', ['$resource', function($resource) {
  return $resource('/api/choropleths/:id/ ', { id: '@id' }, {
    update: {
      method: 'PUT'
    }
  });
}]);

// Resource for Dataset
App.factory('Dataset', ['$resource', function($resource) {
  return $resource('/api/datasets/:id/ ');
}]);

// Resource for Palettes
App.factory('Palettes', ['$resource', function($resource) {
  return $resource('/api/choropleths/palettes/:id/ ');
}]);

App.controller('DatasetTableController', ['$scope', '$http', 'Dataset', function($scope, $http, Dataset) {

  $scope.init = function(id) {
    Dataset.get({id: id}, function(data) {
      $scope.dataset = data;
    });

    $scope.reverse = false;
    $scope.sortOrder='name';
  };

}]);

App.directive('progressBar', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attrs) {
      $(element).width(attrs.progressBar);
    }
  };
});

<|endoftext|>


<commit_before>var Code = require('code');

var Lab = require('lab');
var lab = exports.lab = Lab.script();

var expect = Code.expect;
var describe = lab.experiment;
var it = lab.test;
var beforeEach = lab.beforeEach;

var rewire = require('rewire');
var argv = rewire('../../../lib/cli/argv');

describe('argv', function () {
	describe('help', function () {
		var checkForUsage = function(txt) {
			expect(txt.indexOf('Usage:')).to.equal(0);
		};

		it('should print help message with --help', function (done) {
			argv({_: [], help: true}, checkForUsage);
			done();
		});

		it('should print help message with -h', function (done) {
			argv({_: [], h: true}, checkForUsage);
			done();
		});

		it('should print a usage message with lines of 80 chars or less', function (done) {
			argv({_: [], h: true}, function (txt) {
				var lines = txt.split('\n');
				lines.forEach(function (value) {
					expect(value.length).to.be.below(81);
				});
				done();
			});
		});
	});

	describe('version', function () {
		var checkForVersion = function (txt) {
			expect(txt.search(/^\d+\.\d+\.\d+/)).to.equal(0);
		};

		it('should print the version with --version', function (done) {
			argv({_: [], version: true}, checkForVersion);
			done();
		});

		it('should print the version with -v', function (done) {
			argv({_: [], v: true}, checkForVersion);
			done();
		});
	});

	describe('stackwatch', function () {
		var noop = function () {};
		var reset;

		beforeEach(function (done) {
			if (reset) {
				reset();
				reset = null;
			}
			done();
		});

		var checkForError = function (txt) {
			expect(txt).to.equal('Error: data is not valid');
		};

		it('should call check()', function (done) {
			var checkCalled = false;

			var stackwatchTestDouble = {
				check: function () {
					checkCalled = true;
					done();
				}
			};
			expect(checkCalled).to.be.false();
			argv({_: []}, noop, stackwatchTestDouble);
			expect(checkCalled).to.be.true();
		});

		it('should print error message if error is received by callback from check()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(new Error('This is a sample error message.'));
				}
			};

			argv(
				{_: []},
				function (txt) { expect(txt).to.equal('Error: This is a sample error message.'); },
				stackwatchTestDouble
			);
			done();
		});

		it('should print error message if error is received by callback from start()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items:[{question_id: 'fhqwhgads', link: 'http://example.com/'}]});
				},
				start: function (options, callback) {
					return callback(new Error('This is a sample error message.'));
				},
				stop: clearInterval
			};

			var stdoutTestDouble = function (txt) {
				if (txt !== 'stackwatch is running...') {
					expect(txt).to.equal('Error: This is a sample error message.');
					done();
				}
			};

			argv({_: [], wait: '0'}, stdoutTestDouble, stackwatchTestDouble);
		});

		it('should print error message if error_message included in otherwise invalid data object from check()', function (done) {
			var stackwatchTestDouble = {
				check: function(options, callback) {
					return callback(null, {error_message: 'This is another sample error message.'});
				}
			};

			argv(
				{_: []},
				function (txt) { expect(txt).to.equal('Error: This is another sample error message.'); },
				stackwatchTestDouble
			);
			done();
		});

		it('should print error message if error_message included in otherwise invalid data object from start()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items:[{question_id: 'fhqwhgads', link: 'http://example.com/'}]});
				},
				start: function (options, callback) {
					return callback(null, {error_message: 'This is yet another sample error message.'});
				},
				stop: clearInterval
			};

			var stdoutTestDouble = function (txt) {
				if (txt !== 'stackwatch is running...') {
					expect(txt).to.equal('Error: This is yet another sample error message.');
					done();
				}
			};

			argv({_: [], wait: '0'}, stdoutTestDouble, stackwatchTestDouble);
		});

		it('should print error message if no data from check()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback();
				}
			};
			argv({_: []}, checkForError, stackwatchTestDouble);
			done();
		});

		it('should print error message if no data from start()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items:[{question_id: 'fhqwhgads', link: 'http://example.com/'}]});
				},
				start: function (options, callback) {
					return callback();
				},
				stop: clearInterval
			};

			var stdoutTestDouble = function (txt) {
				if (txt !== 'stackwatch is running...') {
					checkForError(txt);
					done();
				}
			};

			argv({_: [], wait: '0'}, stdoutTestDouble, stackwatchTestDouble);
		});

		it('should print error message if empty data object from check()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {});
				}
			};
			argv({_: []}, checkForError, stackwatchTestDouble);
			done();
		});

		it('should print error message if empty data object from start()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items:[{question_id: 'fhqwhgads', link: 'http://example.com/'}]});
				},
				start: function (options, callback) {
					return callback(null, {});
				},
				stop: clearInterval
			};

			var stdoutTestDouble = function (txt) {
				if (txt !== 'stackwatch is running...') {
					checkForError(txt);
					done();
				}
			};

			argv({_: [], wait: '0'}, stdoutTestDouble, stackwatchTestDouble);
		});

		it('should print error message if data.items is empty', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items: []});
				}
			};
			argv({_: []}, checkForError, stackwatchTestDouble);
			done();
		});

		it('should print error message if no question_id', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items: [{link: 'https://example.com/'}]});
				}
			};
			argv({_: []}, checkForError, stackwatchTestDouble);
			done();
		});

		it('should print error message if no link', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items: [{question_id: 'fhqwhgads'}]});
				}
			};
			argv({_: []}, checkForError, stackwatchTestDouble);
			done();
		});

		it('should call start() right after check() if wait is 0', function (done) {
			var checkCalled = false;
			var startCalled = false;
			var stackwatchTestDouble = {
				check: function (options, callback) {
					checkCalled = true;
					return callback(null, {items: [{question_id: 'fhqwhgads', link: 'https://www.example.com/'}]});
				},
				start: function() {
					startCalled = true;
				}
			};

			argv({_: [], wait: '0'}, noop, stackwatchTestDouble);
			setTimeout(function () {
				expect(checkCalled).to.be.true();
				expect(startCalled).to.be.true();
				done();
			}, 0);
		});

		it('should call opn() if new question_id is present', function (done) {
			reset = argv.__set__('opn', function (url) {
				expect(url).to.equal('https://www.example.com/grumbles');
				done();
			});

			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items: [{question_id: 'fhqwhgads', link: 'https://www.example.com/fhqwhgads'}]});
				},
				start: function(options, callback) {
					return callback(null, {items: [
						{question_id: 'grumbles', link: 'https://www.example.com/grumbles'},
						{question_id: 'fhqwhgads', link: 'https://www.example.com/fhqwhgads'}
					]});
				}
			};
			argv({_: [], wait: '0'}, noop, stackwatchTestDouble);
		});
	});
});
<commit_msg>test coverage to 100%<commit_after>var Code = require('code');

var Lab = require('lab');
var lab = exports.lab = Lab.script();

var expect = Code.expect;
var describe = lab.experiment;
var it = lab.test;
var beforeEach = lab.beforeEach;

var rewire = require('rewire');
var argv = rewire('../../../lib/cli/argv');

describe('argv', function () {
	describe('help', function () {
		var checkForUsage = function(txt) {
			expect(txt.indexOf('Usage:')).to.equal(0);
		};

		it('should print help message with --help', function (done) {
			argv({_: [], help: true}, checkForUsage);
			done();
		});

		it('should print help message with -h', function (done) {
			argv({_: [], h: true}, checkForUsage);
			done();
		});

		it('should print a usage message with lines of 80 chars or less', function (done) {
			argv({_: [], h: true}, function (txt) {
				var lines = txt.split('\n');
				lines.forEach(function (value) {
					expect(value.length).to.be.below(81);
				});
				done();
			});
		});
	});

	describe('version', function () {
		var checkForVersion = function (txt) {
			expect(txt.search(/^\d+\.\d+\.\d+/)).to.equal(0);
		};

		it('should print the version with --version', function (done) {
			argv({_: [], version: true}, checkForVersion);
			done();
		});

		it('should print the version with -v', function (done) {
			argv({_: [], v: true}, checkForVersion);
			done();
		});
	});

	describe('stackwatch', function () {
		var noop = function () {};
		var reset;

		beforeEach(function (done) {
			if (reset) {
				reset();
				reset = null;
			}
			done();
		});

		var checkForError = function (txt) {
			expect(txt).to.equal('Error: data is not valid');
		};

		it('should call check()', function (done) {
			var checkCalled = false;

			var stackwatchTestDouble = {
				check: function () {
					checkCalled = true;
					done();
				}
			};
			expect(checkCalled).to.be.false();
			argv({_: []}, noop, stackwatchTestDouble);
			expect(checkCalled).to.be.true();
		});

		it('should print error message if error is received by callback from check()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(new Error('This is a sample error message.'));
				}
			};

			argv(
				{_: []},
				function (txt) { expect(txt).to.equal('Error: This is a sample error message.'); },
				stackwatchTestDouble
			);
			done();
		});

		it('should print error message if error is received by callback from start()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items:[{question_id: 'fhqwhgads', link: 'http://example.com/'}]});
				},
				start: function (options, callback) {
					return callback(new Error('This is a sample error message.'));
				},
				stop: clearInterval
			};

			var stdoutTestDouble = function (txt) {
				if (txt !== 'stackwatch is running...') {
					expect(txt).to.equal('Error: This is a sample error message.');
					done();
				}
			};

			argv({_: [], wait: '0'}, stdoutTestDouble, stackwatchTestDouble);
		});

		it('should print error message if error_message included in otherwise invalid data object from check()', function (done) {
			var stackwatchTestDouble = {
				check: function(options, callback) {
					return callback(null, {error_message: 'This is another sample error message.'});
				}
			};

			argv(
				{_: []},
				function (txt) { expect(txt).to.equal('Error: This is another sample error message.'); },
				stackwatchTestDouble
			);
			done();
		});

		it('should print error message if error_message included in otherwise invalid data object from start()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items:[{question_id: 'fhqwhgads', link: 'http://example.com/'}]});
				},
				start: function (options, callback) {
					return callback(null, {error_message: 'This is yet another sample error message.'});
				},
				stop: clearInterval
			};

			var stdoutTestDouble = function (txt) {
				if (txt !== 'stackwatch is running...') {
					expect(txt).to.equal('Error: This is yet another sample error message.');
					done();
				}
			};

			argv({_: [], wait: '0'}, stdoutTestDouble, stackwatchTestDouble);
		});

		it('should print error message if no data from check()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback();
				}
			};
			argv({_: []}, checkForError, stackwatchTestDouble);
			done();
		});

		it('should print error message if no data from start()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items:[{question_id: 'fhqwhgads', link: 'http://example.com/'}]});
				},
				start: function (options, callback) {
					return callback();
				},
				stop: clearInterval
			};

			var stdoutTestDouble = function (txt) {
				if (txt !== 'stackwatch is running...') {
					checkForError(txt);
					done();
				}
			};

			argv({_: [], wait: '0'}, stdoutTestDouble, stackwatchTestDouble);
		});

		it('should print error message if empty data object from check()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {});
				}
			};
			argv({_: []}, checkForError, stackwatchTestDouble);
			done();
		});

		it('should print error message if empty data object from start()', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items:[{question_id: 'fhqwhgads', link: 'http://example.com/'}]});
				},
				start: function (options, callback) {
					return callback(null, {});
				},
				stop: clearInterval
			};

			var stdoutTestDouble = function (txt) {
				if (txt !== 'stackwatch is running...') {
					checkForError(txt);
					done();
				}
			};

			argv({_: [], wait: '0'}, stdoutTestDouble, stackwatchTestDouble);
		});

		it('should print error message if data.items is empty', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items: []});
				}
			};
			argv({_: []}, checkForError, stackwatchTestDouble);
			done();
		});

		it('should print error message if no question_id', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items: [{link: 'https://example.com/'}]});
				}
			};
			argv({_: []}, checkForError, stackwatchTestDouble);
			done();
		});

		it('should print error message if no link', function (done) {
			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items: [{question_id: 'fhqwhgads'}]});
				}
			};
			argv({_: []}, checkForError, stackwatchTestDouble);
			done();
		});

		it('should call start() right after check() if wait is 0', function (done) {
			var checkCalled = false;
			var startCalled = false;
			var stackwatchTestDouble = {
				check: function (options, callback) {
					checkCalled = true;
					return callback(null, {items: [{question_id: 'fhqwhgads', link: 'https://www.example.com/'}]});
				},
				start: function() {
					startCalled = true;
				}
			};

			argv({_: [], wait: '0'}, noop, stackwatchTestDouble);
			setTimeout(function () {
				expect(checkCalled).to.be.true();
				expect(startCalled).to.be.true();
				done();
			}, 0);
		});

		it('should call opn() if new question_id is present', function (done) {
			reset = argv.__set__('opn', function (url) {
				expect(url).to.equal('https://www.example.com/grumbles');
				done();
			});

			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items: [{question_id: 'fhqwhgads', link: 'https://www.example.com/fhqwhgads'}]});
				},
				start: function(options, callback) {
					return callback(null, {items: [
						{question_id: 'grumbles', link: 'https://www.example.com/grumbles'},
						{question_id: 'fhqwhgads', link: 'https://www.example.com/fhqwhgads'}
					]});
				}
			};
			argv({_: [], wait: '0'}, noop, stackwatchTestDouble);
		});

		it('should call opn() on new question_id even if previous question_id is no longer present', function (done) {
			reset = argv.__set__('opn', function (url) {
				expect(url).to.equal('https://www.example.com/grumbles');
				done();
			});

			var stackwatchTestDouble = {
				check: function (options, callback) {
					return callback(null, {items: [{question_id: 'fhqwhgads', link: 'https://www.example.com/fhqwhgads'}]});
				},
				start: function(options, callback) {
					return callback(null, {items: [
						{question_id: 'grumbles', link: 'https://www.example.com/grumbles'}
					]});
				}
			};
			argv({_: [], wait: '0'}, noop, stackwatchTestDouble);
		});
	});
});
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import Highlight from 'react-syntax-highlight'
import Note from '../Note'

import mapScreenshot from './map-screenshot.png'
import mockMarkers from './mock-markers.png'
import listScreen from './list-screen.png'

const imports = `import mockdata from '../mock/mockdata.json'`

const componentMockData = `  componentWillMount() {
    this.setState({ markers: mockdata.results })
  }`

const baseComponent = `import React, { Component } from 'react'
import { View, StyleSheet, Text } from 'react-native'
import MapView, { Marker } from 'react-native-maps'
import mockdata from '../mock/mockdata.json'

export default class Map extends Component {
  constructor (props) {
    super(props)
    this.state = {
      markers: []
    }
  }

  componentWillMount () {
    this.setState({ markers: mockdata.results })
  }

  render () {
    return (
      <View style={styles.container}>
        <MapView
          style={styles.container}
          initialRegion={{
            latitude: 45.5209087,
            longitude: -122.6705107,
            latitudeDelta: 0.0922,
            longitudeDelta: 0.0421
          }}
        >
          {this.state && this.state.markers.length > 0 ? this.state.markers.map((marker, i) => {
            return (
              <Marker {...marker} key={i} onPress={() => this.setSelectedVenue(venue)>
                <View style={styles.marker} />
              </Marker>
            )
          }) : null }
        </MapView>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  marker: {
    backgroundColor: '#550bbc',
    padding: 5,
    borderRadius: 5
  },
  text: {
    color: '#FFF',
    fontWeight: 'bold'
  }
})
`

const mapViewMockData = `<MapView
          style={styles.container}
          initialRegion={{
            latitude: mockdata.origin.latitude,
            longitude: mockdata.origin.longitude,
            latitudeDelta: mockdata.origin.latitudeDelta,
            longitudeDelta: mockdata.origin.longitudeDelta
          }}
        >
          { /*
            Our mock data is already being passed to each Marker
            component with {...marker}, which will provide our
            markers with the geolocation data they need to
            render correctly on our map
          */}
          {this.state && this.state.markers.length > 0 ? this.state.markers.map((marker, i) => {
            return (
              <Marker {...marker} key={i}>
                <View style={styles.marker} />
              </Marker>
            )
          }) : null }
      </MapView>`

const mapMarkerMockData = `{this.state && this.state.markers.length > 0 ? this.state.markers.map((marker, i) => {
            return (
              <Marker {...marker} key={i}>
                <View style={styles.marker}>
                  <Text style={styles.text}>{marker.label}</Text>
                </View>
              </Marker>
            )
          }) : null }`

const markersFinished = `export default class Map extends Component {
  constructor (props) {
    super(props)
    this.state = {
      markers: []
    }
  }

  componentWillMount() {
    this.setState({ markers: mockdata.results })
  }

  render () {
    return (
      <View style={styles.container}>
        <MapView
          style={styles.container}
          initialRegion={{
            latitude: mockdata.origin.latitude,
            longitude: mockdata.origin.longitude,
            latitudeDelta: mockdata.origin.latitudeDelta,
            longitudeDelta: mockdata.origin.longitudeDelta
          }}
        >
          {this.state && this.state.markers.length > 0 ? this.state.markers.map((marker, i) => {
            return (
              <Marker {...marker} key={i}>
                <View style={styles.marker} />
              </Marker>
            )
          }) : null }
        </MapView>
      </View>
    )
  }
}`

const resultsList = `import React, { Component } from 'react';
import { ListView } from 'react-native';
// The ListItem component doesn't exist yet, we'll make that next
import ListItem from './ListItem';

class ResultsList extends Component {
  constructor (props) {
    super(props);

    // We pass our mock data to this component from the Map component
    // we then pass this.props.results to the object created from ListView.DataSource
    // to determine if a row should be rendered or not
    const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
    this.state = {
      dataSource: ds.cloneWithRows(this.props.results)
    };
  }

  renderRow (rowData) {
    return (
      <ListItem
        setSelectedVenue={this.props.setSelectedVenue}
        rowData={rowData} />
    );
  }

  // ListView takes a renderRow prop, this takes a function to render an individual row
  render () {
    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={rowData => this.renderRow(rowData)} />
    );
  }
}

export default ResultsList;`

const listItemComponent = `import React from 'react';
import { TouchableOpacity, View, Text, StyleSheet, Image } from 'react-native';

import * as colors from '../styles/colors';

export default ({ rowData, setSelectedVenue }) => {
  // get the properties we want from our mock data
  const { name, address, trendingNumber, travelTime } = rowData;
  return (
    <TouchableOpacity onPress={() => setSelectedVenue(rowData)}>
      <View style={styles.listItemContainer}>
        { /* for now we'll just use a static image, we can make this dynamic when we hook up the app to an API in the next workshop */ }
        <Image source={require('./../assets/images/Beer.png')} style={styles.iconImage} />
        { /* below we're just rendering the mock data */ }
        <View style={styles.mainContent}>
          <Text>{name}</Text>
          <Text style={styles.address}>{address}</Text>
          <Text style={styles.trending}>+{trendingNumber} others trending now</Text>
        </View>
        <View style={styles.travel}>
          <Text style={styles.travelText}>{travelTime}</Text>
          <Image source={require('./../assets/images/glass-outline.png')} style={styles.smallIcon} />
        </View>
      </View>
    </TouchableOpacity>
  );
};`

const listItemStyles = `const styles = StyleSheet.create({
  listItemContainer: {
    padding: 25,
    borderColor: '#777',
    borderBottomWidth: 0.5,
    flexDirection: 'row',
    backgroundColor: colors.WHITE
  },
  iconImage: {
    height: 55,
    width: 45,
    marginRight: 15
  },
  address: {
    fontSize: 12,
    marginBottom: 5,
    color: '#777'
  },
  trending: {
    fontSize: 12,
    color: '#777'
  },
  smallIcon: {
    width: 17,
    height: 27,
    marginTop: 3
  },
  mainContent: {
    flex: 1 // by setting flex to 1, this column will stretch to fill any extra space
  },
  travel: {
    alignItems: 'flex-end'
  },
  travelText: {
    color: colors.PRIMARY_PURPLE
  }
});`

const resultsListImport = `import ResultsList from './ResultsList'`

const mapComponentResultsList = `export default class Map extends Component {
  constructor (props) {
    super(props)
    this.state = {
      markers: []
    }

    this.setSelectedVenue = this.setSelectedVenue.bind(this)
  }

  componentWillMount() {
    this.setState({ markers: mockdata.results })
  }

  // function to select a venue, pass to map markers and ResultsList
  setSelectedVenue(venue) {
    this.setState({
      selectedVenue: venue
    })
  }

  render () {
    return (
      <View style={styles.container}>
        <MapView
          style={styles.container}
          initialRegion={{
            latitude: mockdata.origin.latitude,
            longitude: mockdata.origin.longitude,
            latitudeDelta: mockdata.origin.latitudeDelta,
            longitudeDelta: mockdata.origin.longitudeDelta
          }}
        >
          {this.state && this.state.markers.length > 0 ? this.state.markers.map((marker, i) => {
            return (
              <Marker {...marker} key={i} onPress={() => this.setSelectedVenue(venue)}>
                <View style={styles.marker} />
              </Marker>
            )
          }) : null }
        </MapView>

        { /* check if there's any markers to display, if so generate list of results */}
        { this.state.markers.length > 0 && !this.state.selectedVenue &&
          <View style={styles.container}>
            <ResultsList
              setSelectedVenue={this.setSelectedVenue}
              results={this.state.markers} />
          </View>
        }
      </View>
    )
  }
}`

class MapUiView extends Component {
  render () {
    return (

17. Map

Now it’s time to display our search results. First, we’ll edit our Map component from the last workshop to display the results as map markers. Then, we can add another component to display the results in a list alongside the map.

The Screen

This is what we’ll be building. When it’s done we’ll have our mock data wired up with our styled components.

Map Markers

Let's add our mock data to the rest of our imports at the top of the 'Map.js' file

            <Highlight lang='javascript' value={imports} />

Add the mock data to our component level state, in the Map component. This will allow us to access the mock data from inside the component so we can populate our map and list with it.

            <Highlight lang='javascript' value={componentMockData} />

Delete the old 'handlePress' function from the Map component. Also delete the referenes to it in the class constructor and the MapView prop. We'll be replacing this later with a function that selects a venue when a marker is pressed. Our 'Map.js' file should now look like this:

            <Highlight lang='javascript' value={baseComponent} />

Modify the MapView to use our mock data. This will focus the map on a particular location

            <Highlight lang='javascript' value={mapViewMockData} />

Our map now centers on the correct location and the map markers are automatically added to the map. The Map component should render like this:

Results List

Now that our map and markers are working with our mock data, let's build a List component to give our users another way to view their search results. This is what we'll be building:

Create a new file in the components folder, called 'ResultsList.js'

            <Highlight lang='javascript' value={resultsList} />
            <Note>
Note:The ListView component efficiently displays lists of changing data by only creating list items that will be displayed on the screen - thus saving memory. https://facebook.github.io/react-native/docs/listview.html
</Note>

Create a new file in the component folder for our list items, 'ListItem.js'

            <Highlight lang='javascript' value={listItemComponent} />

Now let's add the styles for the List Item. We'll use flexbox to dynamically size the width of each row, this way it will stretch to fill the screen regardless of device size. The rest of the styles set the colors, padding, image sizes, etc.

            <Highlight lang='javascript' value={listItemStyles} />

Import 'ResultsList.js' into 'Map.js'

            <Highlight lang='javascript' value={resultsListImport} />

Add our setSelectedVenue function, bind it in our constructor, and pass it to both the results list and the map markers

            <Highlight lang='javascript' value={mapComponentResultsList} />

    )
  }
}

export default MapUiView
<commit_msg>update<commit_after>import React, { Component } from 'react'
import Highlight from 'react-syntax-highlight'
import Note from '../Note'

import mapScreenshot from './map-screenshot.png'
import mockMarkers from './mock-markers.png'
import listScreen from './list-screen.png'

const imports = `import mockdata from '../mock/mockdata.json'`

const componentMockData = `  componentWillMount() {
    this.setState({ markers: mockdata.results })
  }`

const baseComponent = `import React, { Component } from 'react'
import { View, StyleSheet, Text } from 'react-native'
import MapView, { Marker } from 'react-native-maps'
import mockdata from '../mock/mockdata.json'

export default class Map extends Component {
  constructor (props) {
    super(props)
    this.state = {
      markers: []
    }
  }

  componentWillMount () {
    this.setState({ markers: mockdata.results })
  }

  render () {
    return (
      <View style={styles.container}>
        <MapView
          style={styles.container}
          initialRegion={{
            latitude: 45.5209087,
            longitude: -122.6705107,
            latitudeDelta: 0.0922,
            longitudeDelta: 0.0421
          }}
        >
          {this.state && this.state.markers.length > 0 ? this.state.markers.map((marker, i) => {
            return (
              <Marker {...marker} key={i} onPress={() => this.setSelectedVenue(venue)>
                <View style={styles.marker} />
              </Marker>
            )
          }) : null }
        </MapView>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1
  },
  marker: {
    backgroundColor: '#550bbc',
    padding: 5,
    borderRadius: 5
  },
  text: {
    color: '#FFF',
    fontWeight: 'bold'
  }
})
`

const mapViewMockData = `<MapView
          style={styles.container}
          initialRegion={{
            latitude: mockdata.origin.latitude,
            longitude: mockdata.origin.longitude,
            latitudeDelta: mockdata.origin.latitudeDelta,
            longitudeDelta: mockdata.origin.longitudeDelta
          }}
        >
          { /*
            Our mock data is already being passed to each Marker
            component with {...marker}, which will provide our
            markers with the geolocation data they need to
            render correctly on our map
          */}
          {this.state && this.state.markers.length > 0 ? this.state.markers.map((marker, i) => {
            return (
              <Marker {...marker} key={i}>
                <View style={styles.marker} />
              </Marker>
            )
          }) : null }
      </MapView>`

const mapMarkerMockData = `{this.state && this.state.markers.length > 0 ? this.state.markers.map((marker, i) => {
            return (
              <Marker {...marker} key={i}>
                <View style={styles.marker}>
                  <Text style={styles.text}>{marker.label}</Text>
                </View>
              </Marker>
            )
          }) : null }`

const markersFinished = `export default class Map extends Component {
  constructor (props) {
    super(props)
    this.state = {
      markers: []
    }
  }

  componentWillMount() {
    this.setState({ markers: mockdata.results })
  }

  render () {
    return (
      <View style={styles.container}>
        <MapView
          style={styles.container}
          initialRegion={{
            latitude: mockdata.origin.latitude,
            longitude: mockdata.origin.longitude,
            latitudeDelta: mockdata.origin.latitudeDelta,
            longitudeDelta: mockdata.origin.longitudeDelta
          }}
        >
          {this.state && this.state.markers.length > 0 ? this.state.markers.map((marker, i) => {
            return (
              <Marker {...marker} key={i}>
                <View style={styles.marker} />
              </Marker>
            )
          }) : null }
        </MapView>
      </View>
    )
  }
}`

const resultsList = `import React, { Component } from 'react';
import { ListView } from 'react-native';
// The ListItem component doesn't exist yet, we'll make that next
import ListItem from './ListItem';

class ResultsList extends Component {
  constructor (props) {
    super(props);

    // We pass our mock data to this component from the Map component
    // then pass this.props.results to the ds object (created from ListView.DataSource)
    // to determine if a row should be rendered or not
    const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
    this.state = {
      dataSource: ds.cloneWithRows(this.props.results)
    };
  }

  renderRow (rowData) {
    return (
      <ListItem
        setSelectedVenue={this.props.setSelectedVenue}
        rowData={rowData} />
    );
  }

  render () {
    // ListView each row by taking in two props
    // 1. dataSource: the data you want to pass to your renderRow function
    // 2. renderRow: a function that take your data and return JSX to render each row
    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={rowData => this.renderRow(rowData)} />
    );
  }
}

export default ResultsList;`

const listItemComponent = `import React from 'react';
import { TouchableOpacity, View, Text, StyleSheet, Image } from 'react-native';

import * as colors from '../styles/colors';

export default ({ rowData, setSelectedVenue }) => {
  // get the properties we want from our mock data
  const { name, address, trendingNumber, travelTime } = rowData;
  return (
    <TouchableOpacity onPress={() => setSelectedVenue(rowData)}>
      <View style={styles.listItemContainer}>
        { /* for now we'll just use a static image, we can make this dynamic when we hook up the app to an API in the next workshop */ }
        <Image source={require('./../assets/images/Beer.png')} style={styles.iconImage} />
        { /* below we're just rendering the mock data */ }
        <View style={styles.mainContent}>
          <Text>{name}</Text>
          <Text style={styles.address}>{address}</Text>
          <Text style={styles.trending}>+{trendingNumber} others trending now</Text>
        </View>
        <View style={styles.travel}>
          <Text style={styles.travelText}>{travelTime}</Text>
          <Image source={require('./../assets/images/glass-outline.png')} style={styles.smallIcon} />
        </View>
      </View>
    </TouchableOpacity>
  );
};`

const listItemStyles = `const styles = StyleSheet.create({
  listItemContainer: {
    padding: 25,
    borderColor: '#777',
    borderBottomWidth: 0.5,
    flexDirection: 'row',
    backgroundColor: colors.WHITE
  },
  iconImage: {
    height: 55,
    width: 45,
    marginRight: 15
  },
  address: {
    fontSize: 12,
    marginBottom: 5,
    color: '#777'
  },
  trending: {
    fontSize: 12,
    color: '#777'
  },
  smallIcon: {
    width: 17,
    height: 27,
    marginTop: 3
  },
  mainContent: {
    flex: 1 // by setting flex to 1, this column will stretch to fill any extra space
  },
  travel: {
    alignItems: 'flex-end'
  },
  travelText: {
    color: colors.PRIMARY_PURPLE
  }
});`

const resultsListImport = `import ResultsList from './ResultsList'`

const mapComponentResultsList = `export default class Map extends Component {
  constructor (props) {
    super(props)
    this.state = {
      markers: [],
      selectedVenue: null
    }

    this.setSelectedVenue = this.setSelectedVenue.bind(this)
  }

  componentWillMount() {
    this.setState({ markers: mockdata.results })
  }

  // function to select a venue, pass to map markers and ResultsList
  setSelectedVenue(venue) {
    this.setState({
      selectedVenue: venue
    })
  }

  render () {
    return (
      <View style={styles.container}>
        <MapView
          style={styles.container}
          initialRegion={{
            latitude: mockdata.origin.latitude,
            longitude: mockdata.origin.longitude,
            latitudeDelta: mockdata.origin.latitudeDelta,
            longitudeDelta: mockdata.origin.longitudeDelta
          }}
        >
          {this.state && this.state.markers.length > 0 ? this.state.markers.map((marker, i) => {
            return (
              <Marker {...marker} key={i} onPress={() => this.setSelectedVenue(venue)}>
                <View style={styles.marker} />
              </Marker>
            )
          }) : null }
        </MapView>

        { /* check if there's any markers to display, if so generate list of results */}
        { this.state.markers.length > 0 && !this.state.selectedVenue &&
          <View style={styles.container}>
            <ResultsList
              setSelectedVenue={this.setSelectedVenue}
              results={this.state.markers} />
          </View>
        }
      </View>
    )
  }
}`

class MapUiView extends Component {
  render () {
    return (

17. Map

Now it’s time to display our search results. First, we’ll edit our Map component from the last workshop to display the results as map markers. Then, we can add another component to display the results in a list alongside the map.

The Screen

This is what we’ll be building. When it’s done we’ll have our mock data wired up with our styled components.

Map Markers

Let's add our mock data to the rest of our imports at the top of the 'Map.js' file

            <Highlight lang='javascript' value={imports} />

Add the mock data to our component level state, in the Map component. This will allow us to access the mock data from inside the component so we can populate our map and list with it.

            <Highlight lang='javascript' value={componentMockData} />

Delete the old 'handlePress' function from the Map component. Also delete the referenes to it in the class constructor and the MapView prop. We'll be replacing this later with a function that selects a venue when a marker is pressed. Our 'Map.js' file should now look like this:

            <Highlight lang='javascript' value={baseComponent} />

Modify the MapView to use our mock data. This will focus the map on a particular location

            <Highlight lang='javascript' value={mapViewMockData} />

Our map now centers on the correct location and the map markers are automatically added to the map. The Map component should render like this:

Results List

Now that our map and markers are working with our mock data, let's build a List component to give our users another way to view their search results. This is what we'll be building:

Create a new file in the components folder, called 'ResultsList.js'

            <Highlight lang='javascript' value={resultsList} />
            <Note>
Note:The ListView component efficiently displays lists of changing data by only creating list items that will be displayed on the screen - thus saving memory. https://facebook.github.io/react-native/docs/listview.html
</Note>

Create a new file in the component folder for our list items, 'ListItem.js'

            <Highlight lang='javascript' value={listItemComponent} />

Now let's add the styles for the List Item. We'll use flexbox to dynamically size the width of each row, this way it will stretch to fill the screen regardless of device size. The rest of the styles set the colors, padding, image sizes, etc.

            <Highlight lang='javascript' value={listItemStyles} />

Import 'ResultsList.js' into 'Map.js'

            <Highlight lang='javascript' value={resultsListImport} />

Now we'll add our setSelectedVenue function, which will allow the user to select a particular venue in order to view more details about it. We'll also need to add the setSelectedVenue property to the state,
              bind our new function in our constructor, and pass it to both the results list and the map markers

            <Highlight lang='javascript' value={mapComponentResultsList} />

    )
  }
}

export default MapUiView
<|endoftext|>