<file_sep>controllers.controller('HailRequestController@request', [
    '$scope', '$state', '$stateParams', 'mapEngine', '$rootScope', 'Callback', 'User',
    'Geolocation', '$timeout', '$ionicPopup', 'HailRequest', 'Mode', 'Util', '$ionicLoading',
    '$ionicPopover', '$ionicHistory', 'Validator', '$ionicSideMenuDelegate', '$cordovaDialogs',
    function($scope, $state, $stateParams, mapEngine, $rootScope, Callback, User,
        Geolocation, $timeout, $ionicPopup, HailRequest, Mode, Util, $ionicLoading,
        $ionicPopover, $ionicHistory, Validator, $ionicSideMenuDelegate, $cordovaDialogs) {
        'use strict';

        var REQUEST_STATE = {
            INIT: 0,
            HAIL: 1,
            PICKUP: 2,
            DROPOFF: 3,
            MEETING_POINT: 3.5,
            CONFIRM_MEETING_POINT: 3.75,
            FARE: 4
        };
        $scope.REQUEST_STATE = REQUEST_STATE;


        var fareConfirm = null,
            commentConfirm = null,
            meetingPointConfirm = null,
            dragDealer = null,
            pickmenuBorder = angular.element(document.getElementById("pickmenuBorder")),
            navBtn = null;

        $scope.requestState = REQUEST_STATE.INIT;
        $scope.request = new HailRequest();
        Mode.FindAll(new Callback(function() {
            console.log(Mode.FindById(Mode.ID.TAXI));
            $scope.request.setMode(Mode.FindById(Mode.ID.TAXI));
        }));


        var updateView = function() {
            var navBack = function() {
                navBtn = angular.element(document.getElementById("navBtn"));
                navBtn.removeClass("ion-navicon");
                navBtn.addClass("ion-arrow-left-c");
            };

            var navMenu = function() {
                navBtn = angular.element(document.getElementById("navBtn"));
                navBtn.removeClass("ion-arrow-left-c");
                navBtn.addClass("ion-navicon");
            };

            if ($scope.requestState === REQUEST_STATE.INIT) {
                mapEngine.navigationInfoWindowLeftText("img/icons/info-hail.png");
                mapEngine.navigationInfoWindowRightText("HAIL");
                navMenu();
                dragDealer.enable();
            } else if ($scope.requestState === REQUEST_STATE.HAIL) {
                mapEngine.navigationInfoWindowRightText("SET PICKUP");
                mapEngine.navigationInfoWindowLeftText("img/icons/info-pickup.png");
                navBack();
                dragDealer.disable();
            } else if ($scope.requestState === REQUEST_STATE.PICKUP) {
                mapEngine.navigationInfoWindowRightText("CONFIRM PICKUP");
                mapEngine.navigationInfoWindowLeftText("img/icons/info-pickup.png");
                pickmenuBorder.css("height", "50%");
                navBack();
                dragDealer.disable();
            } else if ($scope.requestState === REQUEST_STATE.MEETING_POINT) {
                mapEngine.navigationInfoWindowRightText("MEETING POINT");
                mapEngine.navigationInfoWindowLeftText(null, $scope.request.nearestPoint.distance);
                
            } else if ($scope.requestState === REQUEST_STATE.CONFIRM_MEETING_POINT) {
                mapEngine.navigationInfoWindowRightText("CONFIRM");
                mapEngine.navigationInfoWindowLeftText("img/icons/info-dropoff.png");
            } else if ($scope.requestState === REQUEST_STATE.DROPOFF) {
                mapEngine.navigationInfoWindowRightText("CONFIRM DROPOFF");
                mapEngine.navigationInfoWindowLeftText("img/icons/info-dropoff.png");
                navBack();
                pickmenuBorder.css("height", "75%");
                dragDealer.disable();
            } else if ($scope.requestState === REQUEST_STATE.FARE) {
                mapEngine.navigationInfoWindowRightText("WAIT");
                navBack();
                dragDealer.disable();
            }

            $scope.$apply();
        };

        var rebuildSyncedRequest = function() {
            if ($stateParams.request !== null) {
                $scope.request = $stateParams.request;

                if ($scope.request.mode !== null) {

                    if ($scope.request.mode.id === Mode.ID.SERVISS)
                        dragDealer.setStep(1);
                    else if ($scope.request.mode.id === Mode.ID.SERVISS_PLUS)
                        dragDealer.setStep(2);
                    else if ($scope.request.mode.id === Mode.ID.TAXI)
                        dragDealer.setStep(3);
                } else {
                    $scope.request.setMode(Mode.FindById(Mode.ID.SERVISS));
                }

                if ($scope.request.pickupLocation !== null)
                    $scope.requestState = REQUEST_STATE.PICKUP;
                else if ($scope.request.dropoffLocation !== null)
                    $scope.requestState = REQUEST_STATE.DROPOFF;



                updateView();
            }
        };

        var resetRequest = function() {
            if ($scope.request.nearestPointWatch) {
                console.log("nearestPointWatch", $scope.request.nearestPointWatch);
                $scope.request.nearestPointWatch.stopWatching();
            }
            mapEngine.gMapsInstance.removePolylines();
            $scope.requestState = REQUEST_STATE.INIT;
            $scope.request = new HailRequest();
            $scope.request.setMode(Mode.FindById(Mode.ID.TAXI));
            dragDealer.setStep(3);
            mapEngine.getMap().setZoom(14);
            updateView();
        };

        $scope.onResetTapped = resetRequest;
        $scope.onNavTapped = function() {
            if ($scope.requestState === REQUEST_STATE.INIT)
                $ionicSideMenuDelegate.toggleLeft();
            else {
                resetRequest();
            }
        };

        var onNearbyCarsFound = new Callback(function(nearByCars) {
            if (!mapEngine.isReady) return;

            mapEngine.gMapsInstance.removeMarkers();
            for (var i = 0; i < nearByCars.length; i++) {
                var position = Geolocation.ParsePosition(nearByCars[i].car_location);
                mapEngine.addMarker(position.lat(), position.lng(), nearByCars[i].image);
            }
        });

        var initModeSelect = function() {
            $scope.step = 1;
            dragDealer = new Dragdealer('mode-select', {
                steps: 3,
                loose: true,
                tapping: true,
                callback: function(x, y) {
                    $scope.step = dragDealer.getStep()[0];
                    $scope.request.setMode(Mode.FromDragDealer($scope.step));
                    User.getInstance().findNearbyCars($scope.request.mode, onNearbyCarsFound);
                    $scope.$apply();
                }
            });
            dragDealer.reflow();
            dragDealer.setStep(3);

            Mode.FindAll(new Callback(function() {
                User.getInstance().findNearbyCars($scope.request.mode, onNearbyCarsFound);
            }));

        };

        var validateDropoffLocation = function(dropoffLocation) {
            if (dropoffLocation === null)
                $rootScope.onError.fire(new Error("You can't set a dropoff location more than 10 KM"));
        };

        var onPlaceChanged = function(place) {
            //if has no geomtry do nothing
            if (!place || !place.geometry)
                return;
            //navigate to this place
            if (place.geometry.viewport) {
                mapEngine.getMap().fitBounds(place.geometry.viewport);
            } else {
                mapEngine.getMap().setCenter(place.geometry.location);
            }
            mapEngine.getMap().setZoom(17);
        };

        $scope.onPickupLocationSelected = function(place) {

            if (!place.geometry) {
                $scope.request.pickupAddress = "";
            } else {
                onPlaceChanged(place);
                $scope.request.pickupLocation = place.geometry.location;
            }
            $scope.$apply();
        };

        $scope.onDropoffLocationSelected = function(place) {


            if (!place.geometry) {
                $scope.request.dropoffAddress = "";
            } else {
                onPlaceChanged(place);
                $scope.request.dropoffLocation = place.geometry.location;
                validateDropoffLocation($scope.request.dropoffLocation);
            }
            $scope.$apply();
        };

        var initModesPopovers = function() {
            $ionicPopover.fromTemplateUrl('mode.popover.html', {
                scope: $scope
            }).then(function(popover) {
                $scope.openPopover = function(event, step) {
                    if (!step) step = $scope.step;
                    popover.show(event);
                };

                $scope.$on('$destroy', function() {
                    popover.remove();
                });
            });


        };

        initModeSelect();
        initModesPopovers();

        $scope.onRequestPlusTapped = function() {
            if ($scope.request.passengers < $scope.request.mode.maxPassengers)
                $scope.request.passengers++;
        };

        $scope.onRequestMinusTapped = function() {
            if ($scope.request.passengers > 1)
                $scope.request.passengers--;
        };

        mapEngine.ready(function() {
            var service = new google.maps.places.PlacesService(mapEngine.getMap());
            var lebanon = new google.maps.LatLng(33.89, 35.51);
            service.textSearch({
                location: lebanon,
                query: "egnyt",
                radius: "50000"
            }, function(r, s) {
                console.log(r, s);
            });

            var onDestinationLocationChange = new Callback(function() {
                var g = new Geolocation();

                $rootScope.onProgress.fire();
                var locationLatLng = mapEngine.getCenter();
                g.latlngToAddress(locationLatLng, new Callback(function(address) {
                    console.log(address);
                    if (address.toUpperCase().indexOf("UNNAMED") > -1)
                        address = "No street name";

                    if ($scope.requestState === REQUEST_STATE.HAIL || $scope.requestState === REQUEST_STATE.PICKUP) { 
                        $scope.request.pickupLocation = locationLatLng;
                        $scope.request.pickupAddress = address;
                        console.log($scope.request.pickupAddress);
                    } else if ($scope.requestState === REQUEST_STATE.DROPOFF) { 
                        $scope.request.dropoffAddress = address;
                        $scope.request.dropoffLocation = locationLatLng;
                        validateDropoffLocation($scope.request.dropoffLocation);
                    }

                    $rootScope.onProgressDone.fire();
                    $scope.$apply();

                }), $rootScope.onError);
            });

            mapEngine.gMapsInstance.on("dragend", function() {
                if ($scope.requestState === REQUEST_STATE.PICKUP || $scope.requestState === REQUEST_STATE.DROPOFF)
                    onDestinationLocationChange.fire();
            });

            var onHailRequestPickedup = new Callback(function() {
                mapEngine.drawRoute({
                    origin: [$scope.request.pickupLocation.lat(), $scope.request.pickupLocation.lng()],
                    destination: [$scope.request.dropoffLocation.lat(), $scope.request.dropoffLocation.lng()],
                    travelMode: "driving",
                    strokeColor: "#7EBBFE",
                    strokeWeight: 7
                });
                $scope.requestState = REQUEST_STATE.FARE;
                updateView();

                $scope.request.estimateCost(new Callback(function(cost) {

                    $scope.confirm = {
                        title: 'FARE ESTIMATION',
                        message: Util.String("{0} USD for {1}", [cost, $scope.request.mode.name]),
                        buttons: {
                            isSubmit: false,
                            yes: "CONFIRM",
                            no: "CANCEL",
                            promotePositive: true
                        }
                    };

                    $scope.onNoTapped = function() {
                        fareConfirm.close();
                        resetRequest();
                    };
                    $scope.onYesTapped = function() {
                        fareConfirm.close();

                        $scope.onSubmitTapped = function(comment) {
                            $scope.request.comment = comment;
                            commentConfirm.close();
                            $ionicLoading.show({
                                template: 'Matching you with a Driver now!'
                            });
                            $scope.request.make(User.getInstance(), new Callback(function(driver) {
                                mapEngine.gMapsInstance.off("dragend");
                                $ionicLoading.hide();
                                $ionicHistory.clearCache();
                                $state.go("menu.requestconfirmed", {
                                    request: $scope.request
                                });

                            }), new Callback(function(e) {
                                $rootScope.onError.fire(e);
                                
                                resetRequest();
                            }));
                        };

                        $scope.confirm = {
                            message: "Send a Note About Your Location So Driver Can Find You Quicker",
                            buttons: {
                                isSubmit: true
                            },
                            input: {
                                data: $scope.request.comment,
                                placeholder: "I am next to beirut municipality entrance",
                                isAllowed: true
                            }
                        };

                        $timeout(function() {
                            commentConfirm = $ionicPopup.confirm({
                                templateUrl: "templates/confirm.popup.html",
                                cssClass: "eserviss-confirm text-center",
                                scope: $scope
                            });
                        }, 500);


                    };

                    fareConfirm = $ionicPopup.confirm({
                        templateUrl: "templates/confirm.popup.html",
                        cssClass: "eserviss-confirm text-center",
                        scope: $scope
                    });

                }), new Callback(function(e) {
                    $rootScope.onError.fire(e);
                    $scope.requestState = REQUEST_STATE.DROPOFF;
                    updateView();
                }));
            });

            var onLocationEnabled = new Callback(function() {

                $scope.myLocationTapped = function() {
                    User.getInstance().findPosition(new Callback(function(position) {
                        mapEngine.addUserAccuracy(position.lat(), position.lng(), position.accuracy);
                        mapEngine.setCenter(position.lat(), position.lng());
                    }));
                };

                var g = new Geolocation();
                User.getInstance().findPosition(new Callback(function(position) {
                    mapEngine.addUserAccuracy(position.lat(), position.lng(), position.accuracy);
                    mapEngine.setCenter(position.lat(), position.lng());
                }), $rootScope.onError);

                mapEngine.navigationMarker(function() {
                    
                });
                mapEngine.navigationInfo(function() {

                    if ($scope.requestState === REQUEST_STATE.INIT) {
                        $scope.request.inService(new Callback(function() {
                            onDestinationLocationChange.fire();
                            $scope.requestState = REQUEST_STATE.PICKUP; //due to ux will compress the hail step to pickup
                            updateView();
                        }), $rootScope.onError);
                    } else if ($scope.requestState === REQUEST_STATE.HAIL) { //deprecated state
                        $scope.request.inService(new Callback(function() {
                            onDestinationLocationChange.fire();
                            $scope.requestState = REQUEST_STATE.PICKUP;
                            updateView();
                        }), $rootScope.onError);
                    } else if ($scope.requestState === REQUEST_STATE.PICKUP) {
                        $scope.request.inService(new Callback(function() {

                            $scope.requestState = REQUEST_STATE.DROPOFF;
                            updateView();

                        }), $rootScope.onError);
                    } else if ($scope.requestState === REQUEST_STATE.DROPOFF) {

                        var validator = new Validator();
                        if (validator.isNull($scope.request.dropoffLocation, "Please enter dropoff location first")) {
                            $rootScope.onError.fire(validator.getError());
                            return;
                        }

                        $scope.request.inService(new Callback(function() {
                            if ($scope.request.mode.id === Mode.ID.SERVISS) {

                                $scope.request.validateServicePickup(new Callback(function(isNear) {

                                    if (isNear) {
                                        onHailRequestPickedup.fire();
                                        return;
                                    }

                                    $scope.confirm = {
                                        title: 'MEETING POINT',
                                        message: $scope.request.nearestPoint.message,
                                        buttons: {
                                            isSubmit: false,
                                            yes: "ACCEPT",
                                            no: "CHOOSE TAXI",
                                        }
                                    };

                                    $scope.onNoTapped = function() {
                                        meetingPointConfirm.close();
                                        resetRequest();
                                    };

                                    $scope.onYesTapped = function() {
                                        var nearestPointImg = angular.element(document.getElementById("meetingpointImg"));
                                        meetingPointConfirm.close();
                                        $scope.requestState = REQUEST_STATE.MEETING_POINT;
                                        updateView();
                                        $scope.request.nearestPoint.header.line[0] = " START WALKING TOWARDS MEETING POINT NOW";
                                        nearestPointImg.css("vertical-align", "middle");
                                        $scope.request.watchToMeetingPoint(new Callback(function(userPosition) {
                                            mapEngine.drawRoute({
                                                origin: [userPosition.lat(), userPosition.lng()],
                                                destination: [$scope.request.nearestPoint.location.lat(), $scope.request.nearestPoint.location.lng()],
                                                travelMode: "walking",
                                                strokeColor: "#7EBBFE",
                                                strokeWeight: 7
                                            });
                                            mapEngine.setCenter($scope.request.nearestPoint.location.lat(), $scope.request.nearestPoint.location.lng());
                                        }), new Callback(function() {
                                            $scope.request.nearestPoint.header.line[0] = "YOU HAVE REACHED YOUR MEETING POINT!";
                                            $scope.request.nearestPoint.header.line[1] = "PLEASE CONFIRM YOUR RIDE REQUEST";

                                            nearestPointImg.css("vertical-align", "top");
                                            $scope.requestState = REQUEST_STATE.CONFIRM_MEETING_POINT;
                                            updateView();
                                        }), new Callback(function() {
                                            $scope.request.nearestPoint.header.line[0] = "YOU ARE CLOSE TO MEETING POINT";
                                            nearestPointImg.css("vertical-align", "middle");
                                        }, 1), $rootScope.onError);
                                    };

                                    meetingPointConfirm = $ionicPopup.confirm({
                                        templateUrl: "templates/confirm.popup.html",
                                        cssClass: "eserviss-confirm text-center",
                                        scope: $scope
                                    });



                                }), $rootScope.onError);

                            } else {
                                onHailRequestPickedup.fire();
                            }

                        }), $rootScope.onError);
                    } else if ($scope.requestState === REQUEST_STATE.MEETING_POINT) {
                        $cordovaDialogs.alert("Eserviss is wathcing your position until you reach the meeting point", 'Meeting Point');
                    } else if ($scope.requestState === REQUEST_STATE.CONFIRM_MEETING_POINT) {
                        mapEngine.gMapsInstance.removePolylines();
                        $scope.request.pickupLocation = $scope.request.nearestPoint.location.toLatLng();
                        var g = new Geolocation();
                        g.latlngToAddress($scope.request.pickupLocation, new Callback(function(address) {
                            $scope.request.pickupAddress = address;
                            $scope.$apply();
                        }));
                        onHailRequestPickedup.fire();
                    }

                    $scope.$apply();
                });

                updateView();
                rebuildSyncedRequest();

            });

            $rootScope.ifLocationEnabled(onLocationEnabled);
        });

        $scope.$on('$destroy', function() {
            
        });

    }
]);

controllers.controller('HailRequestController@confirmed', [
    '$scope', '$state', '$stateParams', 'mapEngine', 'User', '$rootScope', 'Callback',
    'Util', 'Geolocation',
    function($scope, $state, $stateParams, mapEngine, User, $rootScope, Callback,
        Util, Geolocation) {
        'use strict';

        var confirmedScrollElem = angular.element(document.getElementById("confirmedScroll")),
            infoElem = angular.element(document.getElementById("info")),
            geolocation = new Geolocation();

        $scope.request = $stateParams.request;
        $scope.infoState = 1;

        

        $scope.onCloseTapped = function() {
            if ($scope.infoState === 1) { //if driver info shown
                confirmedScrollElem.removeClass('slideOutDown');
                confirmedScrollElem.removeClass('slideInDown');
                confirmedScrollElem.addClass('slideOutDown');
                infoElem.css("height", "auto");
                $scope.infoState = 0;
            } else if ($scope.infoState === 0) { //if driver info hidden
                confirmedScrollElem.removeClass('slideOutDown');
                confirmedScrollElem.removeClass('slideInDown');
                confirmedScrollElem.addClass('slideInDown');
                infoElem.css("height", "50%");
                $scope.infoState = 1;
            }
        };

        $scope.onContactTapped = function() {
            plugins.listpicker.showPicker({
                title: "Contact Driver",
                items: [{
                    text: "Send a Message",
                    value: "MESSAGE"
                }, {
                    text: "Call Driver",
                    value: "CALL"
                }]
            }, function(action) {
                if (action === 'MESSAGE') {
                    plugins.socialsharing.shareViaSMS({
                        message: ''
                    }, $scope.driver.DriverPhone, null, function() {});
                } else if (action === 'CALL') {
                    plugins.CallNumber.callNumber(function() {}, function(e) {
                        $rootScope.onError.fire(new Error(e, true, true));
                    }, $scope.driver.DriverPhone);
                }

            }, function() {});
        };

        $scope.onShareEtaTapped = function() {
            var POST_MESSAGE = Util.String("Get Eserviss app at http://eserviss.com/app");
            var POST_TITLE = "Eserviss";

            plugins.socialsharing.share(POST_MESSAGE, POST_TITLE);
        };

        var initSync = function() {
            $scope.request.sync(User.getInstance(), new Callback(function() {
                $scope.$apply();
            }), $rootScope.onError);

            $scope.request.onDroppedoff = new Callback(function() {
                geolocation.stopWatching();
                $state.go("menu.receipt", {
                    request: $scope.request
                });

            });
        };
        initSync();

        var onLocationEnabled = new Callback(function() {
            User.getInstance().findPosition(new Callback(function(position) {
                //mapEngine.addMarker(position.lat(), position.lng() - 0.005, "img/icons/pin-car.png");
                mapEngine.addUserAccuracy(position.lat(), position.lng(), position.accuracy
                    
                );
                mapEngine.setCenter(position.lat(), position.lng());
            }), $rootScope.onError);

            mapEngine.drawRoute({
                origin: [$scope.request.pickupLocation.lat(), $scope.request.pickupLocation.lng()],
                destination: [$scope.request.dropoffLocation.lat(), $scope.request.dropoffLocation.lng()],
                travelMode: "driving",
                strokeColor: "#7EBBFE",
                strokeWeight: 7
            });


            geolocation.watch(new Callback(function(position) {
                mapEngine.addUserAccuracy(position.lat(), position.lng(), position.accuracy);
                mapEngine.setCenter(position.lat(), position.lng());
            }));
        });

        mapEngine.ready(function() {
            $rootScope.ifLocationEnabled(onLocationEnabled);
        });

        $scope.$on('$destroy', function() {
            geolocation.stopWatching();
        });

    }
]);

controllers.controller('HailRequestController@receipt', [
    '$scope', '$state', '$stateParams', 'Validator', '$rootScope', 'Callback',
    '$ionicHistory', 'User', '$cordovaDialogs', '$timeout',
    function($scope, $state, $stateParams, Validator, $rootScope, Callback,
        $ionicHistory, User, $cordovaDialogs, $timeout) {
        'use strict';

        $ionicHistory.nextViewOptions({
            disableBack: true
        });

        var today = new Date();
        $scope.date = {
            day: today.getDate(),
            month: today.toDateString().split(' ')[1],
            year: today.getFullYear()
        };

        $scope.request = $stateParams.request;
        $scope.receipt = {};

        

        $scope.request.consumeCost(User.getInstance(), new Callback(function() {
            $cordovaDialogs.alert("Your trip cost has been charged from your balance", 'Trip Cost');
        }), $rootScope.onError);

        $scope.onSubmitTapped = function(receipt) {

            var validator = new Validator();
            if (validator.isEmpty(receipt.rating, "Please insert rating first in your feedback") ||
                validator.isEmpty(receipt.comment, "Please insert comment first in your feedback")) {
                $rootScope.onError.fire(validator.getError());
                return;
            }

            $scope.request.feedback(User.getInstance(), $scope.receipt.rating, $scope.receipt.comment, new Callback(function() {
                $ionicHistory.clearCache();
                $timeout(function() {
                    $state.go("menu.hailrequest", {}, {
                        reload: true
                    });
                    
                }, 50);
            }), $rootScope.onError);

        };


    }
]);<file_sep>controllers.factory('Util', [

    function() {
        'use strict';

        var Util = augment.defclass({

            constructor: function() {

            }
        });

        
        Util.Alert = function(object) {
            var jsonStringified = "";
            try {
                jsonStringified = JSON.stringify(object);
            } catch (e) {
                jsonStringified = object;
            }

            alert(jsonStringified);
        };

        Util.IsBrowser = function () {
            return typeof cordova === "undefined";
        };

        
        Util.ToTwoDigits = function(number) {
            var baseTwo = number;
            if (number < 10 && number > -10) {
                baseTwo = "0" + number;
            }

            return baseTwo;
        };

        
        Util.FormatMilliseconds = function(milliseconds) {
            var days, hours, minutes, seconds;
            seconds = Math.floor(milliseconds / 1000);
            minutes = Math.floor(seconds / 60);
            seconds = seconds % 60;
            hours = Math.floor(minutes / 60);
            minutes = minutes % 60;
            days = Math.floor(hours / 24);
            hours = hours % 24;

            return Util.ToTwoDigits(hours) + ":" + Util.ToTwoDigits(minutes) + ":" + Util.ToTwoDigits(seconds);

        };

        
        Util.String = function(str, args) {
            var regex = new RegExp("{-?[0-9]+}", "g");

            return str.replace(regex, function(item) {
                var intVal = parseInt(item.substring(1, item.length - 1));
                var replace;
                if (intVal >= 0) {
                    replace = args[intVal];
                } else if (intVal === -1) {
                    replace = "{";
                } else if (intVal === -2) {
                    replace = "}";
                } else {
                    replace = "";
                }
                return replace;
            });
        };

        
        Util.Find = function(list, isMatched) {

            for (var i = 0; i < list.length; i++) {
                if (isMatched(list[i]))
                    return list[i];
            }
            return null;
        };

        
        Util.IsString = function(value) {
            return typeof value === 'string';
        };

        
        Util.InArray = function(value, list) {
            if (list.indexOf(value) === -1)
                return false;

            return true;
        };

        
        Util.PushUnique = function(value, list, isMatched) {
            var element = Util.Find(list, isMatched);

            if (element != null) {
                return;
            }

            list.push(value);
        }

        
        Util.IsArrayLike = function(obj) {
            var NODE_TYPE_ELEMENT = 1;

            if (obj == null || Util.IsWindow(obj)) {
                return false;
            }

            var length = obj.length;

            if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
                return true;
            }

            return Util.IsString(obj) || Array.isArray(obj) || length === 0 ||
                typeof length === 'number' && length > 0 && (length - 1) in obj;
        }

        
        Util.IsFunction = function(value) {
            return typeof value === 'function';
        }

        
        Util.IsWindow = function(obj) {
            return obj && obj.window === obj;
        }

        
        Util.RandomNumber = function(min, max) {
            if (!min) min = 0;
            if (!max) max = 1000;
            return Math.floor(Math.random() * (max - min + 1)) + min;
        }


        
        Util.ForEach = function(obj, iterator, context) {
            var key, length;
            if (obj) {
                if (Util.IsFunction(obj)) {
                    for (key in obj) {
                        // Need to check if hasOwnProperty exists,
                        // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
                        if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
                            iterator.call(context, obj[key], key, obj);
                        }
                    }
                } else if (Array.isArray(obj) || Util.IsArrayLike(obj)) {
                    var isPrimitive = typeof obj !== 'object';
                    for (key = 0, length = obj.length; key < length; key++) {
                        if (isPrimitive || key in obj) {
                            iterator.call(context, obj[key], key, obj);
                        }
                    }
                } else if (obj.forEach && obj.forEach !== Util.ForEach) {
                    obj.forEach(iterator, context, obj);
                } else {
                    for (key in obj) {
                        if (obj.hasOwnProperty(key)) {
                            iterator.call(context, obj[key], key, obj);
                        }
                    }
                }
            }
            return obj;
        };

        
        Util.EndWith = function(str, suffix) {
            return str.indexOf(suffix, str.length - suffix.length) !== -1;
        }

        Util.Base64 = function(type, data) {
            return Util.String("data:image/{0};base64,{1}", [type, data]);
        };

        Util.RandomString = function(length) {
            var text = "";
            var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

            for (var i = 0; i < length; i++)
                text += possible.charAt(Math.floor(Math.random() * possible.length));

            return text;
        };

        return Util;


    }
]);<file_sep>application.factory('Promotion', [
    'Model', 'Http',
    function(Model, Http) {
        'use strict';

        var Promotion = augment(Model, function(parent) {
            
            this.constructor = function(row) {
            	this._fields = ["name", "description", "image", "cta", "link"];
            	this._tableName = "Promotion";
                this._modelType = Promotion;
                parent.constructor.call(this, row);
            };
        });

        Promotion.FindAll = function (user, onSuccess, onError) {
            
            var http = new Http();
            http.get({
                url: CONFIG.SERVER.URL,
                model: Promotion,
                params: {
                    promo: true,
                    userId: user.id
                },
                onSuccess: onSuccess,
                onFail: onError,
                onError: onError
            });
        };

        return Promotion;
    }
]);<file_sep>controllers.factory('ImageManager', [
    'Error', '$cordovaFileTransfer', 'Util', 'Http', 'Callback',
    function(Error, $cordovaFileTransfer, Util, Http, Callback) {
        'use strict';

        var ImageManager = augment.defclass({

            constructor: function() {
                this.cameraOptions = {};
                this.filePath = null;
                if (typeof(cordova) !== "undefined") {
                    this.cameraOptions = {
                        destinationType: Camera.DestinationType.FILE_URI,
                        quality: 60,
                        correctOrientation: true,
                        targetWidth: 720
                    };
                }

                
            },
            encoded: function() {

                this.cameraOptions.destinationType = Camera.DestinationType.DATA_URL;
                return this;
            },
            locationed: function() {

                this.cameraOptions.destinationType = Camera.DestinationType.FILE_URI;
                return this;
            },
            
            fromCamera: function(onSuccess, onError) {
                var self = this;

                this.cameraOptions.sourceType = Camera.PictureSourceType.CAMERA;
                navigator.camera.getPicture(function(r) {

                    if (self.cameraOptions.destinationType === Camera.DestinationType.FILE_URI)
                        self.filePath = r;

                    onSuccess.fire(r);
                }, function(e) {
                    if (onError) onError.fire(e, true, true);
                }, this.cameraOptions);
                return this;
            },
            
            fromGallery: function(onSuccess, onError) {
                var self = this;

                this.cameraOptions.sourceType = Camera.PictureSourceType.PHOTOLIBRARY;
                navigator.camera.getPicture(function(r) {

                    if (self.cameraOptions.destinationType === Camera.DestinationType.FILE_URI)
                        self.filePath = r;

                    onSuccess.fire(r);
                }, function(e) {
                    if (onError) onError.fire(e, true, true);
                }, this.cameraOptions);

                





                return this;
            },
            fileMetadata: function(onSuccess, onError) {
                var imageUrl = this.filePath;
                var defaultError = new Error("Error happened while getting file data");

                window.resolveLocalFileSystemURL(imageUrl, function(fileEntry) {
                    fileEntry.file(function(fileObj) {

                        fileObj.isImage = function() {
                            return fileObj.type.search("image") !== -1;
                        };

                        onSuccess.fire(fileObj);

                    }, function(err) {
                        $rootScope.onError.fire(defaultError);
                    });
                }, function(err) {
                    $rootScope.onError.fire(defaultError);
                });
            },
            
            upload: function(url, onSuccess, onError, others) {
                var self = this;
                if (this.filePath) {
                    if (!others.params) others.params = {};
                    
                    

                    if (ionic.Platform.isAndroid())
                        url = url.replace("https", "http"); // fix server ssl cert. issue
                    
                    $cordovaFileTransfer.upload(
                        url,
                        self.filePath, {
                            fileKey: others.key || "image",
                            fileName: self.filePath.substr(self.filePath.lastIndexOf('/') + 1),
                            params: others.params
                        }, true
                    )
                        .then(function(result) { //on API call success
                            var response = null;
                            try {
                                response = JSON.parse(result.response);
                            } catch (e) {
                                response = result.response;
                            }
                            onSuccess.fire(response);
                            

                        }, function(err) { // on error happens on API call
                            var e = null;
                            //check what error happens
                            switch (err.code) {

                                //if file (profile picture) is invalid
                                case FileTransferError.FILE_NOT_FOUND_ERR:
                                case FileTransferError.INVALID_URL_ERR:
                                    e = new Error("Invalid file, Please reselect profile picture");
                                    break;

                                    //if there is connection error
                                case FileTransferError.CONNECTION_ERR:
                                    e = new Error("Connection error, Please check your internet connection");
                                    break;

                                    //if user aborted profile picture upload
                                case FileTransferError.ABORT_ERR:
                                    e = new Error("User signup aborted");
                                    break;

                                    //if other error happened
                                default:
                                    e = new Error("Unknown error happened while connecting to server, please try again");
                                    break;
                            }

                            //handle error showing to user
                            if (onError) onError.fire(e);

                        }, function(progress) {});

                } else {
                    var http = new Http();
                    http.isLoading = false;
                    http.post({
                        url: url,
                        params: others.params,
                        onSuccess: onSuccess,
                        onFail: new Callback(function(e) {
                            onError.fire(e);
                        }),
                        onError: onError
                    });
                }

            },
            
            thumbnail: function(width, height, onSuccess, onError, others) {
                var options = {
                    uri: this.filePath,
                    folderName: others.folder || "Tmp",
                    quality: others.quality || 75,
                    width: width,
                    height: height
                };

                window.ImageResizer.resize(options,
                    function(image) {
                        onSuccess.fire(image);
                    }, function() {
                        onError.fire(new Error("Error happened while making image thumbnail"));
                    });
            }
        });

        return ImageManager;


    }
]);<file_sep>controllers.factory('Model', [
    '$ionicPlatform', 'DatabaseConnector', 'Util', '$timeout',
    function($ionicPlatform, DatabaseConnector, Util, $timeout) {
        'use strict';

        var Model = augment.defclass({
            constructor: function(row) {
                this.id = row && row.id ? row.id : null;

                if (this._tableName !== null) {
                    this._dbConnector = new DatabaseConnector(this._tableName);
                }

                Util.ForEach(row, function(value, key) {
                    if (Util.InArray(key, this._fields))
                        this[key] = value;
                }, this);

                this._bindMethods();

            },

            _bindMethods: function() {
                var self = this;
                this._modelType.Find = function() {
                    self._dbConnector.selectFirst({
                        model: self._modelType,
                        conditions: {
                            "id": self.id
                        },
                        onSuccess: onSuccess,
                        onFail: onError
                    });
                }
            },

            
            getPrimaryKey: function() {
                return this.id;
            },

            
            setPrimaryKey: function(id) {
                this.id = id;
            },

            
            delete: function(onSuccess, onFail) {
                this._dbConnector.deleteById(this.getPrimaryKey(), onSuccess, onFail);
                return this;
            },

            
            update: function(onSuccess, onFail) {
                this._dbConnector.updateById(this.getPrimaryKey(), this.toJson(), onSuccess, onFail);
                return this;
            },

            
            save: function(onSuccess, onFail) {

                if (this.id) {
                    this.update(onSuccess, onFail);
                } else {
                    this._dbConnector.insert(this._modelType, this.toJson(), onSuccess, onFail);
                }

                return this;
            },

            
            toString: function() {
                return this.id;
            },

            
            toJson: function() {
                var json = {
                    id: this.id
                };

                Util.ForEach(this, function(value, key) {
                    if (Util.InArray(key, this._fields))
                        json[key] = value;
                }, this);

                return json;
            },

            toArray: function() {
                var fields = [];

                Util.ForEach(this, function(value, key) {
                    if (Util.InArray(key, this._fields))
                        fields.push(value);
                }, this);

                return fields;
            },

            toList: function() {
                var json = {};

                Util.ForEach(this, function(value, key) {
                    if (Util.InArray(key, this._fields) && !Util.InArray(key, this._hidden)) 
                        json[key] = value;
                }, this);

                return json;
            }
        });

        Model.Find = function(model, params, order, onSuccess, onError) {
            var tmp = new model();
            var tableName = tmp._tableName;
            var dbConnector = new DatabaseConnector(tableName);

            if (Array.isArray(params)) {
                dbConnector.select({
                    model: model,
                    order: order,
                    conditions: params,
                    onSuccess: onSuccess,
                    onFail: onError
                });
            } else {
                dbConnector.selectFirst({
                    model: model,
                    order: order,
                    conditions: [
                        ["id", "=", params]
                    ],
                    onSuccess: onSuccess,
                    onFail: onError
                });
            }

        }

        return Model;
    }
]);<file_sep>application.factory('Mode', [
    'Model', 'Util', 'Http', 'Callback',
    function(Model, Util, Http, Callback) {
        'use strict';

        var Mode = augment(Model, function(parent) {
            
            this.constructor = function(row) {
                this._fields = ["mode", "name", "allowed_passengers", "maxPassengers", "minFare", "cancelationFee", "etaTime", "trip_icon", "hail_title", "type_id", "trip_icon", "noservice"];
                this._tableName = "Mode";
                this._modelType = Mode;
                parent.constructor.call(this, row);

                this.id = this.type_id;
                this.maxPassengers = this.allowed_passengers;
                this.icon = this.trip_icon;
                this.name = this.mode;


                

                console.log(this.icon);
            };

            this.getDragDealerStep = function() {
                for (var i = 0; i < Mode.All.length; i++) {
                    if (Mode.All[i].id == this.id)
                        return (i+1);
                };
                
            };

            this.isTaxi = function () {
                return this.id == Mode.ID.TAXI;
            };

            this.isService = function () {
                return this.id == Mode.ID.SERVISS;
            };


            this.isServicePlus = function () {
                return this.id == Mode.ID.SERVISS_PLUS;
            };

            this.isFree = function () {
                return this.id == Mode.ID.FREE;
            }


            this.eta = function (onSuccess, onError) {
                var self = this;

                var http = new Http();
                http.isLoading = false;
                http.get({
                    url: CONFIG.SERVER.URL,
                    params: {
                        eta_estimation: true,
                        tripTypeId: this.id
                    },
                    onSuccess: new Callback(function (r) {
                        if (self.isTaxi())
                            self.etaTime = r.taxi;
                        else if (self.isService())
                            self.etaTime = r.service;
                        else if (self.isServicePlus())
                            self.etaTime = r.serviceplus;

                        if (onSuccess) onSuccess.fire(r);
                    }),
                    onFail: onError,
                    onError: onError
                });
            }
        });

        Mode.ID = {
            TAXI: 1,
            SERVISS: 2,
            SERVISS_PLUS: 3,
            FREE: 4
        };

        
        
        Mode.All = [];
        Mode.FindAll = function (onSuccess, onError) {

            if (Mode.All.length > 0) {
                if (onSuccess) onSuccess.fire(Mode.All);
                return;
            }

            var http = new Http();
            http.isLoading = false;
            http.get({
                url: CONFIG.SERVER.URL,
                model: Mode,
                params: {
                    modes_fare: true
                },
                onSuccess: new Callback(function (modes) {
                    console.log(modes);
                    Mode.All = modes;
                    if (onSuccess) onSuccess.fire(Mode.All);
                }),
                onError: onError
            });
        };

        Mode.FindById = function(id) {
            if (Mode.All === null)
                return null;

            return Util.Find(Mode.All, function(mode) {
                return mode.id == id;
            });

        };

        Mode.FromDragDealer = function (stepId) {
            if (!Mode.All) return null;

            return Mode.All[stepId - 1];

            
        };

        return Mode;
    }
]);<file_sep>'use strict'

controllers.factory('Validator', [
    'Error', 'Util',
    function(Error, Util) {

        var Validator = augment.defclass({

            constructor: function() {
                this._error = new Error();
                this._blockMessage = '';
                this._isPassed = true;
            },

            
            _validate: function(isValid, errorMessage, inputId) {
                inputId = !inputId ? null : inputId;

                var isValidFlag = false;

                if (isValid())
                    isValidFlag = true;
                this._isPassed = this._isPassed && !isValidFlag;

                if (errorMessage != null && isValidFlag) {
                    this._error.show(errorMessage);
                    this._blockMessage = this._blockMessage.length === 0 ? errorMessage : this._blockMessage + ' and ' + errorMessage;
                }

                if (inputId !== null) {
                	var inputElem = angular.element(document.getElementById(inputId));
                    if (isValidFlag) {
                        inputElem.addClass('input-error');
                    } else {
                    	inputElem.removeClass('input-error');
                    }

                }


                return isValidFlag;
            },

            
            isEmpty: function(value, errorMessage, inputId) {
                return this._validate(function() {
                    return !value || value.length == 0;
                }, errorMessage, inputId);
            },

            
            isNull: function(value, errorMessage, inputId) {
                return this._validate(function() {
                    return value == null;
                }, errorMessage, inputId);
            },

            
            isNotEmail: function(value, errorMessage, inputId) {
                return this._validate(function() {
                    var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
                    return !re.test(value);
                }, errorMessage, inputId);
            },

            
            isNotNumber: function(value, errorMessage, inputId) {
                return this._validate(function() {
                    return !value || isNaN(value);
                }, errorMessage, inputId);
            },

            
            isIn: function(values, isMatched, errorMessage, inputId) {
                return this._validate(function() {
                    return Util.Find(values, isMatched) != null;
                }, errorMessage, inputId);
            },

            
            getError: function() {
                return this._error;
            },

            
            getErrorBlock: function() {
            	var error = new Error();
            	console.log(this._blockMessage);
                error.show(this._blockMessage);
                return error;
            },

            isPassed: function () {
            	return this._isPassed;
            }
        });

        return Validator;


    }
]);<file_sep>application.factory('SyncTrip', [
    'Model', 'Mode', 'Driver',
    function(Model, Mode, Driver) {
        'use strict';

        var SyncTrip = augment(Model, function(parent) {
            
            this.constructor = function(row) {
                this._fields = ["user_id", "car_id", "ride_id", "driver_id", "hail", "response", "arrived", "pickup", "dropoff", "cancel", "noservice", "pickup_address", "dropoff_address", "pickup_location", "dropoff_location", "passengers", "fare", "paid", "payment_method", "trip_mode", "cancel_by_driver"];
                this._tableName = "SyncTrip";
                this._modelType = SyncTrip;
                parent.constructor.call(this, row);
                this.passengers = parseInt(this.passengers);
                this.trip_mode = parseInt(this.trip_mode);

                this.passengers = this.passengers === 0 ? 1 : this.passengers;
                this.mode = this.trip_mode > 0 ? Mode.FindById(this.trip_mode) : null;
                this.driver = new Driver(row.driver);
                this.pickupLocation = null;
                this.dropoffLocation = null;

                var self = this;
                
                if (this.pickup_location.trim().length !== 0 && this.pickup_location.split(',').length === 2) {
                    this.pickupLocation = {
                        lat: function () {
                            return self.pickup_location.split(',')[0];
                        },
                        lng: function () {
                            return self.pickup_location.split(',')[1];
                        }
                    };
                }

                if (this.dropoff_location.trim().length !== 0 && this.dropoff_location.split(',').length === 2) {
                    this.dropoffLocation = {
                        lat: function () {
                            return self.dropoff_location.split(',')[0];
                        },
                        lng: function () {
                            return self.dropoff_location.split(',')[1];
                        }
                    };
                }
            };
        });

        return SyncTrip;
    }
]);<file_sep>'use strict'

controllers.factory('Callback', [
	'$interval',
    function($interval) {

        var Callback = augment.defclass({

            constructor: function(method, maxCalls) {
                this._method = method || null;
                this._maxCalls = maxCalls || Callback.Calls.MAX;
                this._callsCounter = 0;
            },

            
            fire: function(param1, param2, param3, param4, param5) {
                if (this._method && this.isCallable()) {
                    Callback.Fire(this._method, param1, param2, param3, param4, param5);
                    this._callsCounter++;
                }
                return this;

            },



            
            isFired: function() {
                return this._callsCounter > 0;
            },

            waitFiredOnce: function() {
                while (!this.isFired());
            },

            
            isCallable: function() {
                if (this._maxCalls == Callback.Calls.MAX)
                    return true;

                return this._callsCounter < this._maxCalls;
            },

            
            setExtras: function(extras) {
                this._extras = extras;
                return this;
            },

            
            getExtras: function() {
                return this._extras;
            }
        });

        
        Callback.Fire = function(method, param1, param2, param3, param4, param5) {

            if (method != null) {

                if (param5)
                    method(param1, param2, param3, param4, param5);
                else if (param4)
                    method(param1, param2, param3, param4);
                else if (param3)
                    method(param1, param2, param3);
                else if (param2)
                    method(param1, param2);
                else if (param1)
                    method(param1);
                else
                    method();

                
            }
        }

        Callback.WaitAll = function(callBacks, onSuccess) {
        	var callsNumber;

            var interval = $interval(function() {
            	callsNumber = 0;
                for (var i = 0; i < callBacks.length; i++) {
                	if ( callBacks[i].isFired() )
                		callsNumber++;
                }

                if (callsNumber == callBacks.length) {
                    $interval.cancel(interval);
                    onSuccess.fire();
                }

            }, 100);
        };

        Callback.Calls = {
            MAX: -1
        }

        return Callback;


    }
]);<file_sep>application.factory('Settings', [
    'Model', 'Http', 'Callback',
    function(Model, Http, Callback) {
        'use strict';

        var Settings = augment(Model, function(parent) {
            
            this.constructor = function(row) {
                this._fields = ["provider_timeout", "server_rate", "server_mode", "server_email", "e_number", "e_email", "nearby_thread", "nearby_pin", "nearby_rate", "drag_thread"];
                this._tableName = "Settings";
                this._modelType = Settings;
                parent.constructor.call(this, row);
            }
        });

        Settings.SharedInstance = null;

        Settings.getInstance = function() {
            if (Settings.SharedInstance === null)
                Settings.SharedInstance = new Settings();

            return Settings.SharedInstance;
        }

        Settings.Download = function(onSuccess) {

            var onSettingsNotFound = new Callback(function () {
                Settings.SharedInstance = new Settings({server_rate: 10000});
                onSuccess.fire(Settings.SharedInstance);
            });

            var http = new Http();
            
            http.get({
                url: CONFIG.SERVER.URL,
                model: Settings,
                params: {
                    start: true
                },
                onSuccess: new Callback(function(settings) {
                    Settings.SharedInstance = settings;
                    onSuccess.fire(settings);
                }),
                onFail: onSettingsNotFound,
                onError: onSettingsNotFound
            });
        };

        return Settings;
    }
]);<file_sep>application.factory('HailRequest', [
    'Model', 'Http', 'Error', 'Callback', 'Settings',
    'Driver', 'Util', 'SyncTrip', 'Mode', '$timeout',
    'Geolocation',
    function(Model, Http, Error, Callback, Settings,
        Driver, Util, SyncTrip, Mode, $timeout,
        Geolocation) {
        'use strict';

        var HailRequest = augment(Model, function(parent) {
            
            this.constructor = function(row) {
                this._fields = ["ride_id", "hail", "response", "arrived", "pickup", "dropoff", "cancel", "noservice", "pickup_address", "dropoff_address", "pickup_location", "dropoff_location", "passengers", "fare", "paid", "payment_method", "trip_mode", "driver", "driver_dropoff_address", "driver_dropoff_location"];
                this._tableName = "HailRequest";
                this._modelType = HailRequest;
                parent.constructor.call(this, row);
                this.pickupLocation = this.pickup_location ? Geolocation.ParsePosition(this.pickup_location) : null;
                this.pickupAddress = this.pickup_address ? this.pickup_address : null;
                this.dropoffAddress = this.dropoff_address ? this.dropoff_address : null;
                this.dropoffLocation = this.dropoff_location ? Geolocation.ParsePosition(this.dropoff_location) : null;
                this.mode = this.trip_mode ? Mode.FindById(parseInt(this.trip_mode)) : null;
                this.passengers = this.passengers ? this.passengers : 1;
                this.pingInterval = null;
                this.rideId = this.ride_id ? this.ride_id : null;
                this.comment = null;
                this.driver = this.driver ? new Driver(this.driver) : new Driver(); //assigned driver
                this.pingActions = [];
                this.status = null;
                this.onDroppedoff = null;
                this.totalCost = 0;
                this.driverTimeout = null;
                this.isPingable = true;
                this.isSyncable = true;
                this.nearestPoint = {};
                this.nearestPointWatch = null;
                this.stage = null;
                this.onEtaArrival = null;
                this.etaArriaval = null;
                this.pingActionIndex = null;
            };

            this.getDropoffLocation = function() {
                return Geolocation.ParsePosition("30.2,31.4");

                var dropoffLocation = null;


                if (this.dropoff_location.length <= 1 || this.driver_dropoff_location.length <= 1)
                    return null;

                dropoffLocation = this.dropoff_location.length > 1 ? Geolocation.ParsePosition(this.dropoff_location) : null;

                if (!dropoffLocation)
                    dropoffLocation = this.driver_dropoff_location.length > 1 ? Geolocation.ParsePosition(this.driver_dropoff_location) : null;

                return dropoffLocation;
            };

            this.getDropoffAddress = function() {

                if (!this.dropoff_address || !this.driver_dropoff_address)
                    return null;

                if (this.dropoff_address.toUpperCase().search("NOT SET") !== -1)
                    return this.dropoff_address;

                if (this.driver_dropoff_address.toUpperCase().search("NOT SET") !== -1)
                    return this.driver_dropoff_address;

                return null;
            };

            this.estimateCost = function(onSuccess, onError) {
                var self = this;

                var makeEstimateRequest = function() {
                    var http = new Http();
                    http.get({
                        url: CONFIG.SERVER.URL,
                        params: {
                            fare: true,
                            pickupLat: self.pickupLocation.lat(),
                            pickupLng: self.pickupLocation.lng(),
                            dropoffLat: self.dropoffLocation.lat(),
                            dropoffLng: self.dropoffLocation.lng(),
                            modeId: self.mode.id
                        },
                        onSuccess: new Callback(function(cost) {
                            self.totalCost = cost * self.passengers;
                            if (onSuccess) onSuccess.fire(self.totalCost);
                        }),
                        onFail: onError,
                        onError: onError
                    });
                };

                if (this.mode.id === Mode.ID.SERVISS) {
                    makeEstimateRequest();
                } else {
                    var http = new Http();
                    http.isLoading = false;
                    http.get({
                        url: CONFIG.SERVER.URL,
                        params: {
                            trip_validate: true,
                            trip_type: this.mode.id,
                            lat1: this.pickupLocation.lat(),
                            long1: this.pickupLocation.lng(),
                            lat2: this.dropoffLocation.lat(),
                            long2: this.dropoffLocation.lng(),
                            address1: this.pickupAddress,
                            address2: this.dropoffAddress
                        },
                        onSuccess: new Callback(function() {
                            makeEstimateRequest();
                        }),
                        onFail: onError,
                        onError: onError
                    });
                }


            };

            this.consumeCost = function(user, onSuccess, onError) {
                var self = this;
                //user.subtractCredit(this.totalCost, onSuccess, onError);
                var http = new Http();
                http.get({
                    url: CONFIG.SERVER.URL,
                    params: {
                        ride_payment: true,
                        userId: user.id,
                        rideId: this.rideId
                    },
                    onSuccess: new Callback(function(m, s, r, data) {
                        self.totalCost = data.fee;
                        onSuccess.fire();
                    }),
                    onFail: new Callback(function(e, s, r, data) {
                        self.totalCost = data.fee;
                        onError.fire(e);
                    }),
                    onError: onError
                });
            };

            this.ping = function(user, onSuccess, onError, onFail) {
                if (this.pingActionIndex) {
                    this.isPingable = true;
                    return;
                }

                var self = this;

                var http = new Http();
                http.isLoading = false;

                var sendPingData = function() {

                    if (!self.isPingable)
                        return;

                    http.get({
                        url: CONFIG.SERVER.URL,
                        model: Driver,
                        params: {
                            ping: true,
                            userId: user.id
                        },
                        onSuccess: new Callback(function(driver) {
                            self.isPingable = false;

                            if (self.driverTimeout) $timeout.cancel(self.driverTimeout);

                            self.driver = driver;
                            onSuccess.fire(driver);
                        }),
                        onFail: new Callback(function(e) {
                            self.isPingable = false;
                            onError.fire(e);

                            if (onFail) onFail.fire(e);
                        }),
                        onError: onError
                    });
                };

                var pingIndex = user.pingActions.push(sendPingData);
                this.pingActionIndex = pingIndex - 1;

                

            };

            this.sync = function(user, onSuccess, onError) {
                var self = this;
                self.isSyncable = true;

                var http = new Http();
                http.isLoading = false;

                var makeSync = function() {
                    if (!self.isSyncable)
                        return;

                    http.get({
                        url: CONFIG.SERVER.URL,
                        model: SyncTrip,
                        params: {
                            sync: true,
                            userId: user.id
                        },
                        onSuccess: new Callback(function(sync) {
                            
                            if (!sync) return;
                            if (sync.cancel_by_driver.trim().length !== 0) {
                                self.status = "RIDE IS CANCELED";
                                self.isSyncable = false;
                                self.stage = HailRequest.STAGE.CANCEL;
                                if (self.onDriverCanceled) self.onDriverCanceled.fire();
                            } else if (sync.cancel.trim().length !== 0) {
                                self.status = "RIDE IS CANCELED";
                                self.isSyncable = false;
                                self.stage = HailRequest.STAGE.CANCEL;
                            } else if (sync.dropoff.trim().length !== 0) {
                                self.status = "THANKS FOR USING ESERVISS";
                                self.isSyncable = false;
                                self.stage = HailRequest.STAGE.DROPOFF;
                                self.fare = sync.fare;
                                console.log(sync, sync.fare);
                                if (self.onDroppedoff) self.onDroppedoff.fire();
                            } else if (sync.pickup.trim().length !== 0) {
                                self.stage = HailRequest.STAGE.PICKUP;
                                self.status = Util.String("YOU HAVE BEEN PICKED UP", [sync.mode.name.toUpperCase()]);
                            } else if (sync.arrived.trim().length !== 0) {
                                self.stage = HailRequest.STAGE.ARRIVED;
                                self.status = Util.String("YOUR RIDE HAS ARRIVED", [sync.mode.name.toUpperCase()]);
                            } else if (sync.response.trim().length !== 0) {
                                self.stage = HailRequest.STAGE.RESPONSE;
                                self.status = Util.String("DRIVER IS COMING", [sync.mode.name.toUpperCase()]);
                            }

                            onSuccess.fire(sync);
                        }),
                        onFail: null,
                        onError: onError
                    });

                    var makeEtaArrivalRequest = function() {
                        if (user.position && user.position.lat() && user.position.lng()) {
                            http.get({
                                url: CONFIG.SERVER.URL,
                                params: {
                                    eta_arrival: true,
                                    car_id: self.driver.CarId,
                                    lat: user.position.lat(),
                                    "long": user.position.lng(),
                                },
                                onSuccess: new Callback(function(arrival) {
                                    self.etaArrival = arrival;
                                    if (self.onEtaArrival) self.onEtaArrival.fire(arrival);
                                }),
                                onFail: null,
                                onError: null
                            });
                        }
                    };

                    makeEtaArrivalRequest();

                    


                };
                makeSync();
                user.pingActions.push(makeSync);
            };

            this.findFare = function(user, onSuccess, onError) {
                var self = this;
                var MAX_REQUESTS = 5;
                var requestsCounter = 0;
 
                var findFareRequest = function() {
                    requestsCounter++;
                    var http = new Http();
                    http.isLoading = false;
                    http.get({
                        url: CONFIG.SERVER.URL,
                        model: SyncTrip,
                        params: {
                            sync: true,
                            userId: user.id
                        },
                        onSuccess: new Callback(function(sync) {
                            if (sync.fare.trim().length === 0 && requestsCounter < MAX_REQUESTS) {
                                $timeout(findFareRequest, 1000);
                                return;
                            } else if (requestsCounter >= MAX_REQUESTS) {
                                onError.fire(new Error("Can't get fare now"));
                                return;
                            }

                            self.fare = parseFloat(sync.fare);
                            onSuccess.fire();
                        }),
                        onError: onError,
                        onFail: onError
                    });
                };

                $timeout(findFareRequest, 1000);


            }

            this.make = function(user, onSuccess, onError) {
                var self = this;

                var isLocked = localStorage.getItem(HailRequest.HAIL_LOCK);
                if (isLocked) {
                    onError.fire(new Error("Please close eserviss and reopen it again."));
                    return;
                }

                var requestDriverTimeout = function(settings) {
                    var http = new Http();
                    http.isLoading = false;
                    http.get({
                        url: CONFIG.SERVER.URL,
                        model: Settings,
                        params: {
                            driver_timeout: true,
                            rideID: self.rideId,
                            userID: user.id
                        },
                        onSuccess: new Callback(function(settings) {
                            $timeout(function() {
                                requestDriverTimeout(settings);
                            }, settings.provider_timeout * 1000);
                        }),
                        onFail: new Callback(function(e) {
                            self.isPingable = false;
                            onError.fire(e);
                        }),
                        onError: onError
                    });
                };

                var initDriverTimeout = function() {
                    var http = new Http();
                    http.isLoading = false;
                    http.get({
                        url: CONFIG.SERVER.URL,
                        model: Settings,
                        params: {
                            start: true
                        },
                        onSuccess: new Callback(function(settings) {
                            $timeout(function() {
                                requestDriverTimeout(settings);
                            }, settings.provider_timeout * 1000);
                        }),
                        onError: onError
                    });
                };

                var find = function() {
                    var http = new Http();
                    http.timeout = null;
                    http.isLoading = false;
                    http.get({
                        url: CONFIG.SERVER.URL,
                        params: {
                            find: true,
                            userId: user.id,
                            currentLat: self.pickupLocation.lat(),
                            currentLng: self.pickupLocation.lng(),
                            destinationLat: self.dropoffLocation ? self.dropoffLocation.lat() : null,
                            destinationLng: self.dropoffLocation ? self.dropoffLocation.lng() : null,
                            tripType: self.mode.id,
                            passengers: self.passengers
                        },
                        onSuccess: new Callback(function() {
                            self.isPingable = true;
                            self.ping(user, onSuccess, onError, new Callback(function() {
                                localStorage.removeItem(HailRequest.HAIL_LOCK);
                            }));

                            // initDriverTimeout();
                        }),
                        onFail: null
                            
                            ,
                        onError: null 
                    });
                };

                var makeUserRequest = function() {
                    var http = new Http();
                    http.isLoading = false;
                    http.get({
                        url: CONFIG.SERVER.URL,
                        params: {
                            user: true,
                            user_id: user.id,
                            trip_type: self.mode.id,
                            passengers: self.passengers,
                            lat1: self.pickupLocation.lat(),
                            long1: self.pickupLocation.lng(),
                            lat2: self.dropoffLocation ? self.dropoffLocation.lat() : null,
                            long2: self.dropoffLocation ? self.dropoffLocation.lng() : null,
                            address1: self.pickupAddress,
                            address2: self.dropoffAddress ? self.dropoffAddress : null,
                            comment: self.comment
                        },
                        onSuccess: new Callback(function(rideId) {
                            
                            self.rideId = rideId;
                            localStorage.setItem(HailRequest.HAIL_LOCK, rideId);
                            find();
                        }),
                        onFail: onError,
                        onError: onError
                    });
                };

                makeUserRequest();
            };

            this.feedback = function(user, rating, comment, onSuccess, onError) {
                var self = this;

                var http = new Http();
                http.get({
                    url: CONFIG.SERVER.URL,
                    params: {
                        'insert-feedback': true,
                        rideId: this.rideId,
                        userId: user.id,
                        driverId: this.driver.id,
                        comment: comment,
                        rate: rating
                    },
                    onSuccess: onSuccess,
                    onFail: onError,
                    onError: onError
                });
            };

            this.cancelRide = function(reason, onSuccess, onError) {
                var self = this;

                var http = new Http();
                http.get({
                    url: CONFIG.SERVER.URL,
                    params: {
                        'cancel': true,
                        rideId: this.rideId,
                        comment: reason
                    },
                    onSuccess: onSuccess,
                    onFail: onError,
                    onError: onError
                });
            };

            this.setMode = function(mode) {

                if (mode === null)
                    return;

                this.mode = mode;
                if (this.passengers > mode.maxPassengers)
                    this.passengers = mode.maxPassengers;
            };

            this.validateServicePickup = function(onSuccess, onError) {
                var self = this;
                if (!this.mode || this.mode.id !== Mode.ID.SERVISS) {
                    onSuccess.fire(true);
                    return;
                }

                var http = new Http();
                http.get({
                    url: CONFIG.SERVER.URL,
                    params: {
                        'validate-service-pickup': true,
                        pickupLat: this.pickupLocation.lat(),
                        pickupLng: this.pickupLocation.lng()
                    },
                    onSuccess: new Callback(function(r) {
                        


                        self.nearestPoint.location = Geolocation.ParsePosition(r.location);
                        self.nearestPoint.distance = r.distance;
                        self.nearestPoint.message = r.safe ? r.safe : "Meeting point with service is near";
                        self.nearestPoint.header = {
                            line: []
                        };
                        onSuccess.fire(false);
                    }),
                    onFail: onError,
                    onError: onError
                });
            };

            this.watchToMeetingPoint = function(onProgress, onMinDistance, onNearDistance, onError) {
                var self = this;

                if (this.nearestPointWatch) this.nearestPointWatch.stopWatching();
                var MIN_DISTANCE = 10;
                var NEAR_DISTANCE = 100;

                

                this.nearestPointWatch = new Geolocation();
                this.nearestPointWatch.watch(new Callback(function(position) {
                    onProgress.fire(position);

                    var minDistance = MIN_DISTANCE + position.accuracy;
                    minDistance = minDistance > 50 ? 50 : minDistance;

                    var nearDistance = NEAR_DISTANCE + position.accuracy;
                    nearDistance = nearDistance > 150 ? 150 : nearDistance;


                    var distance = position.calculateDistance(self.nearestPoint.location);

                    if (distance <= nearDistance) {
                        console.log("nearDistance", nearDistance);
                        onNearDistance.fire();
                    }

                    if (distance <= minDistance) {
                        self.nearestPointWatch.stopWatching();
                        onMinDistance.fire();
                    }

                    


                }), onError);
            };

            this.inService = function(onSuccess, onError) {
                var self = this;
                
                var http = new Http();
                
                http.get({
                    url: CONFIG.SERVER.URL,
                    params: {
                        available_cars: true,
                        trip_type: this.mode.id
                    },
                    onSuccess: onSuccess,
                    onFail: new Callback(function(e) {
                        if (self.mode.noservice && self.mode.noservice.length > 0) onError.fire(new Error(self.mode.noservice));
                        else onError.fire(e);
                    }),
                    onError: onError
                });
            };


        });

        

        HailRequest.EstimateCost = function(modeId, pickupLocation, dropoffLocation, onSuccess, onError) {
            var http = new Http();
            http.get({
                url: CONFIG.SERVER.URL,
                params: {
                    fare: true,
                    pickupLat: pickupLocation.lat(),
                    pickupLng: pickupLocation.lng(),
                    dropoffLat: dropoffLocation ? dropoffLocation.lat() : null,
                    dropoffLng: dropoffLocation ? dropoffLocation.lng() : null,
                    modeId: modeId
                },
                onSuccess: onSuccess,
                onFail: onError,
                onError: onError
            });
        };

        HailRequest.HAIL_LOCK = "ESERVISS.HAIL_LOCK";
        HailRequest.STAGE = {
            HAIL: "HAIL",
            RESPONSE: "RESPONSE",
            ARRIVED: "ARRIVED",
            PICKUP: "PICKUP",
            DROPOFF: "DROPOFF",
            CANCEL: "CANCEL",
            NO_SERVICE: "NO_SERVICE"
        };

        return HailRequest;
    }
]);
<file_sep>controllers.controller('HailRequestController@request', [
    '$scope', '$state', '$stateParams', 'mapEngine', '$rootScope', 'Callback', 'User',
    'Geolocation', '$timeout', '$ionicPopup', 'HailRequest', 'Mode', 'Util', '$ionicLoading',
    '$ionicPopover', '$ionicHistory', 'Validator', '$ionicSideMenuDelegate', '$cordovaDialogs',
    'Http', 'Settings',
    function($scope, $state, $stateParams, mapEngine, $rootScope, Callback, User,
        Geolocation, $timeout, $ionicPopup, HailRequest, Mode, Util, $ionicLoading,
        $ionicPopover, $ionicHistory, Validator, $ionicSideMenuDelegate, $cordovaDialogs,
        Http, Settings) {
        'use strict';

        //states of the request
        var REQUEST_STATE = {
            INIT: 0, //initial
            HAIL: 1, //will do a hail can choose between modes
            REQUEST_CAB: 1.5, //requesting a taxi cab
            FARE_ESTIMATION: 1.75, //doing fare estimation
            PICKUP: 2, //setting pickup location
            DROPOFF: 3, //setting dropoff location
            MEETING_POINT: 3.5, //going to meeting point
            CONFIRM_MEETING_POINT: 3.75, //confirm meeting point
            FARE: 4 //final fare is being displayed to user
        };
        $scope.REQUEST_STATE = REQUEST_STATE;



        var fareConfirm = null, //fare confirm popup
            commentConfirm = null, //comment popup
            meetingPointConfirm = null, //meeting point confirm popup
            dragDealer = null, //drag dealer object
            pickmenuBorder = angular.element(document.getElementById("pickmenuBorder")), //pickmenu border
            navBtn = null, // nav button to switch icon
            adsPopup = null; //ads popup

        //init request
        $scope.requestState = REQUEST_STATE.INIT;

        //current request being built
        $scope.request = new HailRequest();

        //input binded from the view
        $scope.input = {};

        /**
         * open comment popup: user enters his comment of his request
         */
        var openCommentPopup = function() {
            /**
             * on submit button tapped in comment popup
             * @param  {String} comment taken from user
             */
            $scope.onSubmitTapped = function(comment) {
                var loadingUpdateTimeout = null;
                $scope.request.comment = comment;
                commentConfirm.close();
                //show loading info to user
                $ionicLoading.show({
                    template: 'Matching you with a driver now...'
                });
                //after 30 secs show to user that we are still searching
                loadingUpdateTimeout = $timeout(function() {
                    $ionicLoading.show({
                        template: 'Search for more cars...'
                    });
                }, 30000);

                //make the request
                $scope.request.make(User.getInstance(), new Callback(function(driver) {
                    //i got the driver accepted me
                    if (loadingUpdateTimeout) {
                        $timeout.cancel(loadingUpdateTimeout);
                        loadingUpdateTimeout = null;
                    }
                    //hide the loading and go to request confirmed page
                    $ionicLoading.hide();
                    $ionicHistory.nextViewOptions({
                        disableBack: true
                    });
                    $ionicHistory.clearCache();
                    $state.go("menu.requestconfirmed", {
                        request: $scope.request
                    });

                }), new Callback(function(e) {
                    //there is no drivers available to accept me
                    if (loadingUpdateTimeout) {
                        $timeout.cancel(loadingUpdateTimeout);
                        loadingUpdateTimeout = null;
                    }
                    $ionicLoading.hide();

                    $rootScope.onError.fire(e);
                }));
            };

            /**
             * on close button tapped
             */
            $scope.onCloseTapped = function() {
                //if in taxi mode enable the user to show fare estimation
                if ($scope.request.mode.isTaxi())
                    $scope.requestState = REQUEST_STATE.FARE_ESTIMATION;
                else
                    $scope.requestState = REQUEST_STATE.REQUEST_CAB;
                updateView();
                commentConfirm.close();
            };

            //the confirm message shown to user to enter a node about location
            $scope.confirm = {
                message: "Send a Note About Your Location So Driver Can Find You Quicker",
                buttons: {
                    isSubmit: true,
                    submit: "CONFIRM HAIL"
                },
                input: {
                    data: $scope.request.comment,
                    placeholder: "I am next to beirut municipality entrance",
                    isAllowed: true
                }
            };

            //show in 500ms the confirm popup (fixing a bug of multiple popups showing)
            $timeout(function() {
                commentConfirm = $ionicPopup.alert({
                    templateUrl: "templates/confirm.popup.html",
                    cssClass: "eserviss-confirm text-center",
                    scope: $scope
                });
            }, 500);
        };

        /**
         * init ads popup
         */
        var initAds = function() {
            //if its not already showb
            if (!CONFIG.IS_ADS_SHOWN) {
                CONFIG.IS_ADS_SHOWN = true;

                //get the ads to show
                var http = new Http();
                http.isLoading = false;
                http.get({
                    url: CONFIG.SERVER.URL,
                    params: {
                        ads: true
                    },
                    onSuccess: new Callback(function(r) {
                        //show the popup with the responsone of the ads
                        var ad = r[0];
                        $scope.onCloseTapped = function() {
                            adsPopup.close();
                        };

                        var cssClass = "eserviss-alert eserviss-ads text-center";
                        cssClass = ad.image.length > 0 ? (cssClass + " has-img") : cssClass;

                        $scope.ad = ad;
                        adsPopup = $ionicPopup.confirm({
                            templateUrl: "templates/ads.popup.html",
                            cssClass: cssClass,
                            scope: $scope
                        });
                    })
                });
            }
        };

        /**
         * update the view depends on the current state
         */
        var updateView = function() {

            /**
             * set the back icon instead of the menu icon
             */
            var navBack = function() {
                navBtn = angular.element(document.getElementById("navBtn"));
                navBtn.removeClass("ion-navicon");
                navBtn.addClass("ion-arrow-left-c");
            };

            /**
             * set the menu icon instead of the back icon
             */
            var navMenu = function() {
                navBtn = angular.element(document.getElementById("navBtn"));
                navBtn.removeClass("ion-arrow-left-c");
                navBtn.addClass("ion-navicon");
            };

            //if dreag dealer set set the initial step of the mode
            if (dragDealer) {
                //if request mode is already set
                if ($scope.request.mode) {
                    dragDealer.setStep($scope.request.mode.getDragDealerStep());
                    var modeActiveIconElem = angular.element(document.getElementById("mode-active-icon"));
                    modeActiveIconElem.attr("src", $scope.request.mode.icon);
                } else {
                    //set the initial step to the last mode
                    dragDealer.setStep(Mode.All.length);
                }


            }

            //if navigation info window is set and drag dealer
            if (mapEngine.navigationInfoWindow && dragDealer) {

                //if nearby cars thread is set to NO, then put the nearby pin icon instead of time
                if (Settings.getInstance().nearby_thread.toUpperCase() === "NO" && Settings.getInstance().nearby_pin) {
                    mapEngine.navigationInfoWindowLeftText(Settings.getInstance().nearby_pin);
                }

                //if init state
                if ($scope.requestState === REQUEST_STATE.INIT) {
                    //set nearby pin if set or set hail icon
                    if (Settings.getInstance().nearby_pin) {
                        mapEngine.navigationInfoWindowLeftText(Settings.getInstance().nearby_pin);
                    } else {
                        mapEngine.navigationInfoWindowLeftText("img/icons/info-hail.png");
                    }
                    //set text to HAIL
                    mapEngine.navigationInfoWindowRightText("HAIL");
                    //init nav to menu
                    navMenu();
                    //enable drag dealer to select a mode
                    dragDealer.enable();
                } else if ($scope.requestState === REQUEST_STATE.HAIL) { // if in hail state
                    //ask user to set pickup
                    mapEngine.navigationInfoWindowRightText("SET PICKUP");
                    mapEngine.navigationInfoWindowLeftText("img/icons/info-pickup.png");
                    //init nav to have back icon
                    navBack();
                    dragDealer.disable();
                } else if ($scope.requestState === REQUEST_STATE.PICKUP) { // if in pickup state
                    //set text of nav to confirm pickup
                    mapEngine.navigationInfoWindowRightText("CONFIRM PICKUP");
                    //set icon
                    mapEngine.navigationInfoWindowLeftText("img/icons/info-pickup.png");
                    //set border height to go behind the icon in the input
                    pickmenuBorder.css("height", "50%");
                    //set the back nav
                    navBack();
                    //keep drag dealer disabled
                    dragDealer.disable();
                } else if ($scope.requestState === REQUEST_STATE.REQUEST_CAB) { // if in request cab state
                    //set the nav info title
                    mapEngine.navigationInfoWindowRightText("REQUEST CAB");
                    //keep it back nav
                    navBack();
                    //keep drag dealder disabled
                    dragDealer.disable();
                } else if ($scope.requestState === REQUEST_STATE.MEETING_POINT) { // if in meeting point state
                    mapEngine.navigationInfoWindowRightText("MEETING POINT");
                    mapEngine.navigationInfoWindowLeftText(null, $scope.request.nearestPoint.distance);
                    /*mapEngine.getMap().setZoom(16);*/
                } else if ($scope.requestState === REQUEST_STATE.CONFIRM_MEETING_POINT) { //if in confirm meeting point state
                    //set the nav text to confirm
                    mapEngine.navigationInfoWindowRightText("CONFIRM");
                    mapEngine.navigationInfoWindowLeftText("img/icons/info-dropoff.png");
                } else if ($scope.requestState === REQUEST_STATE.DROPOFF) { //if in dropoff state
                    //set the nav text to confirm dropoff
                    mapEngine.navigationInfoWindowRightText("CONFIRM DROPOFF");
                    mapEngine.navigationInfoWindowLeftText("img/icons/info-dropoff.png");
                    //keep the menu to back icon
                    navBack();
                    //keep the border height behind the dropoff input
                    pickmenuBorder.css("height", "75%");
                    dragDealer.disable();
                } else if ($scope.requestState === REQUEST_STATE.FARE) { // if in fare state
                    //show wait
                    mapEngine.navigationInfoWindowRightText("WAIT");
                    navBack();
                    dragDealer.disable();
                }
            }

            if (ionic.Platform.isAndroid()) $scope.$apply();
        };

        //find all modes and bind them to the view
        Mode.FindAll(new Callback(function(modes) {
            $scope.request.setMode(modes[0]);
            updateView();
            $scope.modes = modes;
        }));



        /**
         * rebuild synced request
         */
        var rebuildSyncedRequest = function() {
            //if the synced request is sent in params
            if ($stateParams.request !== null) {
                //set the current request to the one in params
                $scope.request = $stateParams.request;

                //if the synced one has no mode set the first mode as deafult one
                if ($scope.request.mode === null) {
                    Mode.FindAll(new Callback(function(modes) {
                        $scope.request.setMode(modes[0]);
                    }));
                }
                //if there is apick a up location is set init with location
                if ($scope.request.pickupLocation !== null) {
                    $scope.input.pickup = $scope.request.pickupPlace;
                    mapEngine.setCenter($scope.request.pickupLocation.lat(), $scope.request.pickupLocation.lng());

                    if ($scope.request.mode.isTaxi() || $scope.request.mode.isFree())
                        $scope.requestState = REQUEST_STATE.REQUEST_CAB;
                    else
                        $scope.requestState = REQUEST_STATE.PICKUP;
                } else if ($scope.request.dropoffLocation !== null)
                    $scope.requestState = REQUEST_STATE.DROPOFF;

                //update the view
                $timeout(updateView, 100);
            }
        };

        /**
         * reset the current request
         */
        var resetRequest = function() {
            //if going to meeting point clear the watch
            if ($scope.request.nearestPointWatch) {

                $scope.request.nearestPointWatch.stopWatching();
            }
            //remove any routes drawn
            mapEngine.gMapsInstance.removePolylines();
            //set back state to init
            $scope.requestState = REQUEST_STATE.INIT;
            //remove current request
            $scope.request = new HailRequest();
            
            //set zoom back to 15
            mapEngine.getMap().setZoom(15);

            //find all modes and bind them to the view
            Mode.FindAll(new Callback(function(modes) {
                $scope.request.setMode(modes[0]);
                updateView();
            }));
            
        };
        //on reset request tapped: the back menu tapped
        $scope.onResetTapped = resetRequest;
        /**
         * on menu tapped
         */
        $scope.onNavTapped = function() {
            //if before hail open the sidemenu
            if ($scope.requestState <= REQUEST_STATE.HAIL)
                $ionicSideMenuDelegate.toggleLeft();
            else {
                //else reset the request
                resetRequest();
            }
        };

        //on nearby cars found
        var onNearbyCarsFound = new Callback(function(nearByCars) {
            //stop if map not ready yet
            if (!mapEngine.isReady) return;

            //remove current map markers
            mapEngine.gMapsInstance.removeMarkers();

            //if no nearby yet
            if (!nearByCars || nearByCars.length === 0) {
                //set the pin icon of nearby
                if (Settings.getInstance().nearby_pin) {
                    mapEngine.navigationInfoWindowLeftText(Settings.getInstance().nearby_pin);
                }
                return;
            }

            //move on each nearby car and set it
            for (var i = 0; i < nearByCars.length; i++) {
                if (nearByCars[i].car_location) {
                    var position = Geolocation.ParsePosition(nearByCars[i].car_location);
                    mapEngine.addMarker(position.lat(), position.lng(), nearByCars[i].image);
                }
            }

            // set the ETA of the first nearby car
            if (nearByCars.length > 0)
                mapEngine.navigationInfoWindowLeftText(null, nearByCars[0].time + "min");
        });
        
        //set nearby cars found callback
        User.getInstance().onNearbyCarsFound = onNearbyCarsFound;

        /**
         * init modes select drag dealer
         */
        var initModeSelect = function() {
            //init drag dealer object
            dragDealer = new Dragdealer('mode-select', {
                steps: $scope.modes.length,
                loose: true,
                tapping: true,
                callback: function(x, y) { // on drag dealer select change
                    //get the drag dealer step
                    $scope.step = dragDealer.getStep()[0];
                    //set the mode of the selected step
                    $scope.request.setMode(Mode.FromDragDealer($scope.step));
                    //bind to user mode the current request mode
                    User.getInstance().nearbyMode = $scope.request.mode;
                    //change the active mode icon
                    var modeActiveIconElem = angular.element(document.getElementById("mode-active-icon"));
                    modeActiveIconElem.attr("src", $scope.request.mode.icon);
                    if (ionic.Platform.isAndroid()) $scope.$apply();
                }
            });
            //in 1 second set the active icon
            $timeout(function() {
                //update the active mode icon
                dragDealer.reflow();
                var modeActiveIconElem = angular.element(document.getElementById("mode-active-icon"));
                modeActiveIconElem.attr("src", $scope.request.mode.icon);
            }, 1000);

            //find all modes
            Mode.FindAll(new Callback(function(modes) {
                //set drag dealer step on the current selected mode
                dragDealer.setStep($scope.request.mode.getDragDealerStep());
                var modeActiveIconElem = angular.element(document.getElementById("mode-active-icon"));
                modeActiveIconElem.attr("src", $scope.request.mode.icon);
                User.getInstance().findNearbyCars($scope.request.mode, null, onNearbyCarsFound);
            }));

        };

        var validateDropoffLocation = function(dropoffLocation) {
            if (dropoffLocation === null)
                $rootScope.onError.fire(new Error("You can't set a dropoff location more than 10 KM"));
        };

        var onPlaceChanged = function(place) {
            //if has no geomtry do nothing
            if (!place || !place.geometry)
                return;
            //navigate to this place
            if (place.geometry.viewport) {
                mapEngine.getMap().fitBounds(place.geometry.viewport);
            } else {
                mapEngine.getMap().setCenter(place.geometry.location);
            }
            mapEngine.getMap().setZoom(17);
        };
        //user for new google places component
        var googlePlaceSelectEvent = null;

        $scope.onPickupLocationSelected = function(place) {

            if (!place.geometry) {
                $scope.request.pickupAddress = "";
            } else {
                onPlaceChanged(place);
                $scope.request.pickupLocation = place.geometry.location;
                $scope.request.pickupAddress = place.formatted_address;
            }
            if (ionic.Platform.isAndroid()) $scope.$apply();
        };

        $scope.onDropoffLocationSelected = function(place) {


            if (!place.geometry) {
                $scope.request.dropoffAddress = "";
            } else {
                onPlaceChanged(place);
                $scope.request.dropoffLocation = place.geometry.location;
                $scope.request.dropoffAddress = place.formatted_address;
                validateDropoffLocation($scope.request.dropoffLocation);
            }
            if (ionic.Platform.isAndroid()) $scope.$apply();
        };

        $scope.$on('$ionicView.leave', function() {
            if (googlePlaceSelectEvent) googlePlaceSelectEvent();
        });

        $scope.$on('$ionicView.enter', function() {
            googlePlaceSelectEvent = $scope.$on("g-places-autocomplete:select", function(event, place) {
                if ($scope.requestState === REQUEST_STATE.PICKUP)
                    $scope.onPickupLocationSelected(place);
                else if ($scope.requestState === REQUEST_STATE.DROPOFF)
                    $scope.onDropoffLocationSelected(place);
            });
        });

        var initModesPopovers = function() {
            $ionicPopover.fromTemplateUrl('mode.popover.html', {
                scope: $scope
            }).then(function(popover) {
                $scope.openPopover = function(event, step) {
                    if (!step) step = $scope.step;
                    popover.show(event);
                };

                $scope.$on('$destroy', function() {
                    popover.remove();
                });
            });


        };

        initModeSelect();
        initModesPopovers();

        $scope.onRequestPlusTapped = function() {
            if ($scope.request.passengers < $scope.request.mode.maxPassengers)
                $scope.request.passengers++;
        };

        $scope.onRequestMinusTapped = function() {
            if ($scope.request.passengers > 1)
                $scope.request.passengers--;
        };

        $scope.onPickupTapped = function() {
            $state.go("menu.pickuplocations", {
                request: $scope.request
            });
        };

        mapEngine.ready(function() {

            var onDestinationLocationChange = new Callback(function() {
                var g = new Geolocation();

                $rootScope.onProgress.fire();
                var locationLatLng = mapEngine.getCenter();
                g.latlngToAddress(locationLatLng, new Callback(function(address, place) {
                    if (address.toUpperCase().indexOf("UNNAMED") > -1)
                        address = "No street name";

                    if ($scope.requestState === REQUEST_STATE.HAIL || $scope.requestState === REQUEST_STATE.PICKUP || $scope.requestState === REQUEST_STATE.REQUEST_CAB || $scope.requestState === REQUEST_STATE.FARE_ESTIMATION) { /*!$scope.request.pickupAddress || $scope.request.pickupAddress.trim().length === 0*/
                        $scope.request.pickupLocation = locationLatLng;
                        $scope.request.pickupAddress = address;
                        $scope.input.pickup = place;

                    } else if ($scope.requestState === REQUEST_STATE.DROPOFF) { /*!$scope.request.dropoffAddress || $scope.request.dropoffAddress.trim().length === 0*/
                        $scope.request.dropoffAddress = address;
                        $scope.request.dropoffLocation = locationLatLng;
                        $scope.input.dropoff = place;
                        validateDropoffLocation($scope.request.dropoffLocation);
                    }

                    $rootScope.onProgressDone.fire();
                    if (ionic.Platform.isAndroid()) $scope.$apply();

                }), $rootScope.onError);
            });

            //if not comming from pickup locations
            if (!($stateParams.request && $stateParams.request.pickupPlace) && $scope.requestState == REQUEST_STATE.REQUEST_CAB)
                onDestinationLocationChange.fire();

            mapEngine.gMapsInstance.on("dragend", function() {
                if ($scope.request.mode) {
                    if (Settings.getInstance().drag_thread.toUpperCase() === "YES")
                        User.getInstance().findNearbyCars($scope.request.mode, mapEngine.getCenter(), onNearbyCarsFound);
                    // mapEngine.navigationInfoWindowLeftText(null, $scope.request.mode.etaTime + "min");
                }

                if ($scope.requestState === REQUEST_STATE.PICKUP || $scope.requestState === REQUEST_STATE.DROPOFF || $scope.requestState === REQUEST_STATE.REQUEST_CAB)
                    onDestinationLocationChange.fire();
            });

            var onHailRequestPickedup = new Callback(function() {
                mapEngine.drawRoute({
                    origin: [$scope.request.pickupLocation.lat(), $scope.request.pickupLocation.lng()],
                    destination: [$scope.request.dropoffLocation.lat(), $scope.request.dropoffLocation.lng()],
                    travelMode: "driving",
                    strokeColor: "#7EBBFE",
                    strokeWeight: 7
                });
                $scope.requestState = REQUEST_STATE.FARE;
                updateView();

                $scope.request.estimateCost(new Callback(function(cost) {

                    $scope.confirm = {
                        title: 'FARE ESTIMATION',
                        message: Util.String("{0} USD for {1}", [cost, $scope.request.mode.name]),
                        buttons: {
                            isSubmit: false,
                            yes: "CONFIRM",
                            no: "CANCEL",
                            promotePositive: true
                        }
                    };

                    $scope.onNoTapped = function() {
                        fareConfirm.close();
                        resetRequest();
                    };
                    $scope.onYesTapped = function() {
                        fareConfirm.close();

                        $scope.onSubmitTapped = function(comment) {
                            $scope.request.comment = comment;
                            commentConfirm.close();
                            $ionicLoading.show({
                                template: 'Matching you with a Driver now!'
                            });
                            $scope.request.make(User.getInstance(), new Callback(function(driver) {
                                mapEngine.gMapsInstance.off("dragend");
                                $ionicLoading.hide();
                                $ionicHistory.clearCache();
                                $state.go("menu.requestconfirmed", {
                                    request: $scope.request
                                });

                            }), new Callback(function(e) {
                                $rootScope.onError.fire(e);
                                /*$scope.requestState = REQUEST_STATE.DROPOFF;
                                updateView();*/
                                resetRequest();
                            }));
                        };

                        $scope.confirm = {
                            message: "Send a Note About Your Location So Driver Can Find You Quicker",
                            buttons: {
                                isSubmit: true
                            },
                            input: {
                                data: $scope.request.comment,
                                placeholder: "I am next to beirut municipality entrance",
                                isAllowed: true
                            }
                        };

                        $timeout(function() {
                            commentConfirm = $ionicPopup.confirm({
                                templateUrl: "templates/confirm.popup.html",
                                cssClass: "eserviss-confirm text-center",
                                scope: $scope
                            });
                        }, 500);


                    };

                    fareConfirm = $ionicPopup.confirm({
                        templateUrl: "templates/confirm.popup.html",
                        cssClass: "eserviss-confirm text-center",
                        scope: $scope
                    });

                }), new Callback(function(e) {
                    $rootScope.onError.fire(e);
                    $scope.requestState = REQUEST_STATE.DROPOFF;
                    updateView();
                }));
            });

            var onLocationEnabled = new Callback(function() {

                initAds();

                $scope.myLocationTapped = function() {
                    User.getInstance().findPosition(new Callback(function(position) {
                        mapEngine.addUserAccuracy(position.lat(), position.lng(), position.accuracy);
                        mapEngine.setCenter(position.lat(), position.lng());
                        if ($scope.requestState === REQUEST_STATE.PICKUP || $scope.requestState === REQUEST_STATE.DROPOFF || $scope.requestState === REQUEST_STATE.REQUEST_CAB)
                            onDestinationLocationChange.fire();
                    }));
                };

                var g = new Geolocation();
                User.getInstance().findPosition(new Callback(function(position) {
                    mapEngine.addUserAccuracy(position.lat(), position.lng(), position.accuracy);
                    mapEngine.setCenter(position.lat(), position.lng());
                    rebuildSyncedRequest();
                }), $rootScope.onError);

                mapEngine.navigationMarker(function() {
                    /*mapEngine.addCenterMarker();*/
                });
                mapEngine.navigationInfo(function() {

                    if ($scope.requestState === REQUEST_STATE.INIT) {
                        $scope.request.inService(new Callback(function() {
                            onDestinationLocationChange.fire();
                            if ($scope.request.mode.isTaxi() || $scope.request.mode.isFree()) {
                                /*$state.go("menu.pickuplocations", {
                                    request: $scope.request
                                });*/
                                $scope.requestState = REQUEST_STATE.REQUEST_CAB;
                            } else {
                                $scope.requestState = REQUEST_STATE.PICKUP; //due to ux will compress the hail step to pickup
                            }
                            updateView();

                        }), $rootScope.onError);
                    } else if ($scope.requestState === REQUEST_STATE.PICKUP) {
                        $scope.request.inService(new Callback(function() {

                            $scope.requestState = REQUEST_STATE.DROPOFF;
                            updateView();

                        }), $rootScope.onError);
                    } else if ($scope.requestState === REQUEST_STATE.REQUEST_CAB) {
                        $scope.request.inService(new Callback(function() {
                            onDestinationLocationChange.fire();
                            $scope.requestState = REQUEST_STATE.FARE_ESTIMATION;
                            openCommentPopup();
                            updateView();
                            /*mapEngine.gMapsInstance.off("dragend");
                            $state.go("menu.sendnote", {
                                request: $scope.request
                            });*/

                        }), $rootScope.onError);
                    } else if ($scope.requestState === REQUEST_STATE.FARE_ESTIMATION) {
                        $scope.request.inService(new Callback(function() {
                            onDestinationLocationChange.fire();
                            openCommentPopup();
                            updateView();
                        }), $rootScope.onError);
                    } else if ($scope.requestState === REQUEST_STATE.DROPOFF) {

                        var validator = new Validator();
                        if (validator.isNull($scope.request.dropoffLocation, "Please enter dropoff location first")) {
                            $rootScope.onError.fire(validator.getError());
                            return;
                        }

                        $scope.request.inService(new Callback(function() {
                            if ($scope.request.mode.id === Mode.ID.SERVISS) {

                                $scope.request.validateServicePickup(new Callback(function(isNear) {

                                    if (isNear) {
                                        onHailRequestPickedup.fire();
                                        return;
                                    }

                                    $scope.confirm = {
                                        title: 'MEETING POINT',
                                        message: $scope.request.nearestPoint.message,
                                        buttons: {
                                            isSubmit: false,
                                            yes: "ACCEPT",
                                            no: "CHOOSE TAXI",
                                        }
                                    };

                                    $scope.onNoTapped = function() {
                                        meetingPointConfirm.close();
                                        resetRequest();
                                    };

                                    $scope.onYesTapped = function() {
                                        var nearestPointImg = angular.element(document.getElementById("meetingpointImg"));
                                        meetingPointConfirm.close();
                                        $scope.requestState = REQUEST_STATE.MEETING_POINT;
                                        updateView();
                                        $scope.request.nearestPoint.header.line[0] = " START WALKING TOWARDS MEETING POINT NOW";
                                        nearestPointImg.css("vertical-align", "middle");
                                        $scope.request.watchToMeetingPoint(new Callback(function(userPosition) {
                                            mapEngine.drawRoute({
                                                origin: [userPosition.lat(), userPosition.lng()],
                                                destination: [$scope.request.nearestPoint.location.lat(), $scope.request.nearestPoint.location.lng()],
                                                travelMode: "walking",
                                                strokeColor: "#7EBBFE",
                                                strokeWeight: 7
                                            });
                                            mapEngine.setCenter($scope.request.nearestPoint.location.lat(), $scope.request.nearestPoint.location.lng());
                                        }), new Callback(function() {
                                            $scope.request.nearestPoint.header.line[0] = "YOU HAVE REACHED YOUR MEETING POINT!";
                                            $scope.request.nearestPoint.header.line[1] = "PLEASE CONFIRM YOUR RIDE REQUEST";

                                            nearestPointImg.css("vertical-align", "top");
                                            $scope.requestState = REQUEST_STATE.CONFIRM_MEETING_POINT;
                                            updateView();
                                        }), new Callback(function() {
                                            $scope.request.nearestPoint.header.line[0] = "YOU ARE CLOSE TO MEETING POINT";
                                            nearestPointImg.css("vertical-align", "middle");
                                        }, 1), $rootScope.onError);
                                    };

                                    meetingPointConfirm = $ionicPopup.confirm({
                                        templateUrl: "templates/confirm.popup.html",
                                        cssClass: "eserviss-confirm text-center",
                                        scope: $scope
                                    });



                                }), $rootScope.onError);

                            } else {
                                onHailRequestPickedup.fire();
                            }

                        }), $rootScope.onError);
                    } else if ($scope.requestState === REQUEST_STATE.MEETING_POINT) {
                        $cordovaDialogs.alert("Eserviss is wathcing your position until you reach the meeting point", 'Meeting Point');
                    } else if ($scope.requestState === REQUEST_STATE.CONFIRM_MEETING_POINT) {
                        mapEngine.gMapsInstance.removePolylines();
                        $scope.request.pickupLocation = $scope.request.nearestPoint.location.toLatLng();
                        var g = new Geolocation();
                        g.latlngToAddress($scope.request.pickupLocation, new Callback(function(address) {
                            $scope.request.pickupAddress = address;
                            if (ionic.Platform.isAndroid()) $scope.$apply();
                        }));
                        onHailRequestPickedup.fire();
                    }

                    if (ionic.Platform.isAndroid()) $scope.$apply();
                });

                updateView();


            });

            $rootScope.ifLocationEnabled(onLocationEnabled);
        });

        $scope.$on('$destroy', function() {
            /*window.addEventListener('native.keyboardhide', function() {});*/
        });

    }
]);

controllers.controller('HailRequestController@confirmed', [
    '$scope', '$state', '$stateParams', 'mapEngine', 'User', '$rootScope', 'Callback',
    'Util', 'Geolocation', '$cordovaDialogs', '$ionicHistory', '$timeout', 'HailRequest',
    function($scope, $state, $stateParams, mapEngine, User, $rootScope, Callback,
        Util, Geolocation, $cordovaDialogs, $ionicHistory, $timeout, HailRequest) {
        'use strict';

        var confirmedScrollElem = angular.element(document.getElementById("confirmedScroll")),
            infoElem = angular.element(document.getElementById("info")),
            geolocation = new Geolocation();

        var MARKER_ID = {
            USER: 1,
            MODE: 2
        };

        $scope.request = $stateParams.request;
        $scope.infoState = 1;

        //unlock app
        localStorage.removeItem(HailRequest.HAIL_LOCK);

        /*$scope.request.driver.findRating(new Callback(function (rating) {
            if (ionic.Platform.isAndroid()) $scope.$apply();
        }), $rootScope.onError);*/

        $scope.onCloseTapped = function() {
            if ($scope.infoState === 1) { //if driver info shown
                confirmedScrollElem.removeClass('slideOutDown');
                confirmedScrollElem.removeClass('slideInDown');
                confirmedScrollElem.addClass('slideOutDown');
                infoElem.css("height", "auto");
                $scope.infoState = 0;
            } else if ($scope.infoState === 0) { //if driver info hidden
                confirmedScrollElem.removeClass('slideOutDown');
                confirmedScrollElem.removeClass('slideInDown');
                confirmedScrollElem.addClass('slideInDown');
                infoElem.css("height", "50%");
                $scope.infoState = 1;
            }
        };

        $scope.onContactTapped = function() {

            plugins.listpicker.showPicker({
                title: "Contact Driver",
                items: [{
                    text: "Send a Message",
                    value: "MESSAGE"
                }, {
                    text: "Call Driver",
                    value: "CALL"
                }]
            }, function(action) {
                if (action === 'MESSAGE') {
                    plugins.socialsharing.shareViaSMS({
                        message: ''
                    }, $scope.request.driver.DriverPhone, null, function() {});
                } else if (action === 'CALL') {
                    plugins.CallNumber.callNumber(function() {}, function(e) {
                        $rootScope.onError.fire(new Error(e, true, true));
                    }, $scope.request.driver.DriverPhone);
                }

            }, function() {});
        };

        $scope.onShareEtaTapped = function() {
            var POST_MESSAGE = Util.String("Get Eserviss app at http://eserviss.com/app");
            var POST_TITLE = "Eserviss";

            plugins.socialsharing.share(POST_MESSAGE, POST_TITLE);
        };

        var initSync = function() {
            $scope.request.onEtaArrival = new Callback(function(etaArrival) {
                var PADDING_BOTTOM = 0.02;

                if ($scope.request.stage === HailRequest.STAGE.RESPONSE) {

                    var carLocation = etaArrival.car_location ? Geolocation.ParsePosition(etaArrival.car_location) : null;
                    if (!carLocation) {
                        mapEngine.addMarker(User.getInstance().position.lat(), User.getInstance().position.lng(), etaArrival.user_pin, MARKER_ID.USER);
                        mapEngine.setCenter(User.getInstance().position.lat(), User.getInstance().position.lng() + PADDING_BOTTOM);
                    } else {
                        mapEngine.infoBubble(carLocation.lat(), carLocation.lng(), etaArrival.display);
                        mapEngine.addMarker(User.getInstance().position.lat(), User.getInstance().position.lng() + PADDING_BOTTOM, etaArrival.user_pin, MARKER_ID.USER);
                        mapEngine.setCenter(carLocation.lat(), carLocation.lng() + PADDING_BOTTOM);

                        if (User.getInstance().position.calculateDistance(carLocation) <= 100) {
                            mapEngine.setZoom(15);
                        } else {
                            mapEngine.fitMap(carLocation, User.getInstance().position);
                        }

                    }
                } else {
                    mapEngine.removeInfoBubble();
                    mapEngine.setCenter(User.getInstance().position.lat(), User.getInstance().position.lng());
                }
                // mapEngine.addUserAccuracy(User.getInstance().position.lat(), User.getInstance().position.lng(), User.getInstance().position.accuracy, others);

            });

            $scope.request.sync(User.getInstance(), new Callback(function() {
                if ($scope.request.stage === HailRequest.STAGE.PICKUP && $scope.request.getDropoffLocation()) {
                    mapEngine.addMarker($scope.request.getDropoffLocation().lat(), $scope.request.getDropoffLocation().lng(), $scope.request.mode.icon, MARKER_ID.MODE);
                }

                if (ionic.Platform.isAndroid()) $scope.$apply();

            }), $rootScope.onError);


            $scope.request.onPickedup = new Callback(function() {
                mapEngine.addMarker($scope.request.getDropoffLocation().lat(), $scope.request.getDropoffLocation().lng(), $scope.request.getDropoffLocation());
            });

            $scope.request.onDroppedoff = new Callback(function() {
                geolocation.stopWatching();
                if ($scope.request.mode.isFree()) {
                    $state.go("menu.freereceipt", {
                        request: $scope.request
                    });
                } else {
                    $state.go("menu.receipt", {
                        request: $scope.request
                    });
                }


            });

            $scope.request.onDriverCanceled = new Callback(function() {

                $cordovaDialogs.alert("Driver didn't find you, the ride has been canceled", 'Ride Canceled')
                    .then(function() {
                        $ionicHistory.nextViewOptions({
                            disableBack: true
                        });
                        $ionicHistory.clearCache();
                        mapEngine.resetMap();
                        $timeout(function() {
                            $state.go("menu.hailrequest");
                        }, 50);
                    });
            });
        };
        initSync();

        var onLocationEnabled = new Callback(function() {
            User.getInstance().findPosition(new Callback(function(position) {
                mapEngine.addUserAccuracy(position.lat(), position.lng(), position.accuracy);
                mapEngine.setCenter(position.lat(), position.lng());
            }), $rootScope.onError);

            if ($scope.request.dropoffLocation && $scope.request.dropoffLocation.lat() && $scope.request.dropoffLocation.lng()) {
                mapEngine.drawRoute({
                    origin: [$scope.request.pickupLocation.lat(), $scope.request.pickupLocation.lng()],
                    destination: [$scope.request.dropoffLocation.lat(), $scope.request.dropoffLocation.lng()],
                    travelMode: "driving",
                    strokeColor: "#7EBBFE",
                    strokeWeight: 7
                });
            }


            geolocation.watch(new Callback(function(position) {
                User.getInstance().position = position;
                mapEngine.addUserAccuracy(position.lat(), position.lng(), position.accuracy);
            }));
        });



        $scope.$on('$ionicView.leave', function() {
            geolocation.stopWatching();
            $scope.request.isSyncable = false;
            /*mapEngine.removeMarker();*/
            mapEngine.resetMap();
            User.getInstance().onNearbyCarsFound = null;
        });

        $scope.$on('$ionicView.enter', function() {
            mapEngine.ready(function() {
                // onLocationEnabled.fire();
                $rootScope.ifLocationEnabled(onLocationEnabled);
            });
        });

    }
]);

controllers.controller('HailRequestController@receipt', [
    '$scope', '$state', '$stateParams', 'Validator', '$rootScope', 'Callback',
    '$ionicHistory', 'User', '$cordovaDialogs', '$timeout',
    function($scope, $state, $stateParams, Validator, $rootScope, Callback,
        $ionicHistory, User, $cordovaDialogs, $timeout) {
        'use strict';

        $ionicHistory.nextViewOptions({
            disableBack: true
        });

        var today = new Date();
        $scope.date = {
            day: today.getDate(),
            month: today.toDateString().split(' ')[1],
            year: today.getFullYear()
        };

        $scope.request = $stateParams.request;
        $scope.receipt = {};

        /*User.getInstance().findCredit(new Callback(function(credit) {
            if (credit.cash < $scope.request.totalCost) {
                $cordovaDialogs.alert("Please pay the trip cost to the driver", 'Trip Cost');
            } else {
                $scope.request.consumeCost(User.getInstance(), new Callback(function() {
                    $cordovaDialogs.alert("Your trip cost has been charged from your balance", 'Trip Cost');
                }), $rootScope.onError);
            }

        }), $rootScope.onError);*/

        var consumeCost = function() {
            $scope.request.consumeCost(User.getInstance(), new Callback(function() {
                $cordovaDialogs.alert("Your trip cost has been charged from your balance", 'Trip Cost');
            }), $rootScope.onError);
        };

        $scope.request.findFare(User.getInstance(), new Callback(function() {
            console.log("fare is", $scope.request.fare);
            User.getInstance().findCredit(new Callback(function(userBalance) {
                var toCharge = Math.ceil($scope.request.fare - userBalance.cash);
                if (toCharge > 0) {
                    User.getInstance().recharge(toCharge, new Callback(consumeCost), new Callback(function(e) {
                        $rootScope.onError.fire(e);
                        consumeCost();
                    }));
                } else {
                    consumeCost();
                }
            }), new Callback(consumeCost));
        }), new Callback(consumeCost));




        $scope.onSubmitTapped = function(receipt) {

            var validator = new Validator();
            if (validator.isEmpty(receipt.rating, "Please insert rating first in your feedback") ||
                validator.isEmpty(receipt.comment, "Please insert comment first in your feedback")) {
                $rootScope.onError.fire(validator.getError());
                return;
            }

            $scope.request.feedback(User.getInstance(), $scope.receipt.rating, $scope.receipt.comment, new Callback(function() {
                $ionicHistory.clearCache();
                $timeout(function() {
                    $state.go("menu.hailrequest", {}, {
                        reload: true
                    });
                    /*$state.go("menu.hailrequest");*/
                }, 50);
            }), $rootScope.onError);

        };


    }
]);

controllers.controller('HailRequestController@freereceipt', [
    '$scope', '$state', '$stateParams', 'Validator', '$rootScope', 'Callback',
    '$ionicHistory', 'User', '$cordovaDialogs', '$timeout', 'HailRequest', 'Geolocation',
    '$cordovaInAppBrowser',
    function($scope, $state, $stateParams, Validator, $rootScope, Callback,
        $ionicHistory, User, $cordovaDialogs, $timeout, HailRequest, Geolocation,
        $cordovaInAppBrowser) {
        'use strict';

        $ionicHistory.nextViewOptions({
            disableBack: true
        });

        var today = new Date();
        $scope.date = {
            day: today.getDate(),
            month: today.toDateString().split(' ')[1],
            year: today.getFullYear()
        };
        $scope.request = $stateParams.request;

        var g = new Geolocation();
        g.findPosition(new Callback(function(dropoffLocation) {
            HailRequest.EstimateCost(1, $scope.request.pickupLocation, dropoffLocation, new Callback(function(totalCost) {
                $scope.totalCost = totalCost;
            }));
        }));

        $scope.onCancelTapped = function() {
            $ionicHistory.clearCache();
            $timeout(function() {
                $state.go("menu.hailrequest", {}, {
                    reload: true
                });
                /*$state.go("menu.hailrequest");*/
            }, 50);
        };

        $scope.onReviewTapped = function() {
            var options = {
                location: 'yes',
                clearcache: 'yes',
                toolbar: 'no'
            };

            var url = "http://leb.cab";
            if (ionic.Platform.isIOS())
                url = "https://itunes.apple.com/us/app/eserviss-taxi.-service-bus/id1024534126?ls=1&mt=8";

            $cordovaInAppBrowser.open(url, '_blank', options)
                .then(function(event) {

                    $ionicHistory.clearCache();
                    $timeout(function() {
                        $state.go("menu.hailrequest", {}, {
                            reload: true
                        });
                    }, 50);
                });


        };


    }
]);

controllers.controller('HailRequestController@pickuplocations', [
    '$scope', '$state', '$stateParams', '$ionicHistory', '$timeout', 'User', 'Callback', '$rootScope',
    'Util',
    function($scope, $state, $stateParams, $ionicHistory, $timeout, User, Callback, $rootScope,
        Util) {
        'use strict';

        $scope.request = $stateParams.request;
        var googlePlaceSelectEvent = null;

        $ionicHistory.nextViewOptions({
            disableBack: true
        });

        $scope.onLocationSelected = function(place) {
            var location = place.geometry.location;
            $scope.request.pickupLocation = location;
            $scope.request.pickupPlace = place;
        };

        /*$scope.$on('$ionicView.leave', function() {
            if (googlePlaceSelectEvent) googlePlaceSelectEvent();
        });

        $scope.$on('$ionicView.enter', function() {
            googlePlaceSelectEvent = $scope.$on("g-places-autocomplete:select", function(event, place) {
                $scope.request.pickupLocation = place.geometry.location;
                $scope.request.pickupAddress = place.formatted_address;
            });
        });*/

        User.getInstance().findFavorites(new Callback(function(favorites) {
            favorites = favorites.splice(0, 4);

            var homeFavorite = favorites[0];

            var workFavorite = favorites[1];

            $scope.onAddHomeTapped = function() {
                if (homeFavorite && homeFavorite.location.lat() && homeFavorite.location.lng()) {
                    $scope.request.pickupLocation = homeFavorite.location;
                    $scope.onDoneTapped();
                } else {
                    $rootScope.onError.fire(new Error("You don't have a home favorite added"));
                }
            };

            $scope.onAddWorkTapped = function() {
                if (workFavorite && workFavorite.location.lat() && workFavorite.location.lng()) {
                    $scope.request.pickupLocation = workFavorite.location;
                    $scope.onDoneTapped();
                } else {
                    $rootScope.onError.fire(new Error("You don't have a work favorite added"));
                }
            };
        }), $rootScope.onError);

        $scope.$on('$ionicView.leave', function() {
            if (googlePlaceSelectEvent) googlePlaceSelectEvent();
        });

        $scope.$on('$ionicView.enter', function() {
            googlePlaceSelectEvent = $scope.$on("g-places-autocomplete:select", function(event, place) {
                $scope.onLocationSelected(place);
                $scope.request.pickupAddress = place.formatted_address;
            });
        });


        $scope.onDoneTapped = function() {
            if ($ionicHistory.backView().stateName === "menu.hailrequest") {
                $ionicHistory.clearCache();
                $timeout(function() {
                    $ionicHistory.backView().stateParams.request = $scope.request;
                    $ionicHistory.goBack();
                }, 50);
            } else {
                $ionicHistory.goBack();
            }
        };
    }
]);

controllers.controller('HailRequestController@estimationfee', [
    '$scope', '$state', '$stateParams', '$ionicHistory', '$timeout',
    'Callback', '$rootScope',
    function($scope, $state, $stateParams, $ionicHistory, $timeout,
        Callback, $rootScope) {
        'use strict';

        var googlePlaceSelectEvent = null;

        $scope.request = $stateParams.request;

        $ionicHistory.nextViewOptions({
            disableBack: true
        });

        $scope.onLocationSelected = function(place) {
            var location = place.geometry.location;
            $scope.request.dropoffLocation = location;
            $scope.request.estimateCost(null, $rootScope.onError);
        };

        $scope.onDoneTapped = function() {
            if ($ionicHistory.backView().stateName === "menu.hailrequest") {
                $ionicHistory.clearCache();
                $timeout(function() {
                    $ionicHistory.backView().stateParams.request = $scope.request;
                    $ionicHistory.goBack();
                }, 50);
            } else {
                $ionicHistory.goBack();
            }
        };

        $scope.$on('$ionicView.leave', function() {
            if (googlePlaceSelectEvent) googlePlaceSelectEvent();
        });

        $scope.$on('$ionicView.enter', function() {
            googlePlaceSelectEvent = $scope.$on("g-places-autocomplete:select", function(event, place) {
                $scope.request.dropoffAddress = place.formatted_address;
                $scope.onLocationSelected(place);
            });
        });
    }
]);

controllers.controller('HailRequestController@sendnote', [
    '$scope', '$state', '$stateParams', '$ionicHistory', '$timeout',
    'Callback', '$rootScope', 'User', '$ionicLoading', 'HailRequest',
    'Mode',
    function($scope, $state, $stateParams, $ionicHistory, $timeout,
        Callback, $rootScope, User, $ionicLoading, HailRequest,
        Mode) {
        'use strict';

        $scope.request = $stateParams.request;
        var loadingUpdateTimeout = null;

        $ionicHistory.nextViewOptions({
            disableBack: true
        });

        $scope.onConfirmTapped = function() {
            $ionicLoading.show({
                template: 'Matching you with a driver now...'
            });

            loadingUpdateTimeout = $timeout(function() {
                $ionicLoading.show({
                    template: 'Search for more cars...'
                });
            }, 30000);

            $scope.request.make(User.getInstance(), new Callback(function(driver) {
                if (loadingUpdateTimeout) {
                    console.log("loadingUpdateTimeout", loadingUpdateTimeout);
                    $timeout.cancel(loadingUpdateTimeout);
                    loadingUpdateTimeout = null;
                }
                $ionicLoading.hide();
                $ionicHistory.clearCache();
                $state.go("menu.requestconfirmed", {
                    request: $scope.request
                });

            }), new Callback(function(e) {
                if (loadingUpdateTimeout) {
                    $timeout.cancel(loadingUpdateTimeout);
                    loadingUpdateTimeout = null;
                }
                $ionicLoading.hide();

                $rootScope.onError.fire(e, new Callback(function() {

                    $scope.request = new HailRequest();
                    $scope.request.setMode(Mode.FindById(Mode.ID.TAXI));
                    $ionicHistory.clearCache();
                    $timeout(function() {
                        $state.go("menu.hailrequest", {
                            request: $scope.request
                        });
                    }, 50);

                }));
            }));
        };
    }
]);

controllers.controller('HailRequestController@cancelride', [
    '$scope', '$state', '$stateParams', 'Callback', '$rootScope',
    'Validator', '$timeout', '$ionicHistory',
    function($scope, $state, $stateParams, Callback, $rootScope,
        Validator, $timeout, $ionicHistory) {
        'use strict';
        $scope.request = $stateParams.request;
        $scope.cancel = {};

        $ionicHistory.nextViewOptions({
            disableBack: true
        });

        $scope.onCancelTapped = function() {
            var validator = new Validator();
            validator.isNull($scope.cancel.reason, "Please enter a valid reason first");
            if (!validator.isPassed()) {
                $rootScope.onError.fire(validator.getError());
                return;
            }


            $scope.request.cancelRide($scope.cancel.reason, new Callback(function() {
                $ionicHistory.clearCache();
                $timeout(function() {
                    $state.go("menu.hailrequest");
                }, 50);
            }), $rootScope.onError);
        };




    }
]);
<file_sep>application.factory('Country', [
    'Model', 'Http', 'Callback',
    function(Model, Http, Callback) {
        'use strict';

        var Country = augment(Model, function(parent) {
            
            this.constructor = function(row) {
            	this._fields = ["name", "phoneCode"];
            	this._tableName = "Country";
                this._modelType = Country;
                parent.constructor.call(this, row);
            };


        });

        Country.FindAll = function (onSuccess, onError) {
            var http = new Http();
            http.get({
                url: "countries.json",
                model: Country,
                onSuccess: onSuccess,
                onError: onError
            });
        };

        return Country;
    }
]);<file_sep>controllers.controller('GuestController@share', [
    '$scope', '$state', '$stateParams', 'User', 'Callback', '$rootScope',
    'Util', 'Error',
    function($scope, $state, $stateParams, User, Callback, $rootScope,
        Util, Error) {
        'use strict';

        //link to be refered per user
        var POST_MESSAGE = "Get Eserviss app at http://leb.cab";
        var POST_TITLE = "ESERVISS app install";

        //check if client is android or ios whether to use link email or app email
        $scope.isAndroid = ionic.Platform.isAndroid();
        $scope.isIos = ionic.Platform.isIOS();

        //find share referal code
        User.getInstance().findShare(new Callback(function(share) {
            //update message with the referal code
            $scope.share = share;
            POST_MESSAGE = Util.String("Get Eserviss app at http://leb.cab, Referal code is {0}", [share.code]);

            $scope.title = POST_TITLE;
            $scope.message = POST_MESSAGE;
        }), $rootScope.onError);

        /**
         * on copy (icon) referal link tapped
         */
        $scope.onCopyReferalLink = function() {
            //check if cordova isn't defined (browser testing)
            if (typeof(cordova) === "undefined") return;
            //copy the message to clipboard
            cordova.plugins.clipboard.copy(POST_MESSAGE);
            //user feedback that message is copied using toast message
            $cordovaToast.showLongBottom('Your referal link copied')
                .then(function(success) {}, function(error) {});
        };

        /**
         * open share action sheet
         * @param  {String} message to share
         * @param  {String} title   of the shared message
         */
        var shareActionsheet = function(message, title) {
            if (!message) message = POST_MESSAGE;
            if (!title) title = POST_TITLE;

            plugins.socialsharing.share(message, title);
        };

        /**
         * on sms button tapped
         */
        $scope.onSmsTapped = function() {
            plugins.socialsharing.shareViaSMS({
                message: POST_MESSAGE
            }, null, null, shareActionsheet);
        };

        /**
         * on facebook button tapped
         */
        $scope.onFacebookTapped = function() {
            plugins.socialsharing.shareViaFacebookWithPasteMessageHint(POST_MESSAGE, null, null, "Your referal post is copied, paste it if you like", null, shareActionsheet);
        };

        /**
         * on google plus button tapped
         */
        $scope.onGoogleTapped = function() {
            if (ionic.Platform.isAndroid()) {
                plugins.socialsharing.shareVia('com.google.android.apps.plus', POST_MESSAGE, POST_TITLE, null, null, null, shareActionsheet);
            } else {
                shareActionsheet();
            }
        };

        /**
         * on twitter button tapped
         */
        $scope.onTwitterTapped = function() {
            plugins.socialsharing.shareViaTwitter(POST_MESSAGE, null, null, null, shareActionsheet);
        };

        /**
         * on whatsapp button tapped
         */
        $scope.onWhatsappTapped = function() {
            plugins.socialsharing.shareViaWhatsApp(POST_MESSAGE, null, null, null, shareActionsheet);
        };

        /**
         * on email button tapped
         */
        $scope.onEmailTapped = function() {
            plugins.socialsharing.shareViaEmail(POST_MESSAGE, POST_TITLE, [], [], [], null, function () {
                alert("email succeeded");
            }, shareActionsheet);
        };


    }
]);
<file_sep>controllers.controller('HomeController@splash', [
    '$scope', '$state', '$stateParams', '$ionicHistory', '$ionicPlatform', '$cordovaDialogs',
    '$rootScope', 'Callback', 'Geolocation', '$ionicModal', '$timeout', 'Settings', '$ionicLoading',
    'User', 'Util', 'Mode', '$cordovaFacebook',
    function($scope, $state, $stateParams, $ionicHistory, $ionicPlatform, $cordovaDialogs,
        $rootScope, Callback, Geolocation, $ionicModal, $timeout, Settings, $ionicLoading,
        User, Util, Mode, $cordovaFacebook) {
        'use strict';

        $ionicHistory.nextViewOptions({
            disableBack: true
        });

        $ionicPlatform.ready(function() {
            if (!Util.IsBrowser()) {
                $cordovaFacebook.getLoginStatus();
                GappTrack.track("UA-69276680-1", "", "1", false, function() {}, function(message) {});
            }
        });

        $rootScope.onError = new Callback(function(e, onTapped) {
            var message = e && e.message ? e.message : "Check your internet connectivity";

            $cordovaDialogs.alert(message, 'Note')
                .then(function() {
                    if (onTapped && typeof(onTapped) !== "string") onTapped.fire();
                    $ionicLoading.hide();
                });
        });

        $rootScope.onProgress = new Callback(function() {
            $ionicLoading.show({
                template: '<ion-spinner></ion-spinner>'
            });
        });

        $rootScope.onProgressDone = new Callback(function() {
            $ionicLoading.hide();
        });

        $rootScope.ifLocationEnabled = function(onEnabled, onNotEnabled) {
            var locationoffModal = null;

            if (Util.IsBrowser()) {
                onEnabled.fire();
                return;
            }


            var g = new Geolocation();
            g.isLocationEnabled(new Callback(function(isEnabled) {
                if (isEnabled)
                    onEnabled.fire();
                else {

                    if (locationoffModal !== null) {
                        locationoffModal.show();
                        return;
                    }

                    $ionicModal
                        .fromTemplateUrl("templates/locationoff.modal.html", {
                            scope: $scope,
                            animation: 'slide-in-up'
                        })
                        .then(function(modal) {
                            locationoffModal = modal;

                            modal.show();
                            $scope.onTurnOnLocationTapped = function() {
                                modal.hide();
                                g.openLocationSettings();
                                if (onNotEnabled) onNotEnabled.fire();
                                //reset last action in 7 secs
                                $timeout(function() {
                                    $rootScope.ifLocationEnabled(onEnabled, onNotEnabled);
                                }, 7000);
                            };
                        });
                }

            }), $rootScope.onError);
        };

        $rootScope.$on('$cordovaPush:notificationReceived', function(event, notification) {
            if (ionic.Platform.isAndroid()) {
                switch (notification.event) {
                    case 'registered':
                        if (notification.regid.length > 0) {
                            User.getInstance().registerPushNotification(notification.regid);
                        }
                        break;

                    case 'message':
                        // this is the actual push notification. its format depends on the data model from the push server
                        $cordovaDialogs.alert(notification.message, 'Message!');
                        break;

                    case 'error':
                        //alert('GCM error = ' + notification.msg);
                        break;

                    default:
                        //alert('An unknown GCM event has occurred');
                        break;
                }

            } else if (ionic.Platform.isIOS()) {
                if (notification.alert) {
                    $cordovaDialogs.alert(notification.alert, 'Message!');
                }
            }
        });


        Settings.Download(new Callback(function() {
            $rootScope.onContactUs = function() {
                $cordovaDialogs.confirm("Need Help? Don't hesitate to call us now", 'Customer Support', ['Call', 'Cancel'])
                    .then(function(buttonIndex) {
                        var btnIndex = buttonIndex;
                        if (btnIndex === 1) {
                            plugins.CallNumber.callNumber(function() {}, function(e) {
                                $rootScope.onError.fire(new Error("Error while contacting customer support, try again later", true, true));
                            }, Settings.getInstance().e_number);
                        }
                    });
            };
        }));

        if (User.IsStored()) {

            var onError = new Callback(function(e) {
                $rootScope.onError.fire(e);
                $timeout(function() {
                    $state.go('landing');
                }, 4000);
            });
            Mode.FindAll(new Callback(function() {
                User.getInstance().signin(new Callback(function() {
                    User.getInstance().store();
                    User.InitPushNotification();
                    User.getInstance().ping();
                    User.getInstance().sync(new Callback(function(request) { //on progress
                        $timeout(function() {
                            $state.go('menu.hailrequest', {
                                request: request
                            });
                        }, 4000);
                    }), new Callback(function(request) { // on confirmed
                        $timeout(function() {
                            $state.go('menu.requestconfirmed', {
                                request: request
                            });
                        }, 4000);
                    }), new Callback(function() { //no old synced data
                        $timeout(function() {
                            $state.go('menu.hailrequest');
                        }, 4000);
                    }), onError);

                }), null, onError);
            }), onError);

        } else {
            $timeout(function() {
                $state.go("landing");
            }, 7000);
        }

    }
]);

controllers.controller('HomeController@landing', [
    '$scope', '$state', '$stateParams', '$translate', '$ionicPlatform', '$cordovaDialogs',
    '$rootScope', 'Callback', 'Geolocation', '$ionicModal', '$timeout', 'Settings', '$ionicLoading',
    'User', '$ionicHistory', 'Mode', '$cordovaFacebook', 'Util',
    function($scope, $state, $stateParams, $translate, $ionicPlatform, $cordovaDialogs,
        $rootScope, Callback, Geolocation, $ionicModal, $timeout, Settings, $ionicLoading,
        User, $ionicHistory, Mode, $cordovaFacebook, Util) {
        'use strict';



        var languageConfig = {
            title: "Select a Language",
            items: [{
                text: "English",
                value: "en"
            }, {
                text: "Arabic",
                value: "ar"
            }],
            doneButtonLabel: "Done",
            cancelButtonLabel: "Cancel"
        };





        $scope.onLanguageTapped = function() {
            if (typeof cordova === 'undefined') {
                $translate.use('en');
                $state.go("authenticate");
                return;
            }

            plugins.listpicker.showPicker(languageConfig, function(lang) {
                if (lang === 'en')
                    $scope.language = 'ENGLISH';
                else if (lang === 'ar')
                    $scope.language = 'العربية';

                $translate.use(lang);

                $state.go("authenticate");
            }, function() {});
        };

    }
]);

controllers.controller('HomeController@sidemenu', [
    '$scope', '$state', '$stateParams', '$ionicHistory', '$timeout', '$cordovaDialogs',
    'Settings', '$rootScope', 'Error',
    function($scope, $state, $stateParams, $ionicHistory, $timeout, $cordovaDialogs,
        Settings, $rootScope, Error) {
        'use strict';

        $scope.menuWidth = window.innerWidth;


        $scope.onBackTapped = function() {
            if ($ionicHistory.backView().stateName === "menu.hailrequest") {
                $ionicHistory.clearCache();
                $timeout($ionicHistory.goBack, 50);
            } else {
                $ionicHistory.goBack();
            }
        };

    }
]);
