<!DOCTYPE html>
<html>

<head>
  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>
  <script data-require="angularjs@1.2.15" data-semver="1.2.15" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.js"></script>
  <script src="http://danielcrisp.github.io/angular-rangeslider/angular.rangeSlider.js"></script>
  <script src="http://huei90.github.io/angular-validation/dist/angular-validation.js"></script>
  <script src="http://huei90.github.io/angular-validation/dist/angular-validation-rule.js"></script>
  <link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/themes/smoothness/jquery-ui.css">
  <link rel="stylesheet" href="style.css" />
  <link rel="stylesheet" href="http://danielcrisp.github.io/angular-rangeslider/angular.rangeSlider.css">
  
  <script src="app.js"></script>
  <script src="flightSearchCtrl.js"></script>
  <script src="flightService.js"></script>
  <script src="dateTimePickerDirective.js"></script>
</head>

<body data-ng-app="flightSearch">
  <div class="main" ng-controller="flightSearchCtrl">
    <h1 class=header>Flight Search Engine</h1>
    <div class="amber search-error" ng-show="!clear && flightData.length == 0">No Flights available for given search criteria!!!</div>
    <section class="col col1 sidebar">
      <ul class="tabs group">
        <li ng-click="roundTrip=false;"><a href="#box-one" ng-class="{'highlight':!roundTrip}">One Way</a></li>
        <li ng-click="roundTrip=true;"><a href="#box-two" ng-class="{'highlight':roundTrip}">Two Way</a></li>
      </ul>
      <div class="tab">
        <form ng-submit="search(origin,destination)" name="form">
          <input type="text" name="origin" ng-model="origin" placeholder="Enter Origin City" required/>
          <span class ="amber" ng-show="(form.origin.$dirty || submitted) && form.origin.$error.required">
             Origin City is Required
          </span>
          <input type="text" name="departure" ng-model="destination" placeholder="Enter Destination City" required/>
          <span  class ="amber" ng-show="(form.departure.$dirty || submitted) && form.departure.$error.required">
            Departure City is Required
          </span>
          <div>
          <input type="text" name="departureDt" class="date" placeholder="Departure Date" ng-model="departureDate" datepicker required/>
          <span class ="amber"  ng-show="(form.departureDt.$dirty || submitted) && form.departureDt.$error.required">
             Departure Date is Required
          </span>
          </div>
          <div ng-if="roundTrip">
            <input type="text" name="returnDt" class="date" placeholder="Return Date" ng-model="returnDate" datepicker required/>
            <span  class ="amber" ng-show="(form.returnDt.$dirty || submitted || roundTrip)  && form.returnDt.$error.required">
               Return Date is Required
            </span>
          </div>
          <div>
            <label for="passengers">Passengers:</label>
            <select ng-model="count" id="passengers" required>
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
              <option value="4">4</option>
              <option value="5">5</option>
              <option value="6">6</option>
              <option value="7">7</option>
              <option value="8">8</option>
              <option value="9">9</option>
              <option value="10">10</option>
              <option value="11">11</option>
              <option value="12">12</option>
              <option value="13">13</option>
              <option value="14">14</option>
              <option value="15">15</option>
            </select>
          </div>
          <button class="search">search</button>
          <div class="rangeSlider">
            <h4>Refine Flight Search</h4>
            <div range-slider min="0" max="10000" model-min="range.minPrice" step="1000" model-max="range.maxPrice" filter="currency:'Rs'"></div>
          </div>
        </form>
      </div>
    </section>
    <section class="col  col2 container" ng-if="!clear">
      <h1 class="route"><span>{{origin}} > {{destination}}</span><span ng-show="roundTrip"> {{origin}}</span></h1>
      <div>
        <div>Departure Date: {{departureDate}}</div>
        <div ng-show="roundTrip">Return Date: {{returnDate}} </div>
        <div>Passengers count: {{count}}</div>
      </div>
      <div class="trip" ng-repeat="trips in flightData | filter:filterRange">
        <div class="floatRight" >
          <img ng-src="{{trips.imgUrl}}" alt="{{trips.airline}}" />
          <button ng-click="bookingStatus = 'BOOKED!!!'" ng-bind="bookingStatus"></button>
        </div>
        <div>
          <div class="price">Rs.{{trips.price}}</div>
          <div class="airModel">{{trips.airline}}</div>
          <div>{{trips.date}}</div>
          <div>{{trips.from}} > {{trips.to}}</div>
          <div>Depart :{{trips.departTime}}</div>
          <div>Arrive :{{trips.arriveTime}}</div>
          <div>seats left: {{trips.seats}}</div>
        </div>
      </div>
    </section>
  </div>
</body>
</html>
//main module of flight search engine
(function() {
    'use strict';
     angular.module('flightSearch',['ui-rangeSlider','validation', 'validation.rule']);      
}());
/* Styles go here */
*, *:after, *:before {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  -o-box-sizing:border-box;
  box-sizing: border-box;
}

body {
  height: 100%;
  font-size: 75%;
  font-family: "Trebuchet MS", Arial, Tahoma, sans-serif;
  color: #333;
  padding-top: 10px;
}

.col {
  float: left;
}

.col1 {
  width: 40%;
}
.col2 {
  width: 60%;
}

[class*='col'] {
  padding-right: 20px;
}
[class*='col']:last-of-type {
  padding-right: 0;
}

.main:after {
  content: "";
  display: table;
  clear: both;
}

.header {
  border-bottom: 1px solid #ddd;
  border-top: 1px solid #ddd;
  color: #363636;
  font-family: "Droid Serif", Georgia, serif;
  font-size: 2.6em;
  line-height: 1.8em;
  font-weight: 500;
  margin-top: 25px;
  margin-bottom: 25px;
  padding: 12px 0;
  text-align: center;
}

::-webkit-input-placeholder {
  /* WebKit browsers */
  text-transform: none;
}

:-moz-placeholder {
  /* Mozilla Firefox 4 to 18 */
  text-transform: none;
}

::-moz-placeholder {
  /* Mozilla Firefox 19+ */
  text-transform: none;
}

:-ms-input-placeholder {
  /* Internet Explorer 10+ */
  text-transform: none;
}

.route {
  text-transform:uppercase;
  margin-left:30%;
}

.container .trip .airModel {
  opacity:0.5;
}

input[type='text'] {
  text-transform: uppercase;
  display: block;
  font-family: "Helvetica Neue", Arial, sans-serif;
  border-style: solid;
  border-width: 1px;
  border-color: #dedede;
  margin: 10px;
  font-size: 1.2em;
  padding-left:5px;
  line-height:1.5;
  width: 70%;
  -moz-border-radius: 3px;
  -o-border-radius: 3px;
  -webkit-border-radius: 3px;
  border-radius: 3px;
  color: #777;
  -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) inset;
  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) inset;
  transition: border 0.15s linear 0s, box-shadow 0.15s linear 0s, color 0.15s linear 0s;
  -webkit-transition: border 0.15s linear 0s, box-shadow 0.15s linear 0s, color 0.15s linear 0s;
  -moz-transition: border 0.15s linear 0s, box-shadow 0.15s linear 0s, color 0.15s linear 0s;
  -o-transition: border 0.15s linear 0s, box-shadow 0.15s linear 0s, color 0.15s linear 0s;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) inset;
}

.main .sidebar .tab .date {
    background: url("https://cdn4.iconfinder.com/data/icons/small-n-flat/24/calendar-128.png") no-repeat rgb(255, 255, 255);
    background-size: 22px;
    background-position: right;
    cursor:pointer;
}

.search, .floatRight button {
  margin: 10px 5px 0 10px;
  background-color: #999999;
  border: 1px solid #999999;
  font-size: 18px;
  text-transform: uppercase;
  font-family: "BrixSansBold", Arial, Helvetica, sans-serif;
  padding: 8px 35px;
  -moz-border-radius: 2px;
  -o-border-radius: 2px;
  -webkit-border-radius: 2px;
  border-radius: 2px;
  color: #fff;
  cursor: pointer;
  -webkit-font-smoothing: antialiased;
}

.search:hover,.floatRight button:hover {
  -webkit-transition: 0.25s;
  -moz-transition: 0.25s;
  -ms-transition: 0.25s;
  -o-transition: 0.25s;
  transition: 0.25s;
  opacity: 0.9;
  color: #fff;
  text-decoration: none;
}

label,span.amber{
   margin: 10px;
}

.sidebar .tab .rangeSlider {
  margin-left:10px;
  margin-right:10px;
}

#ui-datepicker-div{
  z-index:2 !important;
}

.tabs {
  list-style: none;
}

.tabs li {
  display: inline;
}

.tab,
.tabs li a {
  border: 1px solid #ccc;
}

.tabs li a {
  color: black;
  float: left;
  display: block;
  padding: 4px 10px;
  margin-left: -1px;
  position: relative;
  left: 1px;
  background: white;
  text-decoration: none;
}


.sidebar .tabs li .highlight {
  background: #ccc;
}

.group:after {
  visibility: hidden;
  display: block;
  font-size: 0;
  content: " ";
  clear: both;
  height: 0;
}

.sidebar {
  box-sizing: border-box;
}

.floatRight {
  float: right;
}

.floatLeft {
  float: left;
}

.container .trip .price {
  font-size: 20pt;
}

.trip {
  border: 1px solid black;
}

.trip .floatRight button{
    margin-top: 44px;
    vertical-align: top;
    font-size: 12px;
     background-color: #2aabd2;
  border: 1px solid #2aabd2;
width:120px;
   
}

img {
  width: 100px;
  height: 100px;
}

.search-error{
  font-size: 21pt;
}

.amber {
  color: red;
}
Flight Search Engine, powered by angularjs
=============

What does it do?
----------------
+ Fetches flight data based on a advanced search query provided by user
+ Price Slider filters the searched flight data in Real time
+ integrated with validation framework
+ jquery-ui datepicker has been used for date fields

Test data[following are the static data available in this plunker]
----------------
+ Origin city is "BLR" & Destination city is "DEL", use them interchangeably to test the search
+ Departure date is "30/5/2016" & Return Date is "1/06/2016" use them interchangeably to test the search
+ Any change on the search form requires serach button to be triggered to perform search operation.
+ Price range slider doesnt require any search button triggring , it will filter out data dynamically.


Demo 
----------------
[demo @ plunker](http://plnkr.co/edit/p2JMjv?p=info)



{
  "data": [{
    "airline": "Airindia 737",
    "imgURL": "http://www.newsread.in/wp-content/uploads/2015/07/Air-India-Logo-480x470.jpg",
    "from": "BLR",
    "to": "DEL",
    "detail": [{
      "date": "2016-05-30",
      "price": "3900",
      "departTime": "12:00 PM",
      "arriveTime": "14:00 PM",
      "seats":"10"
    }, {
      "date": "2016-05-30",
      "price": "3000",
      "departTime": "17:30 PM",
      "arriveTime": "19:30 PM",
      "seats":"3"
    }, {
      "date": "2016-06-01",
      "price": "2100",
      "departTime": "09:00 AM",
      "arriveTime": "11:00 AM",
      "seats":"7"
    }]
  }, {
    "airline": "Airindia 737",
    "imgURL": "http://www.newsread.in/wp-content/uploads/2015/07/Air-India-Logo-480x470.jpg",
    "from": "DEL",
    "to": "BLR",
    "detail": [{
      "date": "2016-05-30",
      "price": "3900",
      "departTime": "08:10 AM",
      "arriveTime": "10:10 AM",
      "seats":"5"
    }, {
      "date": "2016-05-30",
      "price": "3000",
      "departTime": "15:45 PM",
      "arriveTime": "17:45 PM",
      "seats":"1"
    }, {
      "date": "2016-06-01",
      "price": "2190",
      "departTime": "19:20 PM",
      "arriveTime": "21:20 PM",
      "seats":"12"
    }]
  }, {
    "airline": "Indigo Airbus",
    "imgURL": "https://upload.wikimedia.org/wikipedia/en/9/93/IndiGo_Logo.jpg",
    "from": "BLR",
    "to": "DEL",
    "detail": [{
      "date": "2016-05-30",
      "price": "3000",
      "departTime": "09:00 AM",
      "arriveTime": "10:50 AM",
      "seats":"3"
    }, {
      "date": "2016-05-30",
      "price": "2910",
      "departTime": "14:00 PM",
      "arriveTime": "15:50 PM",
      "seats":"5"
    }, {
      "date": "2016-06-01",
      "price": "3180",
      "departTime": "21:10 AM",
      "arriveTime": "23:00 AM",
      "seats":"12"
    }]
  }, {
    "airline": "Indigo Airbus",
    "imgURL": "https://upload.wikimedia.org/wikipedia/en/9/93/IndiGo_Logo.jpg",
    "from": "DEL",
    "to": "BLR",
    "detail": [{
      "date": "2016-05-30",
      "price": "2820",
      "departTime": "07:50 AM",
      "arriveTime": "09:40 AM",
      "seats":"7"
    }, {
      "date": "2016-05-30",
      "price": "2901",
      "departTime": "11:00 AM",
      "arriveTime": "13:10 PM",
      "seats":"21"
    }, {
      "date": "2016-06-01",
      "price": "3000",
      "departTime": "15:20 PM",
      "arriveTime": "17:10 PM",
      "seats":"3"
    }]
  }]
}
// flight search controller,containing entire business logic
angular.module('flightSearch')
  .controller('flightSearchCtrl', ['$scope', 'flightService',
    function($scope, flightService) {

      var data = [];
      $scope.origin = "";
      $scope.destination = "";
      $scope.price = 3000;
      $scope.roundTrip = false;
      $scope.clear = true;
      $scope.count = 1;
      //$scope.minPrice = 0;
     //$scope.maxPrice = 10000;
      $scope.flightData = [];
      $scope.bookingStatus = 'BOOK';
      
      // max and min default price value for range slider 
      $scope.range = {
        minPrice: 0,
        maxPrice: 3500
      };

      //filter function for price range slider
      $scope.filterRange = function(obj) {
        return obj.price > $scope.range.minPrice && obj.price <= $scope.range.maxPrice;
      };

     //remote data request & processing data for view to render
     $scope.search = function(org, dest) {
        //get flight details for given search criteria
        $scope.submitted = true;
        $scope.clear = true; //clear the container before making fresh search
        org = org.toUpperCase();
        dest = dest.toUpperCase();

        // call flightService to get remote data
        flightService.search()
          .then(function(response) {
            data = [];
            for (var index = 0; index < response.length; index++) {
              var curFlight = response[index];
              if (org.includes(curFlight.from) && dest.includes(curFlight.to)) {
                filterRecords(curFlight, $scope.departureDate, index, $scope.price, $scope.count);
              } else if ($scope.roundTrip && (org.includes(curFlight.to) && dest.includes(curFlight.from))) {
                filterRecords(curFlight, $scope.returnDate, index, $scope.price, $scope.count);
              }
            }
            $scope.flightData = data;
            $scope.clear = false;
          });
      };

      //private functions
      //date equality checker
      var dateComparer = function(firstDate, secondDate) {
        return (new Date(firstDate).getTime() == new Date(secondDate).getTime());
      };

      //traverse & pick matching records from JSON array
      var filterRecords = function(curFlight, date, curIndex, price,seatsCount) {
        var currentRecord = curFlight.detail;
        for (var index = 0; index < currentRecord.length; index++) {
          var curTrip = currentRecord[index];
          if (dateComparer(curTrip.date, date) && parseInt(curTrip.seats) >= seatsCount) {
            curTrip.airline = curFlight.airline;
            curTrip.imgUrl = curFlight.imgURL;
            curTrip.from = curFlight.from;
            curTrip.to = curFlight.to;
            data.push(curTrip);
          }
        }
      };
    }
  ]);
//Flight service factory to contact remote server and ask for data
(function() {
  'use strict';
  angular.module('flightSearch')
    .factory('flightService', ['$http', function($http) {

      var flightService = {};

      //private function
      //get request to local resource flights.json
      function getFlightsData() {
        return $http.get('flights.json').then(function(resp) {
          return resp.data;
        });
      }

      //public function exposed outside
      flightService.search = function(origin, destination) {
        return getFlightsData().then(function(response) {
          return response.data;
        });

      };

      return flightService;
    }]);
}());
//jquery ui datepicker for anugular js
(function() {
  angular.module('flightSearch')
    .directive("datepicker", function() {
      return {
        restrict: "A",
        require: "ngModel",
        link: function(scope, elem, attrs, ngModelCtrl) {
          var updateModel = function(dateText) {
            scope.$apply(function() {
              ngModelCtrl.$setViewValue(dateText);
            });
          };
          var options = {
            dateFormat:"yy-mm-dd",
            numberOfMonths: 2,
            //minDate: 0,
            maxDate: 90,
            onSelect: function(dateText) {
              updateModel(dateText);
            }
          };
          elem.datepicker(options);
        }
      };
    });
})();