<!DOCTYPE html>
<html ng-app='app'>

<head>
  <link data-require="bootstrap-css@3.1.1" data-semver="3.1.1" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
  <script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
  <script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
  <script data-require="bootstrap@*" data-semver="3.1.1" src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="phones.js"></script>
  <script src="script.js"></script>
</head>

<body ng-controller='MyCtrl'>
    <table class="table">
      <thead>
        <tr sort-group="myGrid">
          <th ng-repeat="column in columns" sort-by="{{column.name}}" >
            {{column.label}}
            <span 
              ng-show="myGrid.sort.column === column.name" 
              class="text-primary glyphicon"
              ng-class="{'glyphicon-arrow-up': myGrid.sort.direction==='+',  'glyphicon-arrow-down': myGrid.sort.direction==='-' }">
            </span>
          </th>
        </tr>
        <!-- filter by column -->
        <tr>
          <td class="input" ng-repeat="column in columns" >
            <input placeholder="{{column.label}}" ng-model="myFilter[column.name]">
          </td>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="phone in phones | filter:myFilter:comparator | orderBy: myGrid.sort.orderByExp()">
          <td ng-repeat="column in columns">
            {{phone[column.name]}}
          </td>
        </tr>
      </tbody>
    </table>
  
</body>
</html>
angular.module('app', [])
  .directive('sortGroup', function() {
    return {
      restrict: 'A',
      controller: function($scope, $element, $attrs) {
        // create a namespace to host the different sort bindings
        var ns = $scope[$attrs.sortGroup] = {};
        var sort = ns.sort = {
          column      : '',
          direction   : '+',
          orderByExp  : function() {
            return sort.direction + sort.column; 
          }
        };
        
        // method called by child sort-by directives
        this.toggleSort = function(column) {
          if (column === sort.column) {
            // same column, toggle the direction asc <-> desc
            sort.direction = (sort.direction === '+' ? '-' : '+');
          } else {
            // new column, sort asc
            sort.column = column;
            sort.direction = '+';
          }
          $scope.$apply();
        }
       
      }
    }
  })
  .directive('sortBy', function() {
    return {
      restrict: 'A',
      require: '^sortGroup',
      link: function(scope, el, attrs, ctrl) {
        el.on('click', function(e) {
          ctrl.toggleSort(attrs.sortBy); 
        })
      }
    }
  })
  .directive('filterGroup', function() {
    
    function FilterGroup(joinOp) {
      this.filters = [];
      
      function and() {
        var ok = true;
        for (var i = 0; i < this.filters.length; i++) {
          var f = this.filters[i];
          ok = ok && f.applyFilter();
        }
        return ok;
      }
      
      function or() {
        var ok = true;
        for (var i = 0; i < this.filters.length; i++) {
          var f = this.filters[i];
          ok = ok||f.applyFilter();
        }
        return ok;
      }
      
      this.filterFn = function() {
        return joinOp === 'or' ? or : anf;
      }
    }
    
    return {
      restrict: 'AE',
      controller: function($scope, $element, $attrs) {
        // create a namespace to host the different sort bindings
        var fg = $scope[$attrs.filterGroup] = new FilterGroup($attrs.op);
        this. addFilter = function(filter) {
          fg.filters.add(filter);
        };
      }
    }
  })
  .directive('filter', function(){
    
    function Filter(predicate) {
      var comps = {
        'string': {
          '=': function(act, exp) { return act === exp; },
          'in': function(act, exp) { return act.indexOf(exp) >= 0 ; },
          'st': function(act, exp) { return act.indexOf(exp) === 0; }, 
          'end': function(act, exp) { return act.indexOf(exp) === (act.length - exp.length); }
        },
        'number': {
          '=': function(act, exp) { return act === exp; }, 
          '!=': function(act, exp) { return act !== exp; }, 
          '<=': function(act, exp) { return act <= exp; }, 
          '<': function(act, exp) { return act < exp; }, 
          '>=': function(act, exp) { return act >= exp; }, 
          '>': function(act, exp) { return act > exp; }  
        }
        '=': function(act, exp) { return act === exp; }, 
        '!=': function(act, exp) { return act !== exp; }, 
        '<=': function(act, exp) { return act <= exp; }, 
        '<': function(act, exp) { return act < exp; }, 
        '>=': function(act, exp) { return act >= exp; }, 
        '>': function(act, exp) { return act > exp; }, 
         
      }
      this.applyFilter = function(val) {
        
      }
      
    }
    
    return {
      restrict: 'AE',
      require: '^FilterGroup',
      link: function(scope, el, attrs, filterGroup) {
        
        filterGroup.addFilter()
      }
    }
  })
  .controller('MyCtrl', function($scope) {
    $scope.columns = [{
      name: 'id',
      label: 'Id'
    }, {
      name: 'name',
      label: 'Name'
    }, {
      name: 'age',
      label: 'Age'
    }]
    $scope.phones = phones;

    $scope.myFilter = {};
    
    var comparators = {
      '=': function(act, exp) {  return act === exp },
      '!=': function(act, exp) {  return act !== exp },
      '<=': function(act, exp) {  return act <= exp },
      '>=': function(act, exp) {  return act >= exp },
      '<': function(act, exp) {  return act < exp },
      '>': function(act, exp) {  return act > exp },
      'in': function(act, exp) {  return act.indexof(exp) >= 0 },
      'start': function(act, exp) {  return act.indexof(exp) === 0 },
    }
    
    function compareVal(act, exp, op) {
      if(exp === undefined) return true;
      var fnCompare = comparators[op];
      if(fnCompare) 
        return fnCompare(act, exp);
      
      // unsupported type
      return false;
    }
    
    function compareGroup(item, predicates, logOp) {
      var ok = true;
      for(var key in predicates) {
        var pred = predicates[key];
        var result = compare(item[key], pred.value, pred.op)
        ok = (logOp === '$and') ? (ok && result) : (ok || result)
      }
      return ok;
    }
    
    $scope.filter = function(columns, array, predicate) {
      
    }
    
  })
body {
  padding: 60px;
}

th {
  cursor: pointer;
}

td {
  padding: 2px !important;
}
# Angular super grid #
### first iteration ###
var phones = [
    {
        "age": 0, 
        "id": "motorola-xoom-with-wi-fi", 
        "imageUrl": "img/phones/motorola-xoom-with-wi-fi.0.jpg", 
        "name": "Motorola XOOM\u2122 with Wi-Fi", 
        "snippet": "The Next, Next Generation\r\n\r\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb)."
    }, 
    {
        "age": 1, 
        "id": "motorola-xoom", 
        "imageUrl": "img/phones/motorola-xoom.0.jpg", 
        "name": "MOTOROLA XOOM\u2122", 
        "snippet": "The Next, Next Generation\n\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb)."
    }, 
    {
        "age": 2, 
        "carrier": "AT&amp;T", 
        "id": "motorola-atrix-4g", 
        "imageUrl": "img/phones/motorola-atrix-4g.0.jpg", 
        "name": "MOTOROLA ATRIX\u2122 4G", 
        "snippet": "MOTOROLA ATRIX 4G the world's most powerful smartphone."
    }, 
    {
        "age": 3, 
        "id": "dell-streak-7", 
        "imageUrl": "img/phones/dell-streak-7.0.jpg", 
        "name": "Dell Streak 7", 
        "snippet": "Introducing Dell\u2122 Streak 7. Share photos, videos and movies together. It\u2019s small enough to carry around, big enough to gather around."
    }, 
    {
        "age": 4, 
        "carrier": "Cellular South", 
        "id": "samsung-gem", 
        "imageUrl": "img/phones/samsung-gem.0.jpg", 
        "name": "Samsung Gem\u2122", 
        "snippet": "The Samsung Gem\u2122 brings you everything that you would expect and more from a touch display smart phone \u2013 more apps, more features and a more affordable price."
    }, 
    {
        "age": 5, 
        "carrier": "Dell", 
        "id": "dell-venue", 
        "imageUrl": "img/phones/dell-venue.0.jpg", 
        "name": "Dell Venue", 
        "snippet": "The Dell Venue; Your Personal Express Lane to Everything"
    }, 
    {
        "age": 6, 
        "carrier": "Best Buy", 
        "id": "nexus-s", 
        "imageUrl": "img/phones/nexus-s.0.jpg", 
        "name": "Nexus S", 
        "snippet": "Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet."
    }, 
    {
        "age": 7, 
        "carrier": "Cellular South", 
        "id": "lg-axis", 
        "imageUrl": "img/phones/lg-axis.0.jpg", 
        "name": "LG Axis", 
        "snippet": "Android Powered, Google Maps Navigation, 5 Customizable Home Screens"
    }, 
    {
        "age": 8, 
        "id": "samsung-galaxy-tab", 
        "imageUrl": "img/phones/samsung-galaxy-tab.0.jpg", 
        "name": "Samsung Galaxy Tab\u2122", 
        "snippet": "Feel Free to Tab\u2122. The Samsung Galaxy Tab\u2122 brings you an ultra-mobile entertainment experience through its 7\u201d display, high-power processor and Adobe\u00ae Flash\u00ae Player compatibility."
    }, 
    {
        "age": 9, 
        "carrier": "Cellular South", 
        "id": "samsung-showcase-a-galaxy-s-phone", 
        "imageUrl": "img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg", 
        "name": "Samsung Showcase\u2122 a Galaxy S\u2122 phone", 
        "snippet": "The Samsung Showcase\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance, even outdoors"
    }, 
    {
        "age": 10, 
        "carrier": "Verizon", 
        "id": "droid-2-global-by-motorola", 
        "imageUrl": "img/phones/droid-2-global-by-motorola.0.jpg", 
        "name": "DROID\u2122 2 Global by Motorola", 
        "snippet": "The first smartphone with a 1.2 GHz processor and global capabilities."
    }, 
    {
        "age": 11, 
        "carrier": "Verizon", 
        "id": "droid-pro-by-motorola", 
        "imageUrl": "img/phones/droid-pro-by-motorola.0.jpg", 
        "name": "DROID\u2122 Pro by Motorola", 
        "snippet": "The next generation of DOES."
    }, 
    {
        "age": 12, 
        "carrier": "AT&amp;T", 
        "id": "motorola-bravo-with-motoblur", 
        "imageUrl": "img/phones/motorola-bravo-with-motoblur.0.jpg", 
        "name": "MOTOROLA BRAVO\u2122 with MOTOBLUR\u2122", 
        "snippet": "An experience to cheer about."
    }, 
    {
        "age": 13, 
        "carrier": "T-Mobile", 
        "id": "motorola-defy-with-motoblur", 
        "imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg", 
        "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122", 
        "snippet": "Are you ready for everything life throws your way?"
    }, 
    {
        "age": 14, 
        "carrier": "T-Mobile", 
        "id": "t-mobile-mytouch-4g", 
        "imageUrl": "img/phones/t-mobile-mytouch-4g.0.jpg", 
        "name": "T-Mobile myTouch 4G", 
        "snippet": "The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi."
    }, 
    {
        "age": 15, 
        "carrier": "US Cellular", 
        "id": "samsung-mesmerize-a-galaxy-s-phone", 
        "imageUrl": "img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg", 
        "name": "Samsung Mesmerize\u2122 a Galaxy S\u2122 phone", 
        "snippet": "The Samsung Mesmerize\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance,even outdoors"
    }, 
    {
        "age": 16, 
        "carrier": "Sprint", 
        "id": "sanyo-zio", 
        "imageUrl": "img/phones/sanyo-zio.0.jpg", 
        "name": "SANYO ZIO", 
        "snippet": "The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value."
    }, 
    {
        "age": 17, 
        "id": "samsung-transform", 
        "imageUrl": "img/phones/samsung-transform.0.jpg", 
        "name": "Samsung Transform\u2122", 
        "snippet": "The Samsung Transform\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \u201cSprint ID Service Pack\u201d."
    }, 
    {
        "age": 18, 
        "id": "t-mobile-g2", 
        "imageUrl": "img/phones/t-mobile-g2.0.jpg", 
        "name": "T-Mobile G2", 
        "snippet": "The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible."
    }, 
    {
        "age": 19, 
        "id": "motorola-charm-with-motoblur", 
        "imageUrl": "img/phones/motorola-charm-with-motoblur.0.jpg", 
        "name": "Motorola CHARM\u2122 with MOTOBLUR\u2122", 
        "snippet": "Motorola CHARM fits easily in your pocket or palm.  Includes MOTOBLUR service."
    }
]