<head>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet" />

    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-resource.min.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.20/angular-route.min.js"></script>
</head>
<body ng-app="githubApp">
    <div class="container wrapper" ng-controller="MainCtrl">
        <div class="row">
            <div class="col-sm-12 col-md-12">
                <div class="container">
                  <div class="col-sm-12 text-center">
                    <h2 class="text-success">Search Github</h2>
                  </div>
                    <div class="row">
                        <div class="col-xs-8 col-xs-offset-2">
                            <div class="input-group">
                                <select ng-model="searchCategory" class="form-control"
                                        ng-options="cat.name as cat.name for cat in categories">
                                </select>
                                <input type="text" class="form-control" name="search-git"
                                       ng-model="searchTerm" placeholder="Search term..."
                                        on-enter="doSearch()">
                                <span class="input-group-btn">
                                    <button class="btn btn-default" type="button" ng-click="doSearch()">
                                        <span class="glyphicon glyphicon-search"></span>
                                    </button>
                                </span>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="col-xs-12 col-sm-6 col-md-6 text-center project"
                     ng-repeat="result in results | offset: pconfig.currentPage*pconfig.itemsPerPage | limitTo: pconfig.itemsPerPage">
                    <div class="panel panel-info clearfix">
                        <div class="panel-heading">
                            <a ng-href="{{result.url}}" class="color-primary" title="Go to project page" target="_blank">
                                <h4>{{result.title}}</h4>
                            </a>
                        </div>
                        <div class="panel-body">
                            <div class="general-description clearfix">
                                <div class="col-sm-6">
                                    <img ng-src="{{result.owner.avatarUrl}}" />
                                    <a ng-href="{{result.owner.profileUrl}}" title="Go to profile" target="_blank">
                                        <span my-truncate="{{result.owner.name}}"></span>
                                    </a>
                                </div>
                                <div class="col-sm-6 text-left">
                                    <div ng-show="result.createdAt">
                                        <span>Created:</span>
                                        <i>{{result.createdAt | date}}</i><br/>
                                    </div>
                                    <div ng-show="result.watchers">
                                        <span>Watchers:</span>
                                        <b>{{result.watchers}}</b><br/>
                                    </div>
                                    <span>Score:</span>
                                    <label class="label label-success">{{result.score | number:2}}</label>
                                </div>
                            </div>
                            <div class="col-sm-12">
                                <p>{{result.description}}</p>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div id="pagination" class="text-center"></div>
        </div>
    </div>
</body>
.wrapper{
    background-color: #EDEDED;
    padding-top: 40px;
}
.general-description{
    margin-bottom: 20px;
}
.general-description img{
    width: auto;
    max-width: 52px;
    height: auto;
    max-height: 52px;
}
.project{
    height: 280px;
    max-height: 280px;
    margin-bottom: 20px;
}
.panel{
    height: 100%;
}
.panel-body{
    padding: 10px 0 0 0;
}
.input-group .form-control{
    width: auto !important;
}
.input-group{
    margin: 0 auto;
    padding: 20px;
}
.input-group .input-group-btn{
    float: left;
}
'use strict';

var githubApp = angular.module('githubApp', [
        'ngRoute',
        'ngResource',
        'MainCtrl',
        'Utils',
        'Config'
]);

githubApp.config(["$routeProvider", function($routeProvider){
    $routeProvider
        .when('/', {
            controller: 'MainCtrl'
        })
        .otherwise({ redirectTo: '/' });
}]),
  
angular.module('Config', []).constant('Config',
    {
        searchTerm: 'Angular',
        searchCategories: [
            { name: 'repositories' },
            { name: 'users' }
        ]
    }
),
  
  angular.module('MainCtrl', []).controller('MainCtrl', ['$scope','$compile' ,'Config','Github','Utils' ,
function($scope, $compile, Config, Github, Utils){

    $scope.searchTerm = Config.searchTerm;
    $scope.categories = Config.searchCategories;
    $scope.searchCategory = Config.searchCategories[0].name;

    var GithubApi = new Github();
    getResults();
    function getResults(){
        var searchRepos = GithubApi.searchAngularRepos($scope.searchTerm, $scope.searchCategory);

        searchRepos.success(function(data){
            if(data && data.items){
                $scope.results = $scope.searchCategory == 'repositories' ?
                    Utils.extractInfoRepo(data.items) : Utils.extractInfoUser(data.items);
                paginate();
            }
        });
    }


    $scope.doSearch = function(){
        getResults();
    };

    function paginate(){
        $scope.pconfig ={
            currentPage: 0,
            list: $scope.results,
            itemsPerPage: 6,
            id: 'pagination'
        };
        Utils.paginate($scope.pconfig);
        $compile($scope.pconfig.$editLine)($scope);
    }
}]),
  
  githubApp.directive('myTruncate', [ function() {

    return {
        link: function link(scope, element, attrs) {
            element.text(
                    attrs.myTruncate.length > 10 ? attrs.myTruncate.substr(0,8)+'...' : attrs.myTruncate
            );
        }
    };

}]),
  githubApp.directive('onEnter',[ function() {
    return function(scope, element, attrs) {
        element.bind("keydown keypress", function(event) {
            if(event.which === 13) {
                scope.$apply(function(){
                    scope.$eval(attrs.onEnter, {'event': event});
                });
                event.preventDefault();
            }
        });
    };
}]),
  
  githubApp.filter('offset', function() {
    return function(input, start) {
        if(input){
            start = parseInt(start, 10);
            return input.slice(start);
        }
    };
}),
  
githubApp.factory('Github', ['$http', function($http){

    function Github() {

    }

    Github.prototype.searchAngularRepos = function(term, category) {
        return $http.get('https://api.github.com/search/'+category, { params: { q: term } });
    };

    return Github;
}]),
  
  angular.module('Utils', []).service("Utils", [function() {

    this.extractInfoRepo = function(gitHubResults){
        var relevantInfo = [];
        gitHubResults.forEach(function(result){
           var filteredResult = {}, owner = {};
            filteredResult.title = result.name;
            filteredResult.description = result.description || 'No description.';
            filteredResult.createdAt = result.created_at;
            filteredResult.url = result.html_url;
            filteredResult.watchers = result.watchers;
            filteredResult.score = result.score;
            owner.name = result.owner.login;
            owner.avatarUrl = result.owner.avatar_url;
            owner.profileUrl = result.owner.html_url;
            filteredResult.owner = owner;
            relevantInfo.push(filteredResult);
        });
        return relevantInfo;
    };
    this.extractInfoUser = function(gitHubResults){
        var relevantInfo = [];
        gitHubResults.forEach(function(result){
            var filteredResult = {}, owner = {};
            filteredResult.title = result.login;
            filteredResult.description = 'No description.';
            filteredResult.url = result.html_url;
            filteredResult.score = result.score;
            owner.name = result.type;
            owner.avatarUrl = result.avatar_url;
            owner.profileUrl = result.html_url;
            filteredResult.owner = owner;
            relevantInfo.push(filteredResult);
        });
        return relevantInfo;
    };

    /****************************** Pagination ************************************/
    this.paginate = function(pconfig) {
        pconfig.range = function() {
            var rangeSize = 15;
            var ret = [];
            var start;

            start = pconfig.currentPage;
            if ( start > pconfig.pageCount()-rangeSize ) {
                start = pconfig.pageCount()-rangeSize+1;
            }

            for (var i=start; i<start+rangeSize; i++) {
                if(i>0)
                    ret.push(i);
            }
            return ret;
        };
        pconfig.pageCount = function() {
            return Math.ceil(pconfig.list.length/pconfig.itemsPerPage)-1;
        };
        pconfig.rows = function() {
            return pconfig.list.length;
        };
        pconfig.from = function() {
            return pconfig.currentPage*pconfig.itemsPerPage;
        };
        pconfig.to = function() {
            var initial = pconfig.currentPage*pconfig.itemsPerPage + pconfig.itemsPerPage;
            if(initial<pconfig.rows())
                return initial;
            else
                return pconfig.rows();
        };
        pconfig.prevPage = function() {
            if (pconfig.currentPage > 0) {
                pconfig.currentPage--;
            }
        };

        pconfig.prevPageDisabled = function() {
            return pconfig.currentPage === 0 ? "disabled" : "";
        };

        pconfig.nextPage = function() {
            if (pconfig.currentPage < pconfig.pageCount()) {
                pconfig.currentPage++;
            }
        };

        pconfig.nextPageDisabled = function() {
            return pconfig.currentPage === pconfig.pageCount() ? "disabled" : "";
        };
        pconfig.setPage = function(n) {
            pconfig.currentPage = n;
        };
        pconfig.$editLine = $('<ul class="pagination">' +
            '<li ng-class="pconfig.prevPageDisabled()"><a href="" ng-click="pconfig.prevPage()">&#171;</a></li>' +
            '<li ng-class="{active: 0 == pconfig.currentPage}" ng-click="pconfig.setPage(0)"><a href="">1</a></li>' +
            '<li ng-repeat="n in pconfig.range()" ng-class="{active: n == pconfig.currentPage}" ng-click="pconfig.setPage(n)">' +
            '<a href="">{{n+1}}</a></li>' +
            '<li ng-hide="pconfig.currentPage > pconfig.pageCount()-10" ng-click="pconfig.setPage(pconfig.pageCount())">' +
            '<a href="">{{pconfig.pageCount()+1}}</a></li>' +
            '<li ng-class="pconfig.nextPageDisabled()"><a href="" ng-click="pconfig.nextPage()">&#187;</a></li></ul>');
        $('#'+pconfig.id).html(pconfig.$editLine);
    };
}]);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.