Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                
<html>
<head>
 
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
      
    <title>Read volunteers</title>
     
     
    <!-- include material design icons -->
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
</head>
<body>
 
<!-- page content and controls will be here -->

<div class="container" ng-app="myApp" ng-controller="volunteersCtrl">
    <div class="row">
        <div class="col s12">
            <h4>Volunteers</h4>
            <!-- used for searching the current list -->
<div class="row">
        <div style="min-width: 150px" class="input-field col s2 nopad">
            
            <select ng-model="selected" ng-change="hasChanged()" ng-options="item.title for item in fieldTable">
            </select>
        </div>
 <div class="input-field col s8 nopad">
   <input type="text" ng-model="search" class="form-control" placeholder="Search volunteers..." />
   </div>
    </div>
    
    <div id="email-list" class="modal">
    <div class="modal-content">
    	<div class="row">	<b ng-repeat="d in names | filter:filters">{{d.email}}{{$last ? '' : ', '}}</b></div>
    	</div></div>
    
    
  <a ng-href="mailto:{{d.email}}{{$last ? '' : ', '}}" class="waves-effect waves-light btn margin-bottom-1em"><i class="material-icons left">email</i>Email All {{selected.field}} Volunteers ({{(names | filter:filters).length}})</a>
 <a href="#email-list" class="modal-trigger waves-effect waves-light btn margin-bottom-1em">View List of Email Addresses ({{(names | filter:filters).length}})</a>
<br>
 
<!-- table that shows volunteer record list -->
<div style="width: 130px;" class="input-field col s2"><label for="resultlimit"> Results per page: </label></div>
            
                   <div style="min-width: 50px;" class="input-field col s1 ">
   
    <select name="resultlimit" ng-model="resultlimit" ng-init="resultlimit=5">
    	<option value="1">1</option>
      <option value="3">3</option>
      <option value="5">5</option>
      <option value="10">10</option>
       <option value="20">20</option>
      <option value="100">100</option>
    </select>
        </div>   <div class="sorter"><h6 style="display: inline-block;">Order by: </h6>
       <a  ng-class="{'activetab waves-effect waves-light' : predicate == 'name',  'disabled': predicate != 'name'}" ng-click="predicate = 'name'; reverse=!reverse" class="btn">Name
       	<i ng-show="predicate == 'name' && !reverse" class="material-icons right">expand_more</i>
         <i ng-show="predicate == 'name' && reverse" class="material-icons right">expand_less</i></a>
       <a ng-class="{'activetab waves-effect waves-light' : predicate == 'phone',  'disabled': predicate != 'phone'}" ng-click="predicate = 'phone'; reverse=!reverse" class="btn">Phone
       	<i ng-show="predicate == 'phone' && !reverse" class="material-icons right">expand_more</i>
         <i ng-show="predicate == 'phone' && reverse" class="material-icons right">expand_less</i></a>
        <a ng-class="{'activetab waves-effect waves-light' : predicate == 'email',  'disabled': predicate != 'email'}" ng-click="predicate = 'email'; reverse=!reverse" class="btn">Email
        	<i ng-show="predicate == 'email' && !reverse" class="material-icons right">expand_more</i>
         <i ng-show="predicate == 'email' && reverse" class="material-icons right">expand_less</i></a>
        <a ng-class="{'activetab waves-effect waves-light' : predicate == 'status',  'disabled': predicate != 'status'}" ng-click="predicate = 'status'; reverse=!reverse" class="btn">Status<i ng-show="predicate == 'status' && !reverse" class="material-icons right">expand_more</i>
         <i ng-show="predicate == 'status' && reverse" class="material-icons right">expand_less</i></a>
        <a ng-class="{'activetab waves-effect waves-light' : predicate == 'unix',  'disabled': predicate != 'unix'}" ng-click="predicate = 'unix'; reverse=!reverse" class="btn">Submited
        	<i ng-show="predicate == 'unix' && !reverse" class="material-icons right">expand_more</i>
         <i ng-show="predicate == 'unix' && reverse" class="material-icons right">expand_less</i></a>
         </div>
        <div class="divider">
        <hr /></div>
      <dir-pagination-controls boundary-links="true" on-page-change="pageChangeHandler(newPageNumber)"></dir-pagination-controls>   
            
   
<table class="table hoverable bordered">
 
    <thead>
        <tr>
            <th ng-class="{'activetab waves-effect waves-light' : predicate == 'id'}" ng-click="predicate = 'id'; reverse=!reverse">ID
            	<i ng-show="predicate == 'id' && !reverse" class="material-icons right">expand_more</i>
            	<i ng-show="predicate == 'id' && reverse" class="material-icons right">expand_less</i>
            	</th>
            <th ng-class="{'activetab waves-effect waves-light' : predicate == 'name'}"  ng-click="predicate = 'name'; reverse=!reverse">Name
            	<i ng-show="predicate == 'name' && !reverse" class="material-icons right">expand_more</i>
            	<i ng-show="predicate == 'name' && reverse" class="material-icons right">expand_less</i>
            	</th>
            <th ng-class="{'activetab waves-effect waves-light' : predicate == 'phone'}" ng-click="predicate = 'phone'; reverse=!reverse">Phone
             	<i ng-show="predicate == 'phone' && !reverse" class="material-icons right">expand_more</i>
            	<i ng-show="predicate == 'phone' && reverse" class="material-icons right">expand_less</i></th>
            <th ng-class="{'activetab waves-effect waves-light' : predicate == 'email'}" ng-click="predicate = 'email'; reverse=!reverse" >Email
            		<i ng-show="predicate == 'email' && !reverse" class="material-icons right">expand_more</i>
            	<i ng-show="predicate == 'email' && reverse" class="material-icons right">expand_less</i></th>
            <th ng-class="{'activetab waves-effect waves-light' : predicate == 'status'}" ng-click="predicate = 'status'; reverse=!reverse">Status
            	            		<i ng-show="predicate == 'status' && !reverse" class="material-icons right">expand_more</i>
            	<i ng-show="predicate == 'status' && reverse" class="material-icons right">expand_less</i></th>
            <th ng-class="{'activetab waves-effect waves-light' : predicate == 'unix'}" ng-click="predicate = 'unix'; reverse=!reverse">Submitted
            	<i ng-show="predicate == 'unix' && !reverse" class="material-icons right">expand_more</i>
            	<i ng-show="predicate == 'unix' && reverse" class="material-icons right">expand_less</i></th>
            <th class="text-align-center">Action</th>
        </tr>
    </thead>
 
    <tbody>
        <tr dir-paginate="d in names | filter:search | filter:filters | orderBy:predicate:reverse | itemsPerPage: resultlimit ">
            <td>{{ d.id }}</td>
            <td>{{ d.name }}</td>
            <td>{{ d.phone | tel }}</td>
            <td class="breakEmail"><a href="mailto:{{ d.email }}?Subject=Volunteering%20at%20our%20event" target="_blank">{{ d.email }}</a></td>
            <td>{{ d.status }}</td>
            <td>{{ d.unix | date:'MM/dd/yy h:mma' }}</td>
            <td>
                <a ng-click="readOne(d.id)" class="waves-effect waves-light btn customAction"><i class="material-icons left">edit</i>Edit</a>
                <a ng-click="deleteVolunteer($index)" class="waves-effect waves-light btn customAction"><i class="material-icons left">delete</i>Delete</a>
            </td>
        </tr>
    </tbody>
</table>
      <dir-pagination-controls boundary-links="true" on-page-change="pageChangeHandler(newPageNumber)"></dir-pagination-controls>   
            
   
            
            
            
            
            
            
<!-- modal for for creating new volunteer -->
<form name="volunteerForm" id="modal-volunteer-form" class="modal">
    <div class="modal-content">
        <h4 id="modal-volunteer-title">Create New Volunteer Record</h4>
        <div class="row">
            <div class="input-field col s12">
                <input name="name" ng-model="name" type="text" ng-minlength="5" ng-pattern="/^[a-zA-ZàáâäãåąčćęèéêëėįìíîïłńòóôöõøùúûüųūÿýżźñçčšžÀÁÂÄÃÅĄĆČĖĘÈÉÊËÌÍÎÏĮŁŃÒÓÔÖÕØÙÚÛÜŲŪŸÝŻŹÑßÇŒÆČŠŽ∂ð ,.'-]+$/u" id="form-name" placeholder="Type name here..." required />
                <label for="name">Name</label>
            </div>
 
            <div class="input-field col s12">
                <input ng-model="email" type="email" placeholder="Type email here..." required>
                <label for="email">Email Address</label>
            </div>
            
            
 
 
            <div class="input-field col s12">
                <input ng-model="phone" required   pattern="^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$" ng-minlength="10" phone-input type="tel" id="form-phone" placeholder="Type phone number here..."  />
                <label for="phone">Phone Number</label>
            </div>
            
            <div style="    margin-top: -10px; min-width: 150px" class="input-field col s2">
    <label for="status"> Status: </label><br>
    <select name="status" ng-model="status" ng-init="status='APPLIED'">
      <option value="APPLIED">APPLIED</option>
      <option value="APPROVED">APPROVED</option>
      <option value="WITHDRAWN">WITHDRAWN</option>
       <option value="DISMISSED">DISMISSED</option>
      <option value="BANNED">BANNED</option>
    </select>
        </div>
        
        
    
    
    
            <div class="input-field col nopad s12">
                <a ng-class="{'disabled': volunteerForm.$invalid}" id="btn-create-volunteer" class="waves-effect waves-light btn customAction" ng-click="volunteerForm.$valid && createVolunteer()"><i class="material-icons left">add</i>Create</a>
 
                <a ng-class="{'disabled': volunteerForm.$invalid}" id="btn-update-volunteer" class="waves-effect waves-light btn customAction" ng-click="volunteerForm.$valid && updateVolunteer()"><i class="material-icons left">edit</i>Save Changes</a>
 
                <a class="modal-action modal-close waves-effect waves-light btn customAction"><i class="material-icons left">close</i>Close</a>
            </div>
        </div>
    </div>
</form> <!--	END MODAL -->

<!-- floating button for creating volunteer -->
<div class="fixed-action-btn" style="bottom:45px; right:24px;">
    <a class="waves-effect waves-light btn modal-trigger btn-floating btn-large red" href="#modal-volunteer-form" ng-click="showCreateForm()"><i class="large material-icons">add</i></a
</div> <!-- END BUTTON -->
             
             
        </div> <!-- end col s12 -->
    </div> <!-- end row -->
</div> <!-- end container -->


 
<!-- include jquery -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 
<!-- material design js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.3/js/materialize.min.js"></script>
 
<!-- include angular js -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
 

</body>
</html>
              
            
!

CSS

              
                
.width-30-pct{
    width:30%;
}
 
.text-align-center{
    text-align:center;
}
 
.margin-bottom-1em{
    margin-bottom:1em;
}

.breakEmail, .breakEmail a{
	  /* These are technically the same, but use both */
  overflow-wrap: break-word;
  word-wrap: break-word;

  -ms-word-break: break-all;
  /* This is the dangerous one in WebKit, as it breaks things wherever */
  word-break: break-all;
  /* Instead use this non-standard one: */
  word-break: break-word;

  /* Adds a hyphen where the word breaks, if supported (No Blink) */
  -ms-hyphens: auto;
  -moz-hyphens: auto;
  -webkit-hyphens: auto;
  hyphens: auto;
	
}

.activetab {
	background-color: #ee6e73 !important;
	color: #fff!important;   min-width: 50px;
}

.activetab i.right { margin-left: 0px; }

tr th {cursor: pointer;}

.divider, .sorter {display: none;}

.sorter .btn {    margin-bottom: 2px;
    font-size: .75em; padding: 0 2px;}
    .customAction {margin-bottom: 2px;}

.ng-invalid.ng-dirty, input[type=tel].ng-invalid.ng-dirty:focus:not([readonly]){border-bottom: 1px solid #F44336;
    box-shadow: 0 1px 0 0 #F44336;}

@media screen and (max-width: 1300px) {
	.container { width:90%;}
}

/* 
Max width before this PARTICULAR table gets nasty
This query will take effect for any screen smaller than 760px
and also iPads specifically.
*/
@media 
only screen and (max-width: 760px),
(min-device-width: 768px) and (max-device-width: 1024px)  {
	tr {margin-bottom: 10px;}
	.divider {display: block; width:100%;}
	.sorter {display: inline-block; margin-top: 15px;}
	.nopad {padding: 0!important; margin: 0;}

	/* Force table to not be like tables anymore */
	table, thead, tbody, th, td, tr { 
		display: block; 
	}
	
	/* Hide table headers (but not display: none;, for accessibility) */
	thead tr { 
		position: absolute;
		top: -9999px;
		left: -9999px;
	}
	
	tr { border: 1px solid #ccc; }
	
	td { 
		/* Behave  like a "row" */
		border: none;
		border-bottom: 1px solid #eee; 
		position: relative;
		padding-left: 50%;
	}
	
	td:before { 
		/* Now like a table header */
		position: absolute;
		/* Top/left values mimic padding */
		/* top: 6px; */
		left: 6px;
		width: 45%; 
		padding-right: 10px; 
		white-space: nowrap;
		    margin-left: 20px;
	}
	
	/*
	Label the data
	*/
	td:nth-of-type(1):before { content: "ID"; }
	td:nth-of-type(2):before { content: "Name"; }
	td:nth-of-type(3):before { content: "Phone"; }
	td:nth-of-type(4):before { content: "Email"; }
	td:nth-of-type(5):before { content: "Status"; }
	td:nth-of-type(6):before { content: "Submitted"; }
	td:nth-of-type(7):before { content: "Modify"; }
}


@media screen and (max-width: 500px) {
	
td, th {padding: 7px 2px;}
	    .customAction {padding: 0 5px;}
.waves-effect {    font-size: .75em;}
    h4 {    font-size: 1.5em;}
    .modal {    width: 95%;}
	.row .col.nopad {padding: 0;}
		td { padding-left: 115px}
		td:before {width: 90px; margin-left:0; }
}

@media screen and (max-width: 400px) {
	.sorter {width: 100%; margin-top: 0px;     display: block;    clear: both;}
	.container .row {margin: 0;}
	.container {width: 100%;}
}
              
            
!

JS

              
                
$(document).ready(function() {
    $('select').material_select();
});


// angular js codes will be here
 var app = angular.module('myApp', ['angularUtils.directives.dirPagination']);
app.controller('volunteersCtrl', function($scope, $http) {
    // more angular JS codes will be here
      $scope.currencyVal;
    $scope.filters = { };
$scope.predicate = "unix";
$scope.reverse = true;
  
  $scope.fieldTable = [{
    field: "",
    title: "ALL"
  }, {
    field: "APPROVED",
    title: "APPROVED"
  }, {   
  	field: "APPLIED",
    title: "APPLIED"
  }, {
    field: "DISMISSED",
    title: "DISMISSED"
  },  {
    field: "WITHDRAWN",
    title: "WITHDRAWN"
  }, {
    field: "BANNED",
    title: "BANNED"
  }];

   $scope.selected = $scope.fieldTable[0];

  $scope.hasChanged = function() {
$scope.filters = $scope.selected.field;
  }

    
    
    $scope.showCreateForm = function(){
    // clear form
    $scope.clearForm();
        //Setting default
       $scope.status = "APPLIED";
     $scope.unix = Math.floor(Date.now());
    // change modal title
    $('#modal-volunteer-title').text("Create New Volunteer");
     
    // hide update volunteer button
    $('#btn-update-volunteer').hide();
     
    // show create volunteer button
    $('#btn-create-volunteer').show();
     
} // END SHOW CREATE FORM

// clear variable / form values
$scope.clearForm = function(){
    $scope.id = "";
    $scope.name = "";
    $scope.email = "";
    $scope.phone = "";
} // END CLEAR FORM

// create new volunteer 
$scope.createVolunteer = function(){
         
    // fields in key-value pairs
    $http.post('create_volunteer.php', {
            'name' : $scope.name, 
            'email' : $scope.email, 
            'phone' : $scope.phone,
            'status' : $scope.status,
            'unix' : $scope.unix
        }
    ).success(function (data, status, headers, config) {
        console.log(data);
        // tell the user new volunteer was created
        Materialize.toast(data, 4000);
         
        // close modal
        $('#modal-volunteer-form').closeModal();
         
        // clear modal content
        $scope.clearForm();
         
        // refresh the list
        $scope.getAll();
    });
} //END CREATE VOLUNTEER

// read volunteers
    
        $scope.names = [
    {
      "id": "1",
      "name": "Alexander Hamilton",
      "email": "ahamil@example.com",
      "phone": "5555555555",
      "status": "APPLIED",
      "created": "2016-07-07 09:16:32",
      "unix": "1467640600000"
    },
    {
      "id": "2",
      "name": "Jon Snow",
      "email": "jsnow@example.com",
      "phone": "1111111111",
      "status": "APPROVED",
      "created": "2016-05-02 13:12:50",
      "unix": "1467641611111"
    },
    {
      "id": "3",
      "name": "Peter Pan",
      "email": "ppan@example.com",
      "phone": "2222222222",
      "status": "DISMISSED",
      "created": "2016-05-31 14:55:32",
      "unix": "1467642676122"
    },
    {
      "id": "4",
      "name": "Mary Poppins",
      "email": "rickytikytaffy@example.com",
      "phone": "4444444444",
      "status": "WITHDRAWN",
      "created": "2016-05-05 22:44:01",
      "unix": "1467643699333"
    },
    {
      "id": "5",
      "name": "Peter Griffin",
      "email": "roadhouse@rosebud.com",
      "phone": "7777777777",
      "status": "BANNED",
      "created": "2016-06-21 16:33:11",
      "unix": "1467648699444"
    },
    {
      "id": "6",
      "name": "Anders Anderson",
      "email": "ders@example.com",
      "phone": "1112223333",
      "status": "APPLIED",
      "created": "2016-06-22 19:22:22",
      "unix": "1467648699555"
    },
    {
      "id": "7",
      "name": "Natalie Portman",
      "email": "nport@example.com",
      "phone": "1111111111",
      "status": "APPROVED",
      "created": "2016-03-02 12:11:33",
      "unix": "1467648699666"
    },
    {
      "id": "8",
      "name": "Meryl Streep",
      "email": "academy@example.com",
      "phone": "2222222222",
      "status": "DISMISSED",
      "created": "2016-04-17 11:12:44",
      "unix": "1467648699777"
    },
    {
      "id": "9",
      "name": "David Bowie",
      "email": "majortom@example.com",
      "phone": "4444444444",
      "status": "WITHDRAWN",
      "created": "2016-05-15 10:12:55",
      "unix": "1467648699888"
    },
    {
      "id": "10",
      "name": "Katniss Everdeen",
      "email": "prim4ever@example.com",
      "phone": "7777777777",
      "status": "DISMISSED",
      "created": "2016-02-15 08:17:46",
      "unix": "1467648699999"
    },
    {
      "id": "11",
      "name": "Robb Stark",
      "email": "robb@winterfell.com",
      "phone": "1113334444",
      "status": "WITHDRAWN",
      "created": "2016-02-15 08:17:46",
      "unix": "1467648699999"
    },
    {
      "id": "12",
      "name": "Jeff Guy",
      "email": "jeff@hotmail.com",
      "phone": "0123456789",
      "status": "APPROVED",
      "created": "2016-07-10 02:16:10",
      "unix": "1467648761111"
    },
    {
      "id": "13",
      "name": "Arya Stark",
      "email": "arry@kingslanding.com",
      "phone": "4445556666",
      "status": "DISMISSED",
      "created": "2016-02-15 08:17:46",
      "unix": "1467648699999"
    },
    {
      "id": "14",
      "name": "Cassandra Styles",
      "email": "cassie@gmail.com",
      "phone": "7779992222",
      "status": "WITHDRAWN",
      "created": "2016-07-10 20:30:46",
      "unix": "1467648762111"
    },
    {
      "id": "15",
      "name": "Stephanie Meyers",
      "email": "SmyersMQ@gmail.com",
      "phone": "4445556666",
      "status": "APPLIED",
      "created": "2016-07-10 20:35:36",
      "unix": "1467648763111"
    },
    {
      "id": "16",
      "name": "Oprah Winfrey",
      "email": "asdds@fgfg.com",
      "phone": "1234567890",
      "status": "APPLIED",
      "created": "2016-07-10 20:44:03",
      "unix": "1467648764111"
    },
    {
      "id": "17",
      "name": "Arin Hanson",
      "email": "egoraptor@gmail.com",
      "phone": "1234567890",
      "status": "DISMISSED",
      "created": "2016-07-10 20:47:06",
      "unix": "1467648765111"
    },
    {
      "id": "18",
      "name": "Dan Avidan",
      "email": "danny@sexbang.com",
      "phone": "2567001234",
      "status": "APPROVED",
      "created": "2016-07-10 20:50:30",
      "unix": "1467648766111"
    },
    {
      "id": "19",
      "name": "Eddard Stark",
      "email": "Ned@stark.com",
      "phone": "6667774444",
      "status": "BANNED",
      "created": "2016-07-10 20:52:58",
      "unix": "1468183939000"
    },
    {
      "id": "20",
      "name": "Theon Greyjoy",
      "email": "dicks@betray.com",
      "phone": "3334445555",
      "status": "BANNED",
      "created": "2016-07-10 22:57:17",
      "unix": "1468191384105"
    },
    {
      "id": "21",
      "name": "Stephen King",
      "email": "steve@shining.com",
      "phone": "2225551111",
      "status": "WITHDRAWN",
      "created": "2016-07-12 03:42:49",
      "unix": "1468294848273"
    },
    {
      "id": "22",
      "name": "Stephen Colbert",
      "email": "steve@america.com",
      "phone": "9996667777",
      "status": "WITHDRAWN",
      "created": "2016-07-12 03:42:50",
      "unix": "1468294848273"
    },
    {
      "id": "23",
      "name": "Thaddius Gibralter",
      "email": "thad@gmail.com",
      "phone": "2653144660",
      "status": "DISMISSED",
      "created": "2016-07-12 03:42:50",
      "unix": "1468294848275"
    },
    {
      "id": "24",
      "name": "Mei-Ling Zhou",
      "email": "mei@overwatch.com",
      "phone": "3006667777",
      "status": "DISMISSED",
      "created": "2016-07-12 03:42:50",
      "unix": "1468294848280"
    },
    {
      "id": "25",
      "name": "Angela Zeigler",
      "email": "mercy@overwatch.com",
      "phone": "8008675309",
      "status": "APPROVED",
      "created": "2016-07-12 17:32:16",
      "unix": "1468344699228"
    },
    {
      "id": "26",
      "name": "Lena Oxton",
      "email": "tracer@overwatch.com",
      "phone": "8003005000",
      "status": "BANNED",
      "created": "2016-07-12 03:42:50",
      "unix": "1468294848273"
    },
    {
      "id": "27",
      "name": "Hana Song",
      "email": "D.va@overwatch.com",
      "phone": "7001325468",
      "status": "APPROVED",
      "created": "2016-07-12 19:16:14",
      "unix": "1468350535504"
    },
    {
      "id": "28",
      "name": "Fareeha Amari",
      "email": "justicerains@overwatch.com",
      "phone": "3347008000",
      "status": "APPLIED",
      "created": "2016-07-12 21:57:18",
      "unix": "1468360549116"
    },
    {
      "id": "29",
      "name": "Ana Amari",
      "email": "sniper@support.net",
      "phone": "1112223333",
      "status": "APPROVED",
      "created": "2016-07-13 01:50:13",
      "unix": "1468374569046"
    }
  ]



 


// retrieve record to fill out the form
$scope.readOne = function(id){
     
    // change modal title
    $('#modal-volunteer-title').text("Edit Volunteer Record");
     
    // show udpate volunteer button
    $('#btn-update-volunteer').show();
     
    // show create volunteer button
    $('#btn-create-volunteer').hide();
     
    // post id of volunteer to be edited
    $http.post('read_one.php', {
        'id' : id 
    })
    .success(function(data, status, headers, config){
         
        // put the values in form
        $scope.id = data[0]["id"];
        $scope.name = data[0]["name"];
        $scope.email = data[0]["email"];
        $scope.phone = data[0]["phone"];
        $scope.status = data[0]["status"];
         
        // show modal
        $('#modal-volunteer-form').openModal();
    })
    .error(function(data, status, headers, config){
        Materialize.toast('Unable to retrieve record.', 4000);
    });
 } //	END READ ONE

// update volunteer record / save changes
$scope.updateVolunteer = function(){
    $http.post('update_volunteer.php', {
        'id' : $scope.id,
        'name' : $scope.name, 
        'email' : $scope.email, 
        'phone' : $scope.phone,
        'status' : $scope.status
    })
    .success(function (data, status, headers, config){             
        // tell the user volunteer record was updated
        Materialize.toast(data, 4000);
         
        // close modal
        $('#modal-volunteer-form').closeModal();
         
        // clear modal content
        $scope.clearForm();
         
        // refresh the volunteer list
        $scope.getAll();
    });
} //END UPDATE

// delete volunteer record
$scope.deleteVolunteer = function(index){
     
          if(confirm("Are you sure?")){
        $scope.names.splice(index, 1);
  Materialize.toast('Volunteer record deleted', 4000); }
} //END DELETE    

 
    
}); // END ANGULAR


app.directive('phoneInput', function($filter, $browser) {
    return {
        require: 'ngModel',
        link: function($scope, $element, $attrs, ngModelCtrl) {
            var listener = function() {
                var value = $element.val().replace(/[^0-9]/g, '');
                $element.val($filter('tel')(value, false));
            };

            // This runs when we update the text field
            ngModelCtrl.$parsers.push(function(viewValue) {
                return viewValue.replace(/[^0-9]/g, '').slice(0,10);
            });

            // This runs when the model gets updated on the scope directly and keeps our view in sync
            ngModelCtrl.$render = function() {
                $element.val($filter('tel')(ngModelCtrl.$viewValue, false));
            };

            $element.bind('change', listener);
            $element.bind('keydown', function(event) {
                var key = event.keyCode;
                // If the keys include the CTRL, SHIFT, ALT, or META keys, or the arrow keys, do nothing.
                // This lets us support copy and paste too
                if (key == 91 || (15 < key && key < 19) || (37 <= key && key <= 40)){
                    return;
                }
                $browser.defer(listener); // Have to do this or changes don't get picked up properly
            });

            $element.bind('paste cut', function() {
                $browser.defer(listener);
            });
        }

    };
});
app.filter('tel', function () {
    return function (tel) {
        console.log(tel);
        if (!tel) { return ''; }

        var value = tel.toString().trim().replace(/^\+/, '');

        if (value.match(/[^0-9]/)) {
            return tel;
        }

        var country, city, number;

        switch (value.length) {
            case 1:
            case 2:
            case 3:
                city = value;
                break;

            default:
                city = value.slice(0, 3);
                number = value.slice(3);
        }

        if(number){
            if(number.length>3){
                number = number.slice(0, 3) + '-' + number.slice(3,7);
            }
            else{
                number = number;
            }

            return ("(" + city + ") " + number).trim();
        }
        else{
            return "(" + city;
        }

    };
});
 
app.config(function(paginationTemplateProvider) {
    paginationTemplateProvider.setString('<ul class="pagination " ng-if="1 < pages.length || !autoHide">    <li class="nopad" ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == 1 }">        <a href="" ng-click="setCurrent(1)"><i class="material-icons">first_page</i></a>    </li>    <li class="nopad" ng-if="directionLinks" ng-class="{ disabled : pagination.current == 1 }">        <a href="" ng-click="setCurrent(pagination.current - 1)"><i class="material-icons">chevron_left</i></a>    </li>    <li ng-click="setCurrent(pageNumber)"class="waves-effect" ng-repeat="pageNumber in pages track by tracker(pageNumber, $index)" ng-class="{ active : pagination.current == pageNumber, disabled : pageNumber == \'...\' }">        <a href="" >{{ pageNumber }}</a>    </li>    <li class="nopad" ng-if="directionLinks" ng-class="{ disabled : pagination.current == pagination.last }"> <a href="" ng-click="setCurrent(pagination.current + 1)"><i class="material-icons">chevron_right</i></a>    </li> <li class="nopad" ng-if="boundaryLinks"  ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.last)"><i class="material-icons">last_page</i></a></li></ul>');


});
 
 
// jquery codes will be here
$(document).ready(function(){
    // initialize modal
    $('.modal-trigger').leanModal();
});


      /**
 * dirPagination - AngularJS module for paginating (almost) anything.
 *
 *
 * Credits
 * =======
 *
 * Daniel Tabuenca: https://groups.google.com/d/msg/angular/an9QpzqIYiM/r8v-3W1X5vcJ
 * for the idea on how to dynamically invoke the ng-repeat directive.
 *
 * I borrowed a couple of lines and a few attribute names from the AngularUI Bootstrap project:
 * https://github.com/angular-ui/bootstrap/blob/master/src/pagination/pagination.js
 *
 * Copyright 2014 Michael Bromley <michael@michaelbromley.co.uk>
 */

(function() {

    /**
     * Config
     */
    var moduleName = 'angularUtils.directives.dirPagination';
    var DEFAULT_ID = '__default';

    /**
     * Module
     */
    angular.module(moduleName, [])
        .directive('dirPaginate', ['$compile', '$parse', 'paginationService', dirPaginateDirective])
        .directive('dirPaginateNoCompile', noCompileDirective)
        .directive('dirPaginationControls', ['paginationService', 'paginationTemplate', dirPaginationControlsDirective])
        .filter('itemsPerPage', ['paginationService', itemsPerPageFilter])
        .service('paginationService', paginationService)
        .provider('paginationTemplate', paginationTemplateProvider)
        .run(['$templateCache',dirPaginationControlsTemplateInstaller]);

    function dirPaginateDirective($compile, $parse, paginationService) {

        return  {
            terminal: true,
            multiElement: true,
            priority: 100,
            compile: dirPaginationCompileFn
        };

        function dirPaginationCompileFn(tElement, tAttrs){

            var expression = tAttrs.dirPaginate;
            // regex taken directly from https://github.com/angular/angular.js/blob/v1.4.x/src/ng/directive/ngRepeat.js#L339
            var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);

            var filterPattern = /\|\s*itemsPerPage\s*:\s*(.*\(\s*\w*\)|([^\)]*?(?=\s+as\s+))|[^\)]*)/;
            if (match[2].match(filterPattern) === null) {
                throw 'pagination directive: the \'itemsPerPage\' filter must be set.';
            }
            var itemsPerPageFilterRemoved = match[2].replace(filterPattern, '');
            var collectionGetter = $parse(itemsPerPageFilterRemoved);

            addNoCompileAttributes(tElement);

            // If any value is specified for paginationId, we register the un-evaluated expression at this stage for the benefit of any
            // dir-pagination-controls directives that may be looking for this ID.
            var rawId = tAttrs.paginationId || DEFAULT_ID;
            paginationService.registerInstance(rawId);

            return function dirPaginationLinkFn(scope, element, attrs){

                // Now that we have access to the `scope` we can interpolate any expression given in the paginationId attribute and
                // potentially register a new ID if it evaluates to a different value than the rawId.
                var paginationId = $parse(attrs.paginationId)(scope) || attrs.paginationId || DEFAULT_ID;
                
                // (TODO: this seems sound, but I'm reverting as many bug reports followed it's introduction in 0.11.0.
                // Needs more investigation.)
                // In case rawId != paginationId we deregister using rawId for the sake of general cleanliness
                // before registering using paginationId
                // paginationService.deregisterInstance(rawId);
                paginationService.registerInstance(paginationId);

                var repeatExpression = getRepeatExpression(expression, paginationId);
                addNgRepeatToElement(element, attrs, repeatExpression);

                removeTemporaryAttributes(element);
                var compiled =  $compile(element);

                var currentPageGetter = makeCurrentPageGetterFn(scope, attrs, paginationId);
                paginationService.setCurrentPageParser(paginationId, currentPageGetter, scope);

                if (typeof attrs.totalItems !== 'undefined') {
                    paginationService.setAsyncModeTrue(paginationId);
                    scope.$watch(function() {
                        return $parse(attrs.totalItems)(scope);
                    }, function (result) {
                        if (0 <= result) {
                            paginationService.setCollectionLength(paginationId, result);
                        }
                    });
                } else {
                    paginationService.setAsyncModeFalse(paginationId);
                    scope.$watchCollection(function() {
                        return collectionGetter(scope);
                    }, function(collection) {
                        if (collection) {
                            var collectionLength = (collection instanceof Array) ? collection.length : Object.keys(collection).length;
                            paginationService.setCollectionLength(paginationId, collectionLength);
                        }
                    });
                }

                // Delegate to the link function returned by the new compilation of the ng-repeat
                compiled(scope);
                 
                // (TODO: Reverting this due to many bug reports in v 0.11.0. Needs investigation as the
                // principle is sound)
                // When the scope is destroyed, we make sure to remove the reference to it in paginationService
                // so that it can be properly garbage collected
                // scope.$on('$destroy', function destroyDirPagination() {
                //     paginationService.deregisterInstance(paginationId);
                // });
            };
        }

        /**
         * If a pagination id has been specified, we need to check that it is present as the second argument passed to
         * the itemsPerPage filter. If it is not there, we add it and return the modified expression.
         *
         * @param expression
         * @param paginationId
         * @returns {*}
         */
        function getRepeatExpression(expression, paginationId) {
            var repeatExpression,
                idDefinedInFilter = !!expression.match(/(\|\s*itemsPerPage\s*:[^|]*:[^|]*)/);

            if (paginationId !== DEFAULT_ID && !idDefinedInFilter) {
                repeatExpression = expression.replace(/(\|\s*itemsPerPage\s*:\s*[^|\s]*)/, "$1 : '" + paginationId + "'");
            } else {
                repeatExpression = expression;
            }

            return repeatExpression;
        }

        /**
         * Adds the ng-repeat directive to the element. In the case of multi-element (-start, -end) it adds the
         * appropriate multi-element ng-repeat to the first and last element in the range.
         * @param element
         * @param attrs
         * @param repeatExpression
         */
        function addNgRepeatToElement(element, attrs, repeatExpression) {
            if (element[0].hasAttribute('dir-paginate-start') || element[0].hasAttribute('data-dir-paginate-start')) {
                // using multiElement mode (dir-paginate-start, dir-paginate-end)
                attrs.$set('ngRepeatStart', repeatExpression);
                element.eq(element.length - 1).attr('ng-repeat-end', true);
            } else {
                attrs.$set('ngRepeat', repeatExpression);
            }
        }

        /**
         * Adds the dir-paginate-no-compile directive to each element in the tElement range.
         * @param tElement
         */
        function addNoCompileAttributes(tElement) {
            angular.forEach(tElement, function(el) {
                if (el.nodeType === 1) {
                    angular.element(el).attr('dir-paginate-no-compile', true);
                }
            });
        }

        /**
         * Removes the variations on dir-paginate (data-, -start, -end) and the dir-paginate-no-compile directives.
         * @param element
         */
        function removeTemporaryAttributes(element) {
            angular.forEach(element, function(el) {
                if (el.nodeType === 1) {
                    angular.element(el).removeAttr('dir-paginate-no-compile');
                }
            });
            element.eq(0).removeAttr('dir-paginate-start').removeAttr('dir-paginate').removeAttr('data-dir-paginate-start').removeAttr('data-dir-paginate');
            element.eq(element.length - 1).removeAttr('dir-paginate-end').removeAttr('data-dir-paginate-end');
        }

        /**
         * Creates a getter function for the current-page attribute, using the expression provided or a default value if
         * no current-page expression was specified.
         *
         * @param scope
         * @param attrs
         * @param paginationId
         * @returns {*}
         */
        function makeCurrentPageGetterFn(scope, attrs, paginationId) {
            var currentPageGetter;
            if (attrs.currentPage) {
                currentPageGetter = $parse(attrs.currentPage);
            } else {
                // If the current-page attribute was not set, we'll make our own.
                // Replace any non-alphanumeric characters which might confuse
                // the $parse service and give unexpected results.
                // See https://github.com/michaelbromley/angularUtils/issues/233
                var defaultCurrentPage = (paginationId + '__currentPage').replace(/\W/g, '_');
                scope[defaultCurrentPage] = 1;
                currentPageGetter = $parse(defaultCurrentPage);
            }
            return currentPageGetter;
        }
    }

    /**
     * This is a helper directive that allows correct compilation when in multi-element mode (ie dir-paginate-start, dir-paginate-end).
     * It is dynamically added to all elements in the dir-paginate compile function, and it prevents further compilation of
     * any inner directives. It is then removed in the link function, and all inner directives are then manually compiled.
     */
    function noCompileDirective() {
        return {
            priority: 5000,
            terminal: true
        };
    }

    function dirPaginationControlsTemplateInstaller($templateCache) {
        $templateCache.put('angularUtils.directives.dirPagination.template', '<ul class="pagination" ng-if="1 < pages.length || !autoHide"><li ng-if="boundaryLinks" ng-class="{ disabled : pagination.current == 1 }"><a href="" ng-click="setCurrent(1)">&laquo;</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == 1 }"><a href="" ng-click="setCurrent(pagination.current - 1)">&lsaquo;</a></li><li ng-repeat="pageNumber in pages track by tracker(pageNumber, $index)" ng-class="{ active : pagination.current == pageNumber, disabled : pageNumber == \'...\' || ( ! autoHide && pages.length === 1 ) }"><a href="" ng-click="setCurrent(pageNumber)">{{ pageNumber }}</a></li><li ng-if="directionLinks" ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.current + 1)">&rsaquo;</a></li><li ng-if="boundaryLinks"  ng-class="{ disabled : pagination.current == pagination.last }"><a href="" ng-click="setCurrent(pagination.last)">&raquo;</a></li></ul>');
    }

    function dirPaginationControlsDirective(paginationService, paginationTemplate) {

        var numberRegex = /^\d+$/;

        var DDO = {
            restrict: 'AE',
            scope: {
                maxSize: '=?',
                onPageChange: '&?',
                paginationId: '=?',
                autoHide: '=?'
            },
            link: dirPaginationControlsLinkFn
        };

        // We need to check the paginationTemplate service to see whether a template path or
        // string has been specified, and add the `template` or `templateUrl` property to
        // the DDO as appropriate. The order of priority to decide which template to use is
        // (highest priority first):
        // 1. paginationTemplate.getString()
        // 2. attrs.templateUrl
        // 3. paginationTemplate.getPath()
        var templateString = paginationTemplate.getString();
        if (templateString !== undefined) {
            DDO.template = templateString;
        } else {
            DDO.templateUrl = function(elem, attrs) {
                return attrs.templateUrl || paginationTemplate.getPath();
            };
        }
        return DDO;

        function dirPaginationControlsLinkFn(scope, element, attrs) {

            // rawId is the un-interpolated value of the pagination-id attribute. This is only important when the corresponding dir-paginate directive has
            // not yet been linked (e.g. if it is inside an ng-if block), and in that case it prevents this controls directive from assuming that there is
            // no corresponding dir-paginate directive and wrongly throwing an exception.
            var rawId = attrs.paginationId ||  DEFAULT_ID;
            var paginationId = scope.paginationId || attrs.paginationId ||  DEFAULT_ID;

            if (!paginationService.isRegistered(paginationId) && !paginationService.isRegistered(rawId)) {
                var idMessage = (paginationId !== DEFAULT_ID) ? ' (id: ' + paginationId + ') ' : ' ';
                if (window.console) {
                    console.warn('Pagination directive: the pagination controls' + idMessage + 'cannot be used without the corresponding pagination directive, which was not found at link time.');
                }
            }

            if (!scope.maxSize) { scope.maxSize = 9; }
            scope.autoHide = scope.autoHide === undefined ? true : scope.autoHide;
            scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : true;
            scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : false;

            var paginationRange = Math.max(scope.maxSize, 5);
            scope.pages = [];
            scope.pagination = {
                last: 1,
                current: 1
            };
            scope.range = {
                lower: 1,
                upper: 1,
                total: 1
            };

            scope.$watch('maxSize', function(val) {
                if (val) {
                    paginationRange = Math.max(scope.maxSize, 5);
                    generatePagination();
                }
            });

            scope.$watch(function() {
                if (paginationService.isRegistered(paginationId)) {
                    return (paginationService.getCollectionLength(paginationId) + 1) * paginationService.getItemsPerPage(paginationId);
                }
            }, function(length) {
                if (0 < length) {
                    generatePagination();
                }
            });

            scope.$watch(function() {
                if (paginationService.isRegistered(paginationId)) {
                    return (paginationService.getItemsPerPage(paginationId));
                }
            }, function(current, previous) {
                if (current != previous && typeof previous !== 'undefined') {
                    goToPage(scope.pagination.current);
                }
            });

            scope.$watch(function() {
                if (paginationService.isRegistered(paginationId)) {
                    return paginationService.getCurrentPage(paginationId);
                }
            }, function(currentPage, previousPage) {
                if (currentPage != previousPage) {
                    goToPage(currentPage);
                }
            });

            scope.setCurrent = function(num) {
                if (paginationService.isRegistered(paginationId) && isValidPageNumber(num)) {
                    num = parseInt(num, 10);
                    paginationService.setCurrentPage(paginationId, num);
                }
            };

            /**
             * Custom "track by" function which allows for duplicate "..." entries on long lists,
             * yet fixes the problem of wrongly-highlighted links which happens when using
             * "track by $index" - see https://github.com/michaelbromley/angularUtils/issues/153
             * @param id
             * @param index
             * @returns {string}
             */
            scope.tracker = function(id, index) {
                return id + '_' + index;
            };

            function goToPage(num) {
                if (paginationService.isRegistered(paginationId) && isValidPageNumber(num)) {
                    var oldPageNumber = scope.pagination.current;

                    scope.pages = generatePagesArray(num, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
                    scope.pagination.current = num;
                    updateRangeValues();

                    // if a callback has been set, then call it with the page number as the first argument
                    // and the previous page number as a second argument
                    if (scope.onPageChange) {
                        scope.onPageChange({
                            newPageNumber : num,
                            oldPageNumber : oldPageNumber
                        });
                    }
                }
            }

            function generatePagination() {
                if (paginationService.isRegistered(paginationId)) {
                    var page = parseInt(paginationService.getCurrentPage(paginationId)) || 1;
                    scope.pages = generatePagesArray(page, paginationService.getCollectionLength(paginationId), paginationService.getItemsPerPage(paginationId), paginationRange);
                    scope.pagination.current = page;
                    scope.pagination.last = scope.pages[scope.pages.length - 1];
                    if (scope.pagination.last < scope.pagination.current) {
                        scope.setCurrent(scope.pagination.last);
                    } else {
                        updateRangeValues();
                    }
                }
            }

            /**
             * This function updates the values (lower, upper, total) of the `scope.range` object, which can be used in the pagination
             * template to display the current page range, e.g. "showing 21 - 40 of 144 results";
             */
            function updateRangeValues() {
                if (paginationService.isRegistered(paginationId)) {
                    var currentPage = paginationService.getCurrentPage(paginationId),
                        itemsPerPage = paginationService.getItemsPerPage(paginationId),
                        totalItems = paginationService.getCollectionLength(paginationId);

                    scope.range.lower = (currentPage - 1) * itemsPerPage + 1;
                    scope.range.upper = Math.min(currentPage * itemsPerPage, totalItems);
                    scope.range.total = totalItems;
                }
            }
            function isValidPageNumber(num) {
                return (numberRegex.test(num) && (0 < num && num <= scope.pagination.last));
            }
        }

        /**
         * Generate an array of page numbers (or the '...' string) which is used in an ng-repeat to generate the
         * links used in pagination
         *
         * @param currentPage
         * @param rowsPerPage
         * @param paginationRange
         * @param collectionLength
         * @returns {Array}
         */
        function generatePagesArray(currentPage, collectionLength, rowsPerPage, paginationRange) {
            var pages = [];
            var totalPages = Math.ceil(collectionLength / rowsPerPage);
            var halfWay = Math.ceil(paginationRange / 2);
            var position;

            if (currentPage <= halfWay) {
                position = 'start';
            } else if (totalPages - halfWay < currentPage) {
                position = 'end';
            } else {
                position = 'middle';
            }

            var ellipsesNeeded = paginationRange < totalPages;
            var i = 1;
            while (i <= totalPages && i <= paginationRange) {
                var pageNumber = calculatePageNumber(i, currentPage, paginationRange, totalPages);

                var openingEllipsesNeeded = (i === 2 && (position === 'middle' || position === 'end'));
                var closingEllipsesNeeded = (i === paginationRange - 1 && (position === 'middle' || position === 'start'));
                if (ellipsesNeeded && (openingEllipsesNeeded || closingEllipsesNeeded)) {
                    pages.push('...');
                } else {
                    pages.push(pageNumber);
                }
                i ++;
            }
            return pages;
        }

        /**
         * Given the position in the sequence of pagination links [i], figure out what page number corresponds to that position.
         *
         * @param i
         * @param currentPage
         * @param paginationRange
         * @param totalPages
         * @returns {*}
         */
        function calculatePageNumber(i, currentPage, paginationRange, totalPages) {
            var halfWay = Math.ceil(paginationRange/2);
            if (i === paginationRange) {
                return totalPages;
            } else if (i === 1) {
                return i;
            } else if (paginationRange < totalPages) {
                if (totalPages - halfWay < currentPage) {
                    return totalPages - paginationRange + i;
                } else if (halfWay < currentPage) {
                    return currentPage - halfWay + i;
                } else {
                    return i;
                }
            } else {
                return i;
            }
        }
    }

    /**
     * This filter slices the collection into pages based on the current page number and number of items per page.
     * @param paginationService
     * @returns {Function}
     */
    function itemsPerPageFilter(paginationService) {

        return function(collection, itemsPerPage, paginationId) {
            if (typeof (paginationId) === 'undefined') {
                paginationId = DEFAULT_ID;
            }
            if (!paginationService.isRegistered(paginationId)) {
                throw 'pagination directive: the itemsPerPage id argument (id: ' + paginationId + ') does not match a registered pagination-id.';
            }
            var end;
            var start;
            if (angular.isObject(collection)) {
                itemsPerPage = parseInt(itemsPerPage) || 9999999999;
                if (paginationService.isAsyncMode(paginationId)) {
                    start = 0;
                } else {
                    start = (paginationService.getCurrentPage(paginationId) - 1) * itemsPerPage;
                }
                end = start + itemsPerPage;
                paginationService.setItemsPerPage(paginationId, itemsPerPage);

                if (collection instanceof Array) {
                    // the array just needs to be sliced
                    return collection.slice(start, end);
                } else {
                    // in the case of an object, we need to get an array of keys, slice that, then map back to
                    // the original object.
                    var slicedObject = {};
                    angular.forEach(keys(collection).slice(start, end), function(key) {
                        slicedObject[key] = collection[key];
                    });
                    return slicedObject;
                }
            } else {
                return collection;
            }
        };
    }

    /**
     * Shim for the Object.keys() method which does not exist in IE < 9
     * @param obj
     * @returns {Array}
     */
    function keys(obj) {
        if (!Object.keys) {
            var objKeys = [];
            for (var i in obj) {
                if (obj.hasOwnProperty(i)) {
                    objKeys.push(i);
                }
            }
            return objKeys;
        } else {
            return Object.keys(obj);
        }
    }

    /**
     * This service allows the various parts of the module to communicate and stay in sync.
     */
    function paginationService() {

        var instances = {};
        var lastRegisteredInstance;

        this.registerInstance = function(instanceId) {
            if (typeof instances[instanceId] === 'undefined') {
                instances[instanceId] = {
                    asyncMode: false
                };
                lastRegisteredInstance = instanceId;
            }
        };

        this.deregisterInstance = function(instanceId) {
            delete instances[instanceId];
        };
        
        this.isRegistered = function(instanceId) {
            return (typeof instances[instanceId] !== 'undefined');
        };

        this.getLastInstanceId = function() {
            return lastRegisteredInstance;
        };

        this.setCurrentPageParser = function(instanceId, val, scope) {
            instances[instanceId].currentPageParser = val;
            instances[instanceId].context = scope;
        };
        this.setCurrentPage = function(instanceId, val) {
            instances[instanceId].currentPageParser.assign(instances[instanceId].context, val);
        };
        this.getCurrentPage = function(instanceId) {
            var parser = instances[instanceId].currentPageParser;
            return parser ? parser(instances[instanceId].context) : 1;
        };

        this.setItemsPerPage = function(instanceId, val) {
            instances[instanceId].itemsPerPage = val;
        };
        this.getItemsPerPage = function(instanceId) {
            return instances[instanceId].itemsPerPage;
        };

        this.setCollectionLength = function(instanceId, val) {
            instances[instanceId].collectionLength = val;
        };
        this.getCollectionLength = function(instanceId) {
            return instances[instanceId].collectionLength;
        };

        this.setAsyncModeTrue = function(instanceId) {
            instances[instanceId].asyncMode = true;
        };

        this.setAsyncModeFalse = function(instanceId) {
            instances[instanceId].asyncMode = false;
        };

        this.isAsyncMode = function(instanceId) {
            return instances[instanceId].asyncMode;
        };
    }

    /**
     * This provider allows global configuration of the template path used by the dir-pagination-controls directive.
     */
    function paginationTemplateProvider() {

        var templatePath = 'angularUtils.directives.dirPagination.template';
        var templateString;

        /**
         * Set a templateUrl to be used by all instances of <dir-pagination-controls>
         * @param {String} path
         */
        this.setPath = function(path) {
            templatePath = path;
        };

        /**
         * Set a string of HTML to be used as a template by all instances
         * of <dir-pagination-controls>. If both a path *and* a string have been set,
         * the string takes precedence.
         * @param {String} str
         */
        this.setString = function(str) {
            templateString = str;
        };

        this.$get = function() {
            return {
                getPath: function() {
                    return templatePath;
                },
                getString: function() {
                    return templateString;
                }
            };
        };
    }
})();
              
            
!
999px

Console