﻿

/**************************************/
/*Application                         */
/**************************************/


extend(saffire, {
    admin: {
        angular: {

            //shared angular application
            app: angular.module("SaffireAngularApp", ['ngAnimate', 'ui.tree', 'pasvaz.bindonce', 'ui.sortable', 'ngSanitize', 'ngCookies', 'ngMask']).config(['$compileProvider', function ($compileProvider) {
                $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|javascript):/);
            }])
                
        

        }
    }
}
);

saffire.admin.angular.app.config(['$controllerProvider', '$provide', '$locationProvider',
function ($controllerProvider, $provide, $locationProvider) {

    $locationProvider.html5Mode({
        enabled: true,
        requireBase: false,
        rewriteLinks: false
    });

    $provide.factory('registerController',
      function registerControllerFactory() {
          // Params:
          //   constructor: controller constructor function, optionally
          //     in the annotated array form.
          return function registerController(name, constructor) {
              // Register the controller constructor as a service.
              $provide.factory(name + 'Factory', function () {
                  return constructor;
              });
              // Register the controller itself.
              $controllerProvider.register(name, constructor);
          };
      });

}]);

saffire.admin.angular.app.run(['$rootScope', '$templateCache', function ($rootScope, $templateCache) {
    $rootScope.siteBaseURL = SITEBASEURL;
    $rootScope.externalSiteBaseURL = EXTERNALSITEBASEURL;
    $rootScope.externalSiteTheme = EXTERNALSITETHEME;
    $rootScope.clientResourcePath = CLIENTRESOURCEPATH;
    $rootScope.clientThemePath = CLIENTTHEMEPATH;
    $rootScope.fullClientThemePath = FULLCLIENTTHEMEPATH;
    $rootScope.fileServerUploadURL = fileServerUploadURL;
    $rootScope.fileServerImagesURL = fileServerImagesURL;
    $rootScope.fileServerFilesURL = fileServerFilesURL;

    $rootScope.refreshTopNavigation = function () {
        __doPostBack("up_adminTopNavigation", "");
    }

    $templateCache.removeAll();

}]);

saffire.admin.angular.app.filter("sanitize", ['$sce', function ($sce) {
    return function (htmlCode) {
        return $sce.trustAsHtml(htmlCode);
    }
}]);

/**
 * Description:
 *     removes white space from text. useful for html values that cannot have spaces
 * Usage:
 *   {{some_text | nospace}}
 */
saffire.admin.angular.app.filter('nospace', function () {
    return function (value) {
        return (!value) ? '' : value.replace(/ /g, '');
    };
});

/**************************************/
/*Services                            */
/**************************************/

saffire.admin.angular.app.service('saffire.admin.angular.cardTokenizationService', ['$http', '$rootScope', 'saffire.admin.angular.webServices', 'saffire.admin.angular.webPoster', 'saffire.admin.angular.editorService', function ($http, $rootScope, webServices, webPoster, editorService) {
    
    var createCardTokenizationResponse = function () {

        return {
            isValid: false,
            token: '',
            cardBrand: '',
            cardExpMonth: null,
            cardExpYear: null,
            cardLastFour: null,
            cardFirstSix:null,
            cardAddressZip:null,
            errorMessages: []
        }

    }

    var setGetCardTokenFunction = function (func) {

        getCardTokenFunction = func

    }

    var getCardToken = function () {

        if (getCardTokenFunction) {
            return getCardTokenFunction();
        }

    }

    var setResetCardDetailsFunction = function (func) {

        resetCardDetailsFunction = func

    }

    var resetCardDetails = function () {

        if (resetCardDetailsFunction) {

            resetCardDetailsFunction();

        }

    }

    return ({
        createCardTokenizationResponse: createCardTokenizationResponse,
        setGetCardTokenFunction: setGetCardTokenFunction,
        setResetCardDetailsFunction: setResetCardDetailsFunction,
        resetCardDetails: resetCardDetails,
        getCardToken: getCardToken
    })
}])

saffire.admin.angular.app.service('saffire.admin.angular.webServices', ['$http', function webServices($http) {

    //Public API

    return ({
        getWebRequest: getWebRequest,
        getSynchronousRequest: getSynchronousRequest
    });

    //Public Functions

    function getWebRequest(url) {

        var request = $http({
            method: "get",
            url: url,
            params: {},
            data: {}
        })

        return (request.then(handleSuccess, handleError))

    }

    function getSynchronousRequest(url) {
        var request = new XMLHttpRequest();
        request.open('GET', url, false);
        request.send(null);

        return request;

        //if (request.status === 200) {
        //}
    }

    //Private Functions

    function handleSuccess(response) {
        return response.data;
    }

    function handleError(response) {
        return response.data;
    }

}]);

saffire.admin.angular.app.service('saffire.admin.angular.webPoster', ['$http', function webPoster($http) {

    //Public API

    return ({
        postWebRequest: postWebRequest,
        getPostWebRequest: getPostWebRequest
    });

    //Public Functions

    function postWebRequest(url, postJsonString) {

        var request = $http({
            method: "post",
            url: url,
            params: {},
            data: postJsonString
        })

        return (request.then(handleSuccess, handleError))

    }

    function getPostWebRequest(url, postJsonString) {

        var request = $http({
            method: "post",
            url: url,
            params: {},
            data: postJsonString
        })

        return request;

    }

    //Private Functions

    function handleSuccess(response) {
        return response.data;
    }

    function handleError(response) {
        return response.data;
    }

}]);

saffire.admin.angular.app.service('saffire.admin.angular.fileService', [function fileService() {

    //Public API

    return ({
        getFileExtension: getFileExtension,
    });

    //Public Functions

    function getFileExtension(fileName) {

        if (fileName) {
            if (fileName.indexOf('.') > -1) {
                return '.' + fileName.split('.').pop()
            }
        }

        return ''
    }

}]);

saffire.admin.angular.app.service('saffire.admin.angular.dateTimeService', [function dateTimeService() {

    //Public API

    return ({
        getDateRangeString: getDateRangeString,
        getPlainDateRangeString: getPlainDateRangeString,
		getTimeRangeString: getTimeRangeString,
        getTimeFromInteger: getTimeFromInteger,
        getDateString: getDateString,
        getDateTimeString: getDateTimeString
    });

    //Public Functions

    function monthAbbr() {
        return ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];
    }

    function getDateTimeString(date, time, isEndDate) {
        time = (time == '' ? '' : getTimeFromInteger(time).string);
        var dateTime = new Date(date + ' ' + time);

        if (isEndDate && time == '') {
            // add one day beacuse we have no time 
            dateTime.setDate(dateTime.getDate() + 1);

        }
        return dateTime;

    }

    function getTimeFromInteger(time) {
        var hour = 0
        var min = 0
        var hour24 = 0
        var ampm = 'AM'

        var timeString = time.toString()
        var timeLength = timeString.length

        if (timeLength == 4) {
            hour = parseInt(timeString.substring(0, 2))
            min = parseInt(timeString.substring(2, 4))
        }
        else if (timeLength == 3) {
            hour = parseInt(timeString.substring(0, 1))
            min = parseInt(timeString.substring(1, 3))
        }
        else if (timeLength < 3) {
            hour = 0
            min = parseInt(timeString)
        }

        hour24 = hour

        if (hour == 0) {
            hour = 12;
        }
        else if (hour >= 12) {
            ampm = 'PM';
            if (hour > 12) {
                hour = hour - 12;
            }

        }

        return {
            hour: hour,
            hour24: hour24,
            min: min,
            ampm: ampm,
            string: hour.toString() + ':' + pad(min, 2) + ' ' + ampm
        }

    }

    function pad(num, size) {
        var s = num + "";
        while (s.length < size) s = "0" + s;
        return s;
    }

    function getDateString(date, dateIsMonthBased) {
        if (date) {
            if (dateIsMonthBased) {
                return monthAbbr()[date.getMonth()] + ' ' + date.getFullYear().toString();
            } else {
                return monthAbbr()[date.getMonth()] + ' ' + date.getDate().toString() + ', ' + date.getFullYear().toString();
            }
        }

        return '';
    }

    function getPlainDateRangeString(startDate, startDateIsMonthBased, endDate, endDateIsMonthBased) {

        var startDateString = '', endDateString = '';

        if (startDate) {
            var dateParts = startDate.split("/");

            if (startDateIsMonthBased) {
                startDateString = monthAbbr()[parseInt(dateParts[0]) - 1] + ' ' + parseInt(dateParts[2]);
            } else {
                startDateString = monthAbbr()[parseInt(dateParts[0]) - 1] + ' ' + parseInt(dateParts[1]) + ', ' + parseInt(dateParts[2]);
            }
        }

        if (endDate) {
            var dateParts = endDate.split("/");

            if (endDateIsMonthBased) {
                endDateString = monthAbbr()[parseInt(dateParts[0]) - 1] + ' ' + parseInt(dateParts[1]);
            } else {
                endDateString = monthAbbr()[parseInt(dateParts[0]) - 1] + ' ' + parseInt(dateParts[1]) + ', ' + parseInt(dateParts[2]);
            }
        }

        if (valueIsNullOrUndefinedOrEmpty(startDateString)) {
            return ''
        }

        return startDateString + " - " + endDateString;
    }

    function getDateRangeString(startDate, startDateIsMonthBased, endDate, endDateIsMonthBased) {

        var abbr = monthAbbr();

        //Make sure we are dealing with dates
        if (startDate) {
            if (typeof startDate.getMonth !== 'function') {
                startDate = new Date(startDate);
                startDate = new Date(startDate.setMinutes(startDate.getMinutes() + startDate.getTimezoneOffset()));
            }
        }

        if (endDate) {
            if (typeof endDate.getMonth !== 'function') {
                endDate = new Date(endDate);
                endDate = new Date(endDate.setMinutes(endDate.getMinutes() + endDate.getTimezoneOffset()));
            }
        }

        //strip off time components
        if (startDate) {
            startDate = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
        }

        if (endDate) {
            endDate = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
        }

        //remove invalid dates

        if (startDate && endDate) {
            if (endDate < startDate) {
                endDate = null;
            }
            else if (startDate > endDate) {
                startDate = null;
            }
        }

        //remove start and end dates if they are contained within the others month long date
        if (startDate &&
            startDateIsMonthBased &&
            endDate &&
            endDate < new Date(startDate.getFullYear(), startDate.getMonth(), 1).setMonth(startDate.getMonth() + 1)) {
            endDate = null;
        }

        if (endDate &&
            endDateIsMonthBased &&
            startDate &&
            startDate > new Date(endDate.getFullYear(), endDate.getMonth(), 1)) {
            startDate = null;
        }

        //construct string
        if (!startDate || !endDate) {
            if (!startDate && !endDate) {
                return '';
            } else if (!startDate) {
                return getDateString(endDate, endDateIsMonthBased);
            } else {
                return getDateString(startDate, startDateIsMonthBased);
            }
        } else {
            if (startDate.getFullYear() == endDate.getFullYear() && startDate.getMonth() == endDate.getMonth() && startDate.getDate() == endDate.getDate()) {
                return getDateString(startDate, startDateIsMonthBased || endDateIsMonthBased ? true : false);
            }
            else if (startDate.getFullYear() == endDate.getFullYear()) {
                if (startDateIsMonthBased && endDateIsMonthBased) {
                    if (startDate.getMonth() == endDate.getMonth()) {
                        return abbr[startDate.getMonth()] + ' ' + startDate.getFullYear();
                    } else {
                        return abbr[startDate.getMonth()] + ' - ' + abbr[endDate.getMonth()] + ' ' + endDate.getFullYear();
                    }
                }
                else if (startDateIsMonthBased) {
                    return abbr[startDate.getMonth()] + ' - ' + abbr[endDate.getMonth()] + ' ' + endDate.getDate() + ', ' + endDate.getFullYear();
                }
                else if (endDateIsMonthBased) {
                    return abbr[startDate.getMonth()] + ' ' + startDate.getDate() + ' - ' + abbr[endDate.getMonth()] + ' ' + endDate.getFullYear();
                }
                else {
                    return abbr[startDate.getMonth()] + ' ' + startDate.getDate() + ' - ' + abbr[endDate.getMonth()] + ' ' + endDate.getDate() + ', ' + endDate.getFullYear();
                }
            }
            else {
                if (startDateIsMonthBased && endDateIsMonthBased) {
                    return abbr[startDate.getMonth()] + ' ' + startDate.getFullYear() + ' - ' + abbr[endDate.getMonth()] + ' ' + endDate.getFullYear();
                }
                else if (startDateIsMonthBased) {
                    return abbr[startDate.getMonth()] + ' ' + startDate.getFullYear() + ' - ' + abbr[endDate.getMonth()] + ' ' + endDate.getDate() + ', ' + endDate.getFullYear();
                }
                else if (endDateIsMonthBased) {
                    return abbr[startDate.getMonth()] + ' ' + startDate.getDate() + ', ' + startDate.getFullYear() + ' - ' + abbr[endDate.getMonth()] + ' ' + endDate.getFullYear();
                }
                else {
                    return abbr[startDate.getMonth()] + ' ' + startDate.getDate() + ', ' + startDate.getFullYear() + ' - ' + abbr[endDate.getMonth()] + ' ' + endDate.getDate() + ', ' + endDate.getFullYear();
                }
            }
        }

    }

    function getTimeRangeString(startTime, endTime) {
        if (valueIsNullOrUndefinedOrEmpty(startTime) && valueIsNullOrUndefinedOrEmpty(endTime))
            return "";

        else if (valueIsNullOrUndefinedOrEmpty(startTime))
            return getTimeFromInteger(endTime).string;

        else if (valueIsNullOrUndefinedOrEmpty(endTime))
            return getTimeFromInteger(startTime).string;

        else {
            if (startTime != endTime)
                return getTimeFromInteger(startTime).string + " - " + getTimeFromInteger(endTime).string;
            else
                return getTimeFromInteger(startTime).string;
        }
    }
}]);

saffire.admin.angular.app.service('saffire.admin.angular.fileServerService', ['$http', '$rootScope', 'saffire.admin.angular.fileService', function fileServerService($http, $rootScope, fileService) {

    //Public API

    return ({
        uploadFile: uploadFile,
        getFileAbsolutePath: getFileAbsolutePath
    });

    //Public Functions

    function getFileAbsolutePath(fileName, isImage) {

        if (isImage) {
            return $rootScope.fileServerImagesURL + '&i=' + fileName
        }
        else {
            return $rootScope.fileServerFilesURL + '&f=' + fileName
        }

    }

    function uploadFile(fileName, fileData, mimeType, isImage) {

        var url = $rootScope.fileServerUploadURL
        var queryParams = [];

        //change url to https if needed
        if (document.location.protocol == 'https:' && url.indexOf('https:') < 0) {
            url = url.replace('http:', 'https:')
        }

        //fileName
        //remove illegal characters
        fileName = fileName.replace(/[^\w\)\(\.]/gi, '')
        fileExtension = fileService.getFileExtension(fileName);

        //give a static filename just in case it was all illegal chars
        if (fileName.replace(fileExtension, '') == '') {
            fileName = 'file' + fileExtension
        }

        queryParams.push('filename=' + fileName)

        //pathKey
        var pathKey = 'ig';
        if (!isImage) {
            pathKey = 'fg';
        }

        queryParams.push('t=' + pathKey)

        if (queryParams.length) {
            url = url + '&' + queryParams.join('&')
        }

        var request = $http({
            method: "post",
            url: url,
            params: {},
            data: fileData,
            transformRequest: []
        })

        return request

    }

}]);

saffire.admin.angular.app.service('saffire.admin.angular.textService', [function textService() {

    //Public API

    return ({
        validateUrl: validateUrl,
        validateEmail: validateEmail,
        validatePhoneNumber: validatePhoneNumber
    });

    //Public Functions

    function validateEmail(text) {

        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(text);

    }

    function validateUrl(text) {

        var re = /^(http[s]?:\/\/){0,1}(www\.){0,1}[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,5}[\.]{0,1}/;
        return re.test(text);

    }


    function validatePhoneNumber(text) {

        text = text.replace(/\040/gi, ''); //space
        text = text.replace(/-/gi, '');
        text = text.replace(/\(/gi, '');
        text = text.replace(/\)/gi, '');

        return $.isNumeric(text);

    }

}]);

saffire.admin.angular.app.service('saffire.admin.angular.scriptLoaderService', ['$q', function scriptLoaderService($q) {

    //Public API

    var scripts = [];

    return ({
        loadScript: loadScript
    })

    //Private Functions

    function getScriptByScriptPath(scriptPath) {

        var existingScript = $.grep(scripts, function (s) {
            return s.scriptPath == scriptPath
        })

        return existingScript.length ? existingScript[0] : null;

    }

    //Public Functions

    function loadScript(scriptPath) {

        var deffered = $q.defer();

        existingScript = getScriptByScriptPath(scriptPath)

        if (existingScript) {
            //script is already loaded or loading

            if (existingScript.isLoaded) {
                deffered.resolve();
            }
            else {
                existingScript.promises.push(deffered)
            }
        }
        else {
            scripts.push({
                scriptPath: scriptPath,
                isLoaded: false,
                promises: [deffered],
            })

            var head = document.getElementsByTagName('head')[0];
            var script = document.createElement('script');

            script.setAttribute('type', 'text/javascript');
            script.src = scriptPath;

            var onScriptLoaded = function (event) {

                var loadedScriptPath = event.target.src

                var script = getScriptByScriptPath(loadedScriptPath);

                if (script) {
                    script.isLoaded = true;

                    for (p = 0; p < script.promises.length; p++) {
                        script.promises[p].resolve();
                    }
                }
                else {
                    deffered.resolve()
                }

            };

            if (script.addEventListener) { // Standard browsers (including IE9+)
                script.addEventListener('load', onScriptLoaded, false);
            }
            else { // IE8 and below
                script.onreadystatechange = function () {
                    if (script.readyState === 'loaded' || script.readyState === 'complete') {
                        script.onreadystatechange = null;
                        onScriptLoaded();
                    }
                };
            }

            head.appendChild(script);
        }

        return deffered.promise
    };

}]);

saffire.admin.angular.app.service('saffire.admin.angular.pdfPrinterService', ['$q', function pdfPrinterService($q) {

    //Public API

    return ({
        printPDF: printPDF
    })

    //Public Functions

    function printPDF(pdfURL) {

        var deffered = $q.defer();

        if (document.getElementById("pdfIframe"))
            document.getElementById("pdfIframe").remove();

        var ifrm = document.createElement("iframe");

        ifrm.setAttribute('id', 'pdfIframe');
        ifrm.setAttribute("src", pdfURL);
        ifrm.style.visibility = 'hidden';
        ifrm.style.width = '1px';
        ifrm.style.height = '1px';

        document.body.appendChild(ifrm);

        var onIFrameLoaded = function () {
            var newWin = ifrm.contentWindow;
            newWin.document.close();
            newWin.focus();
            newWin.print();

            deffered.resolve();
        };

        if (ifrm.addEventListener) { // Standard browsers (including IE9+)
            ifrm.addEventListener('load', onIFrameLoaded, false);
        }

        return deffered.promise
    };

}]);

saffire.admin.angular.app.service('saffire.admin.angular.urlPrinterService', ['$q', function urlPrinterService($q) {

    //Public API

    return ({
        printURL: printURL
    })

    //Public Functions

    function printURL(URL) {

        var deffered = $q.defer();

        if (document.getElementById("printIframe"))
            document.getElementById("printIframe").remove();

        var ifrm = document.createElement("iframe");

        ifrm.setAttribute('id', 'printIframe');
        ifrm.setAttribute("src", URL);
        ifrm.style.visibility = 'hidden';
        ifrm.style.width = '1px';
        ifrm.style.height = '1px';

        document.body.appendChild(ifrm);

        var onIFrameLoaded = function () {
            var newWin = ifrm.contentWindow;
            newWin.document.close();
            newWin.focus();
            newWin.print();

            deffered.resolve();
        };

        if (ifrm.addEventListener) { // Standard browsers (including IE9+) 
            ifrm.addEventListener('load', onIFrameLoaded, false);
        }

        return deffered.promise
    };

}]);

saffire.admin.angular.app.service('saffire.admin.angular.paginationService', [function paginationService() {

    //Public API

    return ({
        createPaginationObject: createPaginationObject,
        getPagedData: getPagedData
    });

    //Public Functions

    function createPaginationObject(pageSize) {

        return {
            pageIndex: 0,
            pageSize: pageSize,
        }

    }

    function createPaginationObject(pageSize, viewAll) {

        return {
            pageIndex: 0,
            pageSize: pageSize,
            viewAll: viewAll,
            originalPageSize : pageSize
        }
    }

    function getPagedData(records, paginationObject) {

        var startRecordIndex = paginationObject.pageIndex * paginationObject.pageSize;
        var endRecordIndex = startRecordIndex + paginationObject.pageSize;
        if (records.length < endRecordIndex) {
            endRecordIndex = records.length;
        }

        var pagedData = [];

        for (i = startRecordIndex; i < endRecordIndex; i++) {
            pagedData.push(records[i]);
        }

        return pagedData;

    }

}]);

saffire.admin.angular.app.factory('saffire.admin.angular.editorService', ['$interval', function ($interval) {

    var enabled = false;
    var debug = false;
    var showMessageFunction = null;
    var closeMessageFunction = null;
    var broadcastFunction = null;
    var userIsSuperAdmin = false;
    var hasChanges = false;

    var setHasChanges = function (hasChangesFlag) {
        hasChanges = hasChangesFlag;
    }

    var getHasChanges = function () {
        return hasChanges;
    }

    var setShowMessageFunction = function (func) {
        showMessageFunction = func;
    }

    var setCloseMessageFunction = function (func) {
        closeMessageFunction = func
    }

    var showMessage = function (message) {
        showMessageFunction(message);
    }

    var closeMessage = function () {
        closeMessageFunction();
    }

    var setBroadcastFunction = function (func) {
        broadcastFunction = func
    }

    var broadcast = function (name, args) {
        broadcastFunction(name, args);
    }

    var createValidationObject = function () {
        return {
            isValid: true,
            messages: [],
            flags: [],
        }
    }

    var sumValidationObjects = function (validationObjects) {

        var isValid = true;
        var messages = [];
        var flags = [];

        if (validationObjects) {
            $.each(validationObjects, function (voi) {
                var vo = validationObjects[voi];
                if (!vo.isValid) {
                    isValid = false;
                    messages = messages.concat(vo.messages);
                    flags = flags.concat(vo.flags);
                }
            })
        }

        var validationObject = createValidationObject()
        validationObject.isValid = isValid;
        validationObject.messages = messages;
        validationObject.flags = flags;

        return validationObject;
    }

    var sumValidationObjectsWithObject = function (validationObject, validationObjects) {

        var tmp = sumValidationObjects(validationObjects);

        validationObject.isValid = validationObject.isValid && tmp.isValid;
        validationObject.messages = validationObject.messages.concat(tmp.messages);
        validationObject.flags = validationObject.flags.concat(tmp.flags);

    }

    return ({
        enabled: enabled,
        debug: debug,
        showMessage: showMessage,
        setShowMessageFunction: setShowMessageFunction,
        closeMessage: closeMessage,
        setCloseMessageFunction: setCloseMessageFunction,
        setBroadcastFunction: setBroadcastFunction,
        broadcast: broadcast,
        createValidationObject: createValidationObject,
        sumValidationObjects: sumValidationObjects,
        sumValidationObjectsWithObject: sumValidationObjectsWithObject,
        userIsSuperAdmin: userIsSuperAdmin,
        setHasChanges: setHasChanges,
        getHasChanges: getHasChanges,
    })
}])

saffire.admin.angular.app.factory('dialogService', ['$q', '$window', '$timeout', function ($q, $window, $timeout) {

    var openDialog = function (scope, dialogOptions) {

        var modalPromise = $q.defer();

        var defaultOptions = {
            OnCloseCallbackFunction: function (args) {
                scope.$apply(
                    modalPromise.resolve({ args: args })
                )
            },
            Parent: window,
            ScrollFrame: true,
            OnCloseTargetID: '',
            OnCloseEventArg: '',

        }

        angular.extend(defaultOptions, dialogOptions);

        $OpenModal(defaultOptions)

        return modalPromise.promise;

    }

    var closeDialog = function (element) {

        $timeout(function () {

            var dialog = null;
            if (jQuery.type(element) === "string") {
                dialog = $('#' + element);
            } else {
                dialog = element;
            }
            $HideModal(dialog);
        })

    }

    return ({
        openDialog: openDialog,
        closeDialog: closeDialog
    })
}])

/*Google Charts Promise*/
/*This promise will load the google charts library and resolve once the library is fully loaded*/
saffire.admin.angular.app.factory('saffire.admin.angular.googleChartsPromise', ['$rootScope', '$q', '$interval', 'saffire.admin.angular.scriptLoaderService', '$timeout', function googleChartsPromise($rootScope, $q, $interval, scriptLoaderService, $timeout) {

    var apiReady = $q.defer();

    scriptLoaderService.loadScript('https://www.gstatic.com/charts/loader.js').then(function () {
        google.charts.load('current', { packages: ['corechart', 'line', 'geochart'] });
        google.charts.setOnLoadCallback(function () {
            $timeout(function () {
                $rootScope.googleChartsLibraryLoaded = true;
                apiReady.resolve();
            }, 1)
        });
    });

    return apiReady.promise;

}]);

/**************************************/
/*Directives                          */
/**************************************/


/*Google Charts Display Directive*/
saffire.admin.angular.app.directive('sfGoogleChart', ['saffire.admin.angular.googleChartsPromise', '$timeout', function (googleChartsPromise, $timeout) {



    function link(scope, element, attrs) {

        scope.$watch(function () {
            if (scope.chartData) {

                return {
                    data: scope.chartData.data,
                    options: scope.chartData.options,
                    type: scope.chartData.type
                };
            }
        }, function () {
            if (scope.chartData != undefined) {

                googleChartsPromise.then(function () {

                    var chart

                    $timeout(function () {
                        switch (scope.chartData.type) {
                            case 'lineChart':
                                chart = new google.visualization.LineChart(element[0]);
                                break;
                            case 'geoChart':
                                chart = new google.visualization.GeoChart(element[0]);
                                break;
                        }

                        chart.draw(scope.chartData.data, scope.chartData.options);
                    }, 1);

                });
            }

        }, true)
    }

    return {
        scope: {
            chartData: '=chartData'
        },
        link: link

    }
}]);

saffire.admin.angular.app.directive('sfSliderCheck', [function () {

    function link(scope, element, attrs) {

        scope.checkClicked = function () {

            scope.boundProperty = !scope.boundProperty;

            if (scope.onCheckChanged != undefined) {

                scope.onCheckChanged({
                    'commandArgument': scope.commandArgument,
                    'checked': scope.boundProperty
                });

            };

        };

        scope.$watch('boundProperty',
            function () {
                if (scope.boundProperty != undefined) {

                    var sliderBox = element.find('.toggle-slider');

                    if (scope.boundProperty) {
                        sliderBox.css('left', '0px');
                    }
                    else {
                        sliderBox.css("left", "-50%");
                    }

                }

            })
    }

    return {
        restrict: 'E',
        scope: {
            boundProperty: '=',
            commandArgument: '=?',
            onCheckChanged: '=?'
        },
        templateUrl: SITEBASEURL + 'Core/Content/Angular/Templates/sliderCheck.html',
        link: link
    };

}]);

saffire.admin.angular.app.directive('sfDatePickerIcon', [function () {



    function link(scope, element, attrs) {

        scope.todayDate = new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate());

        scope.boundDateText = function () {
            if (scope.boundDate.getDate() == scope.todayDate.getDate() && scope.boundDate.getMonth() == scope.todayDate.getMonth() && scope.boundDate.getFullYear() == scope.todayDate.getFullYear()) {
                return "Today"
            }
            else {
                return (scope.boundDate.getMonth() + 1) + '/' + scope.boundDate.getDate() + '/' + scope.boundDate.getFullYear();
            };
        };

        scope.dateSelected = function (dateText, item) {
            scope.$apply(function () {
                scope.boundDate = new Date(dateText);
            });

            scope.hideDatePicker();
        }

        scope.toggleDatePicker = function () {

            var datePickerContainer = element.find(".dateSelectionContainer");

            if (datePickerContainer.css('display') == 'block') {
                scope.hideDatePicker();
            }
            else {
                scope.showDatePicker();
            }

        };

        scope.showDatePicker = function () {
            var datePickerContainer = element.find(".dateSelectionContainer");
            datePickerContainer.show();

            $(document).delegate('*', 'click', function (e) {
                var elementWithinDateSelector = $(this).hasClass('datePickerContainer') || $(this).parents('.datePickerContainer').length > 0 || $(this).hasClass('ui-datepicker-next') || $(this).hasClass('ui-datepicker-prev') || $(this).hasClass('datePickerTrigger')

                if (!elementWithinDateSelector) {
                    scope.hideDatePicker();
                }
                e.stopPropagation();
            });
        }

        scope.hideDatePicker = function () {
            var datePickerContainer = element.find(".dateSelectionContainer");
            datePickerContainer.hide();
        }

        scope.previousDate = function () {
            scope.boundDate = new Date(scope.boundDate.setDate(scope.boundDate.getDate() - 1))
        };

        scope.nextDate = function () {
            scope.boundDate = new Date(scope.boundDate.setDate(scope.boundDate.getDate() + 1))
        };

        element.find(".dateSelection").datepicker({
            onSelect: scope.dateSelected
            , changeMonth: true
            , changeYear: true
            , yearRange: "1960:c+10"
        });

        scope.$watch('boundDate',
            function () {
                if (scope.boundDate != undefined) {
                    element.find(".dateSelection").datepicker('setDate', scope.boundDate);
                }

            })
    }

    return {
        restrict: 'E',
        scope: {
            boundDate: '=boundDate',
        },
        templateUrl: 'Core/Content/Angular/Templates/sfDatePicker.html',
        link: link
    };

}]);

saffire.admin.angular.app.directive('sfDatePickerText', [function () {

    function link(scope, element, attrs) {

        scope.dateSelected = function (dateText, item) {
            scope.$apply(function () {
                scope.boundDate = new Date(dateText);
            });

        }

        element.datepicker({
            onSelect: scope.dateSelected
            , changeMonth: true
            , changeYear: true
            , yearRange: "1960:c+10"
        });

        scope.$watch('boundDate',
            function () {
                if (scope.boundDate != undefined) {
                    element.datepicker('setDate', scope.boundDate);
                }
            })
    }

    return {
        restrict: 'A',
        scope: {
            boundDate: '=boundDate',
        },
        link: link
    };

}]);

saffire.admin.angular.app.directive('sfPromptOnExit', [function () {

    function link(scope, element, attrs) {

        window.onbeforeunload = function () {
            if (scope.promptOnExit) {
                return "You have pending changes. Are you sure you want to navigate away from this page?";
            }
        }

    }

    return {
        restrict: 'E',
        scope: {
            promptOnExit: '=',
        },
        link: link
    };

}]);

saffire.admin.angular.app.directive('sfProcessingMask', ['$timeout', function ($timeout) {

    function link(scope, element, attrs) {

        var fadeTimeout = 0;
        scope.firstLoad = true;

        if (scope.uiDelay == undefined) {
            scope.uiDelay = 1000
        }

        scope.$watch('enabled',
            function (newVal, oldVal) {

                $timeout.cancel(fadeTimeout);

                if (scope.enabled) {

                    element.css('opacity', 0.0)
                    element.css('display', 'none');
                }
                else {

                    element.css('display', 'block');
                    if (scope.firstLoad) {
                        element.css('opacity', 1.0)
                        scope.firstLoad = false;
                    }
                    else {
                        element.css('opacity', 0.0)


                        fadeTimeout = $timeout(function () {
                            element.animate({
                                opacity: 1.0
                            }, scope.uiDelay, function () {

                            })
                        }, scope.uiDelay)
                    }

                }
            })
    }

    return {
        restrict: 'A',
        scope: {
            enabled: '=',
            uiDelay: '=?'
        },
        link: link

    }
}]);

saffire.admin.angular.app.directive('sfNumberTextInput', ['$filter', function ($filter) {

    function link(scope, element, attrs, ctrl) {

        if (!ctrl) return;

        ctrl.$validators.numberValidator = function (modelValue) {

            var inputIsValid = false;

            if (modelValue) {
                inputIsValid = $.isNumeric(modelValue);

            }
            else {
                inputIsValid = true;
            }

            if (scope.inputIsValid != undefined) {
                scope.inputIsValid = inputIsValid;
            }
            return inputIsValid;
        }
    }

    return {
        require: '?ngModel',
        scope: {
            inputIsValid: '=?'
        },
        link: link,
    };

}]);

saffire.admin.angular.app.filter('zeroFormat', function () {

    return function (input, allowNegative, allowZero) {
        if (valueIsNullOrUndefinedOrEmpty(input))
            return '';

        if (!allowNegative && input < 0) {
            return '';
        }

        if (!allowZero && input == 0) {
            return '';
        }

        return '' + input;
    }
});

saffire.admin.angular.app.directive('sfNumberInput', ['zeroFormatFilter', function (zeroFormatFilter) {

    function link(scope, element, attrs, ctrl) {

        if (!ctrl) return;

        ctrl.$validators.numberValidator = function (modelValue) {

            var inputIsValid = false;

            if (!valueIsNullOrUndefinedOrEmpty(modelValue)) {
                inputIsValid = $.isNumeric(modelValue);

                if (inputIsValid) {
                    if (scope.integerOnly && modelValue.toString().match(/^-?\d+$/) == null) {
                        inputIsValid = false;
                    }
                    else if (scope.integerOnly == undefined && !($.isNumeric(modelValue))) {
                        inputIsValid = false;
                    }
                    else if (!scope.allowNegative && Number(modelValue) < 0) {
                        inputIsValid = false;

                    } else if (!scope.allowZero && modelValue == 0) {
                        inputIsValid = false;
                    }
                }
            }
            else {
                inputIsValid = true;
            }

            if (scope.inputIsValid != undefined) {
                scope.inputIsValid = inputIsValid;
            }

            return inputIsValid;
        }

        ctrl.$parsers.push(function (val) {

            if (scope.allowLeadingZero) {
                return val
            }

            if (scope.integerOnly) {
                if (val.toString().match(/^-?\d+$/) != null) {
                    return Number(val.toString().match(/^-?\d+$/).input);
                }
            }
            else if ($.isNumeric(val)) {
                return Number(val);

                if (scope.integerOnly && parsedVal > 0)
                    parsedVal = parseInt(parsedVal, 10);

                return parsedVal;
            }

            return val;
        });

        ctrl.$formatters.push(function (val) {
            return zeroFormatFilter(val, scope.allowNegative, scope.allowZero);
        });
    }

    return {
        require: '?ngModel',
        scope: {
            inputIsValid: '=?',
            integerOnly: '=',
            allowNegative: '=',
            allowZero: '=',
            allowLeadingZero: '='
        },
        link: link,
    };

}]);


saffire.admin.angular.app.directive('sfFormatTextInput', ['saffire.admin.angular.textService', '$filter', function (textService, $filter) {

    function link(scope, element, attrs, ctrl) {

        if (!ctrl) return;

        ctrl.$validators.numberValidator = function (modelValue) {

            var inputIsValid = false;
            var validationMessage = '';

            if (modelValue && scope.format) {

                if (scope.format == 'email') {
                    inputIsValid = textService.validateEmail(modelValue)
                    if (!inputIsValid) {
                        validationMessage = 'A valid email is required'
                    }
                }
                else if (scope.format == 'phone') {
                    inputIsValid = textService.validatePhoneNumber(modelValue)
                    if (!inputIsValid) {
                        validationMessage = 'A valid phone number is required'
                    }
                }
                else if (scope.format == 'url') {
                    inputIsValid = textService.validateUrl(modelValue)
                    if (!inputIsValid) {
                        validationMessage = 'A valid url is required'
                    }
                }

            }
            else {
                inputIsValid = true;
            }

            if (scope.inputIsValid != undefined) {
                scope.inputIsValid = inputIsValid;
            }

            if (scope.inputValidationMessage != undefined) {
                scope.inputValidationMessage = validationMessage;
            }

            return inputIsValid;
        }
    }

    return {
        require: '?ngModel',
        scope: {
            inputIsValid: '=?',
            inputValidationMessage: '=?',
            format: '@'
        },
        link: link,
    };

}]);

saffire.admin.angular.app.directive('sfWatchCounter', ['$timeout', function ($interval) {

    function link(scope, element, attrs) {

        setInterval(function () {

            var root = angular.element(document.getElementsByTagName('body'));
            var watchers = [];

            var f = function (element) {
                if (element.data().hasOwnProperty('$scope')) {
                    angular.forEach(element.data().$scope.$$watchers, function (watcher) {
                        watchers.push(watcher);
                    });
                }

                angular.forEach(element.children(), function (childElement) {
                    f(angular.element(childElement));
                });
            };

            f(root);
            element.html(watchers.length + ' watchers ')
        }, 1000);

    }

    return {
        restrict: 'A',
        link: link
    };

}]);

saffire.admin.angular.app.directive('sfEnter', function () {
    return function (scope, element, attrs) {
        element.bind("keydown keypress", function (event) {
            if (event.which === 13) {
                scope.$apply(function () {
                    scope.$eval(attrs.sfEnter);
                });

                event.preventDefault();
            }
        });
    };
});

saffire.admin.angular.app.directive('sfFocus', function () {
    return function (scope, element, attrs) {
        element.focus();
    };
});

saffire.admin.angular.app.directive('sfTemplateLoader', ['$scope', function ($scope) {

    return {
        restrict: 'E',
        scope: true,
        templateUrl: function (el, attr) {
            if (attr.templateUrl != null) {
                return SITEBASEURL + attr.templateurl;
            }
        }
    }

}]);

saffire.admin.angular.app.directive('sfColorPicker', [function () {

    function link(scope, element, attrs) {

        var $colorPicker = element.find('.colorPicker');
        
        $colorPicker.spectrum({
            flat: true,
            showInput: true,
            showPalette: false,
            preferredFormat: "hex3",
            showButtons: false,
            change: function (newColor) {
                scope.updateColor(newColor.toHexString());
            },
            move: function (newColor) {
                scope.updateColor(newColor.toHexString());
            }
        });

        scope.setSpectrumColor = function (colorHex) {
            $colorPicker.spectrum("set", colorHex);
        }

        if (scope.color) {
            scope.setSpectrumColor(scope.color);
        }

        scope.updateColor = function (colorHex) {

            colorHex = colorHex.replace(/[^A-F0-9]*/gi, "").toUpperCase();

            if (colorHex.length > 6) {
                colorHex = colorHex.substr(0, 6);
            }

            scope.color = colorHex;
            scope.$apply();

        }
        
        scope.clearCurrentColor = function () {

            scope.setSpectrumColor('');
            scope.color = '';

        }

        $('.sp-dragger, .sp-slider', element).mouseup(function () {
            
            var newColor = element.find('.sp-input').val();
            scope.updateColor(newColor);

        });

        scope.$watch('color', function (newVal, oldVal) {

            var scopeColor = '#' + scope.color;
            var spectrumValue = String($colorPicker.spectrum("get"));
            spectrumValue = spectrumValue.toUpperCase();
            
            if (scope.color && scopeColor != spectrumValue) {
                scope.setSpectrumColor(scope.color);
            }

        });

    }

    return {
        restrict: 'E',
        scope: {
            color: "="
        },
        templateUrl: SITEBASEURL + 'core/content/angular/templates/colorPicker.html',
        link: link
    }

}]);

saffire.admin.angular.app.directive('sfTextEditor', [function () {

    function link(scope, element, attrs) {

        var $redactor = element.find('textarea');

        scope.save = function () {
            scope.value = $redactor.redactor('code.get');
        };

        $.Redactor.prototype.spellcheck = function () {
            return {
                init: function () {
                    var that = this;
                    this.$editor.on('input', function () {
                        var e = jQuery.Event('keyup');
                        e.which = 13;
                        that.$editor.trigger(e);
                    });
                }
            };
        };

        $redactor.redactor({
            buttonSource: true
            , buttons: ['html', 'formatting', 'bold', 'italic', 'underline', 'deleted', 'unorderedlist', 'orderedlist', 'outdent', 'indent', 'file', 'link', 'alignment', 'horizontalrule']
            , formatting: ['p', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'button']
            , formattingAdd: [
              {
                  tag: 'button',
                  title: 'Default Button',
                  class: 'button',
                  clear: true
              }
            ]
            , plugins: ['fontsize', 'fontcolor', 'bufferbuttons', 'pasteasplaintext', 'pastefromword', 'spellcheck']
            , deniedTags: ['html', 'head', 'link', 'body', 'applet']
            , minHeight: 200
            , maxHeight: 500
            , replaceDivs: false
            , paragraphize: false
            , linebreaks: true
            , cleanOnPaste: true
            , cleanSpaces: true
            , cleanStyleOnEnter: true
            , pastePlainText: false
            , lang: 'en-custom'
            , syncCallback: function () {
                scope.value = this.code.get();
                scope.$apply()
            }
            , codeKeyupCallback: function () {
                scope.value = this.code.get();
                scope.$apply()
            }
            , initCallback: function () {
                this.$editor.addClass('content');
            }
            , sourceCallback: function () {
                /*visual mode TO HTML mode*/
                this.$box.addClass('HTMLMode');
            }
            , visualCallback: function () {
                /*HTML mode to visual mode*/
                this.$box.removeClass('HTMLMode');
            }
        });

        if (scope.value) {
            $redactor.redactor('insert.set', scope.value, false);
        }

        scope.$watch('value', function (newVal, oldVal) {
            if (scope.value && scope.value != $redactor.redactor('code.get')) {
                $redactor.redactor('insert.set', scope.value, false);
            }
        });

    }

    return {
        restrict: 'E',
        templateUrl: SITEBASEURL + 'core/content/angular/templates/textEditor.html',
        scope: {
            value: '=text',
        },
        link: link
    }

}]);

saffire.admin.angular.app.directive('sfFileUpload', ['saffire.admin.angular.fileServerService', 'saffire.admin.angular.fileService', function (fileServerService, fileService) {


    function link(scope, element, attrs) {

        scope.fileUploadControl = element.find('#file')[0]

        scope.fileName = ''
        scope.mimeType = ''

        if (scope.buttonText == '' || scope.buttonText == undefined) {
            scope.buttonText = "Browse...";
        }

        scope.clickUpload = function () {
            scope.fileUploadControl.click();
        }

        scope.fileUploadControl.onchange = function () {
            var f = scope.fileUploadControl.files[0];
            var r = new FileReader();

            scope.fileName = scope.fileUploadControl.files[0].name;
            scope.mimeType = scope.fileUploadControl.files[0].type;

            r.onloadstart = function (e) {

                //Check File Size
                if (e.lengthComputable && scope.maxFileSize) {
                    if (e.total > scope.maxFileSize * 1000000) {
                        this.abort()
                        scope.fileUploadControl.value = ''
                        scope.$apply(function () {
                            scope.onValidationError({ validationMessage: 'File size is too large. File must be less than ' + scope.maxFileSize.toString() + 'MB' })
                        })
                        return;
                    }
                }

                //Check mime types
                if (scope.allowFileTypes) {
                    var allowedTypes = scope.allowFileTypes.split(',')
                    var fileExtension = fileService.getFileExtension(scope.fileName);
                    var isValid = false;

                    if (allowedTypes.indexOf('audio') > -1) {
                        var mimeTypes = ["audio/basic", "audio/mpeg", "audio/mpeg", "audio/x-aiff", "audio/xwav", "audio/x-m4a"]
                        var fileExtensions = [".au", ".mp3", ".aif", ".m3u", ".wav", ".m4a"]

                        if (mimeTypes.indexOf(scope.mimeType.toLowerCase()) > -1 || fileExtensions.indexOf(fileExtension.toLowerCase()) > -1) {
                            isValid = true;
                        }
                    }

                    if (allowedTypes.indexOf('image') > -1) {
                        var mimeTypes = ["image/jpg", "image/jpeg", "image/gif", "image/png", "image/bmp"]
                        var fileExtensions = [".jpg", ".jpeg", ".gif", ".png", ".bmp", ".tiff", ".tif"]

                        if (mimeTypes.indexOf(scope.mimeType.toLowerCase()) > -1 || fileExtensions.indexOf(fileExtension.toLowerCase()) > -1) {
                            isValid = true;
                        }
                    }

                    if (allowedTypes.indexOf('pdf') > -1) {
                        var mimeTypes = ["application/pdf"]
                        var fileExtensions = [".pdf"]

                        if (mimeTypes.indexOf(scope.mimeType.toLowerCase()) > -1 || fileExtensions.indexOf(fileExtension.toLowerCase()) > -1) {
                            isValid = true;
                        }
                    }

                    if (!isValid) {
                        this.abort()
                        scope.fileUploadControl.value = ''
                        scope.$apply(function () {
                            scope.onValidationError({ validationMessage: 'Invalid file type selected' })
                        })
                        return;
                    }
                }


                scope.$apply(function () {
                    scope.onUploadStart();
                })

            }

            r.onprogress = function (e) {

            }

            r.onloadend = function (e) {
                if (e.total > 0 && e.total == e.loaded) {
                    var data = new Uint8Array(e.target.result)
                    scope.fileUploadControl.value = ''
                    scope.$apply(function () {
                        scope.onFileUpload({
                            fileName: scope.fileName,
                            mimeType: scope.mimeType,
                            data: data
                        })
                    })

                }

            }
            r.readAsArrayBuffer(f);
        }


    }

    return {
        restrict: 'E',
        scope: {
            onFileUpload: '&',
            onUploadStart: '&',
            onValidationError: '&',
            maxFileSize: '@',
            allowFileTypes: '@',
            buttonClass: '@',
            buttonText: '@',
        },
        templateUrl: SITEBASEURL + 'core/content/angular/templates/fileUpload.html',
        link: link
    };

}]);

saffire.admin.angular.app.directive('convertToNumber', function () {
    return {
        require: 'ngModel',
        link: function (scope, element, attrs, ngModel) {
            ngModel.$parsers.push(function (val) {
                return parseInt(val, 10);
            });
            ngModel.$formatters.push(function (val) {
                return '' + val;
            });
        }
    };
});

saffire.admin.angular.app.directive('convertToDate', function () {
    return {
        require: 'ngModel',
        link: function (scope, element, attrs, ngModel) {
            ngModel.$parsers.push(function (val) {
                if (val) {
                    return new Date(val.getFullYear(), val.getMonth(), val.getDate(), 0, 0, 0, 0);
                }
            });

            ngModel.$formatters.push(function (val) {
                var d = new Date(val);
                return new Date(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0, 0);
            });
        }
    };
});

saffire.admin.angular.app.filter('boolean', function () {

    return function (val) {
        if (val == null || val == undefined)
            return false;

        if (val == "1" || val.toString().toLowerCase() == "true" || val == true)
            return true;
        else
            return false;
    }
});

saffire.admin.angular.app.directive('convertToBoolean', ['booleanFilter', function (booleanFilter) {

    function link(scope, element, attrs, ctrl) {

        if (!ctrl) return;

        ctrl.$formatters.push(function (val) {
            return booleanFilter(val);
        });

    }

    return {
        require: '?ngModel',
        link: link
    };

}]);

saffire.admin.angular.app.directive('sfCharacterCounter', [function () {

    function link(scope, element, attrs) {

        var maxChars = 255;
        var format = { 'format': '%1 left' };
        var hide = false;

        if (attrs.maxChars != undefined) {
            maxChars = attrs.maxChars;
        }

        let elementToApplyCharCounter;

        if ($(element).is('input') || $(element).is('textarea'))
            elementToApplyCharCounter = $(element);
        else {
            let childElement = $(element).find('input');
            if (childElement.length === 0)
                childElement = $(element).find('textarea');

            elementToApplyCharCounter = childElement;
        }

        elementToApplyCharCounter.charCounter(maxChars, format, hide);

        if (attrs['ngModel']) {
            scope.$watch(attrs['ngModel'], function (newVal, oldVal) {
                $($(elementToApplyCharCounter)[0].nextElementSibling).html((maxChars - $(elementToApplyCharCounter).val().replace(/(\n)/gm, '\n\n').length) + " left")
            });
        }

        if (attrs['value']) {
            scope.$watch(attrs['value'], function (newVal, oldVal) {
                newValueLength = newVal ? newVal.replace(/(\n)/gm, '\n\n').length : 0;
                $($(elementToApplyCharCounter)[0].nextElementSibling).html((maxChars - newValueLength) + " left")
            });
        }
    }

    return {
        restrict: 'A',
        link: link
    };

}]);

saffire.admin.angular.app.directive('sfElementViewport', [function () {



    function link(scope, element, attrs) {

        //scope.element = element[0];
        scope.offsetWidth = element[0].offsetWidth;
        scope.offsetHeight = element[0].offsetHeight;

        scope.elementViewportClass = '';

        scope.setViewportClass = function () {
            if (scope.elementViewportBreakPoints) {
                for (i = scope.elementViewportBreakPoints.length - 1; i >= 0; i--) {
                    var viewportBreakPoint = scope.elementViewportBreakPoints[i];
                    if (scope.offsetWidth >= viewportBreakPoint.minWidth) {
                        if (scope.elementViewportClass != viewportBreakPoint.class) {

                            if (!scope.$$phase) {
                                scope.$apply(function () {
                                    scope.elementViewportClass = viewportBreakPoint.class;
                                });
                            }
                            else {
                                scope.elementViewportClass = viewportBreakPoint.class;
                            }


                        }
                        break;
                    }
                }
            }
        }

        scope.checkForChanges = function () {
            if (element[0].offsetWidth !== scope.offsetWidth || element[0].offsetHeight !== scope.offsetHeight) {

                scope.offsetWidth = element[0].offsetWidth;
                scope.offsetHeight = element[0].offsetHeight;

                scope.setViewportClass();
            }
        }

        // Listen to the window's size changes
        window.addEventListener('resize', scope.checkForChanges);

        // Listen to changes on the elements in the page that affect layout
        var observer = new MutationObserver(scope.checkForChanges);
        observer.observe(document.body, {
            attributes: true,
            childList: true,
            characterData: true,
            subtree: true
        });

        scope.setViewportClass();
    }

    return {
        restrict: 'A',
        link: link
    };

}]);

saffire.admin.angular.app.directive("sfCarousel", ['$timeout', function ($timeout) {
    return {
        restrict: 'E',
        transclude: false,
        link: function (scope, thisCarousel) {
            scope.initCarousel = function (element) {
                var defaultOptions = {
                    autoplay: 2500
                };

                var customOptions = scope.$eval($(element).attr('data-options'));
                for (var key in customOptions) {
                    defaultOptions[key] = customOptions[key];
                }
                scope.editorSwiper = new Swiper(element, defaultOptions);
            };

            scope.$watch('! ($parent.module.InEditMode || false)', function (newValue, oldValue) {
                if (newValue != undefined) {
                    if (!newValue) {
                        if (scope.editorSwiper) {
                            scope.editorSwiper.update(false);
                        }

                    }
                }

            }, true);

            scope.$on('StructureChanged', function (event, arge) {
                $timeout(function () {
                    if (scope.editorSwiper) {
                        scope.editorSwiper.update(false);
                    }
                }, 400);
            });

            scope.$on('EditorSizeChanged', function (event, arge) {
                $timeout.cancel(scope.editorSize);
                scope.editorSize = $timeout(scope.updateSlider, 400);
            });

            scope.editorSize;

            scope.updateSlider = function () {
                scope.editorSwiper.update(false);
            };
        }
    };
}])
.directive('sfCarouselItem', [function () {
    return {
        restrict: 'A',
        transclude: false,
        link: function (scope, element) {
            // wait for the last item in the ng-repeat then call init
            if (scope.$last) {
                scope.initCarousel(element.parents('.swiper-container'));
            }
        }
    };
}]);


saffire.admin.angular.app.directive('sfMessenger', ['$interval', '$timeout', function ($interval, $timeout) {

    function link(scope, element, attrs) {

        scope.container = element.closest('.AngularAppContainer')

        scope.showMessage = function (settings) {

            var messageSettings = {
                message: '',
                buttons: [],
                autoHide: false,
                timeoutInterval: 1000,
                customCSSClass: '',
                beforeOpenFunc: function () { },
                afterCloseFunc: function () { }
            }

            messageSettings = $.extend(messageSettings, settings);

            scope.messenger = messageSettings;
            scope.messengerActive = true;

            if (messageSettings.autoHide) {

                $timeout(function () {
                    scope.closeMessage();
                }, messageSettings.timeoutInterval)
            }

            if (messageSettings.beforeOpenFunc) {
                messageSettings.beforeOpenFunc();
            }

            scope.container.addClass('messengerActive');
        }

        scope.closeMessage = function () {
            scope.messengerActive = false;

            scope.container.removeClass('messengerActive');

            if (scope.messenger.afterCloseFunc) {
                scope.messenger.afterCloseFunc();
            }
        }

    };

    return {
        restrict: 'E',
        templateUrl: SITEBASEURL + 'core/content/angular/messenger.html',
        link: link

    }

}]);

saffire.admin.angular.app.directive('sfGeneralDateRange', function () {

    return {
        scope: {
            hideDateLabel: '=',
            hideTimeLabel: '=',
            showStartDateArrows: '=',
            hideDateClear: '=',
            allowMonthSelection: '=',
            showEndDate: '=',
            allowTimeSelectionForSingleDayOnly: '=',
            startDate: '=',
            endDate: '=',
            startTime: '=',
            endTime: '=',
            showEndTime: '=',
            month: '=',
            year: '='
        },
        templateUrl: SITEBASEURL + 'core/content/angular/templates/dateRange.html'
    }

});

saffire.admin.angular.app.directive('sfGeneralDatePicker', function () {

    function link(scope, element, attrs) {

        var month = "", year = "";

        //Global variables
        var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];

        function initializeDatePicker() {

            attachDatePicker('start');

            if (scope.showEndDate) {

                attachDatePicker('end');
            }
            else {
                element.find('.dateDisplay.start').addClass('noEndDate')
            }


            loadDatePicker('start')

            if (scope.showEndDate) {

                loadDatePicker('end')
            }

            if (!scope.showEndDate) {
                element.find('.datePickerContainer.end').hide();
                element.find('#div_datePickerHyphen').hide();
            }

            if (!scope.$root.datePickerDelegateAdded) {

                scope.$root.datePickerDelegateAdded = true;

                $(document).delegate('*', 'click', function (e) {
                    var elementWithinDateSelector = $(this).hasClass('datePickerContainer') || $(this).parents('.datePickerContainer').length > 0 || $(this).hasClass('ui-datepicker-next') || $(this).hasClass('ui-datepicker-prev')

                    if (!elementWithinDateSelector) {
                        hideAllDatePickers();
                    }



                    e.stopPropagation();
                });

            }

        }

        function setStartDate(startDate) {
            setPickerDate(startDate, 'start');
        }

        function setEndDate(endDate) {
            setPickerDate(endDate, 'end');
        }

        function getStartDate(id) {
            return getPickerDate('start');
        }

        function getEndDate(id) {
            return getPickerDate('end');
        }

        function setPickerDate(value, startEnd, ignore) {

            try {

                if (isDate(value) || value.toString() == '') {
                    if (startEnd == 'start') {
                        scope.startDate = value
                        scope.month = ''

                        if (scope.showEndDate) {
                            element.find('.datePickerContainer.end').show();
                            element.find('#div_datePickerHyphen').show();
                        }
                    } else {
                        scope.endDate = value
                    }
                } else {

                    var monthYear = value.toString().split('-');

                    scope.startDate = '';
                    scope.endDate = '';
                    month = monthYear[0];
                    year = monthYear[1];

                    element.find('.datePickerContainer.end').hide();
                    element.find('#div_datePickerHyphen').hide();
                }


                var dateDisplay = element.find('.dateDisplay.' + startEnd);

                if (value.toString() == '') {
                    dateDisplay.html('MM/DD/YY');

                    if (startEnd == 'start') {
                        clearSelection('end');
                        setPickerDate('', 'end');
                        setDisable('end');
                    }
                } else if (isDate(value)) {

                    dateDisplay.html(value);

                    if (startEnd == 'start') {
                        setEnabled('end');

                        //Reset the end date if it is earlier that the selected start date
                        if (getPickerDate('end') != null) {
                            var endDate = new Date(getPickerDate('end').toString());
                            var startDate = new Date(value.toString());

                            if (startDate > endDate) {
                                setPickerDate('', 'end');
                                clearSelection('end');
                            }
                        }
                    }

                } else {
                    var monthYear = value.toString().split('-');

                    dateDisplay.html(monthNames[parseInt(monthYear[0], 10) - 1]);
                    setPickerDate('', 'end');
                    setDisable('end');
                }

                element.find(".dateSelection.start").datepicker('refresh');
                element.find(".dateSelection.end").datepicker('refresh');

                if (scope.month != '' && scope.month.val != null && startEnd == 'start') {


                }

                if (scope.allowTimeSelectionForSingleDayOnly && scope.associatedTimePickerContainerID) {

                    var startDate = getPickerDate('start');
                    var endDate = getPickerDate('end');

                    if (startDate && endDate) {
                        if ((new Date(endDate) - new Date(startDate)) == 0) {
                            $('#' + scope.associatedTimePickerContainerID).css('display', 'block');
                        }
                        else {
                            $('#' + scope.associatedTimePickerContainerID).css('display', 'none');
                        }
                    }
                    else {
                        $('#' + scope.associatedTimePickerContainerID).css('display', 'none');
                    }

                }


            } catch (e) {

            }

        }

        function loadDatePicker(startEnd) {

            var pickerLoadDate = '';

            if (month != '' && startEnd == 'start') {
                pickerLoadDate = month + '-' + year;
            }
            else {
                if (startEnd == 'start') {
                    pickerLoadDate = scope.startDate;
                }
                else {
                    pickerLoadDate = scope.endDate;
                }
            }


            if (pickerLoadDate == '') {
                if (startEnd == 'start') {
                    setDisable('end')
                }
            }
            else {
                if (isDate(pickerLoadDate)) {
                    element.find(".dateSelection." + startEnd).datepicker("setDate", pickerLoadDate);
                }
                else {
                    clearSelection(startEnd)
                }


                if (startEnd == 'start') {
                    setEnabled('end')
                }
            }

            setPickerDate(pickerLoadDate, startEnd, true)

        }

        function getPickerDate(startEnd) {

            var returnDate


            if (month != '' && startEnd == 'start') {
                returnDate = month + '-' + year;
            }
            else {
                if (startEnd == 'start') {
                    returnDate = scope.startDate;
                }
                else {
                    returnDate = scope.endDate;
                }
            }

            if (returnDate == '') {
                returnDate = null;
            }

            return returnDate;
        }

        function getStartEnd(item) {

            var startEnd

            if (element.find(item).hasClass('start')) {
                startEnd = 'start'
            }
            else {
                startEnd = 'end'
            }

            return startEnd

        }

        function showDatePicker(startEnd) {

            hideAllDatePickers();

            var dateSelectionContainer = element.find('.dateSelectionContainer.' + startEnd);

            dateSelectionContainer.show();

            if (startEnd == 'start') {


                if (scope.month != '' && scope.month != null) {
                    $(".dateSelection.start").find('.ui-datepicker-month').val((parseInt(month, 10) - 1).toString())
                    $(".dateSelection.start").find('.ui-datepicker-month').change();
                    $(".dateSelection.start").find('.ui-datepicker-year').val(year.toString());
                    $(".dateSelection.start").find('.ui-datepicker-year').change();
                }

            }
            else {

                //Put the end picker in the same month as the start date if no end date has been selected
                if (scope.endDate == '' || scope.endDate == null) {

                    if (scope.startDate != '' && scope.startDate != null && isDate(scope.startDate)) {

                        var startDate = new Date(scope.startDate);

                        $(".dateSelection.end").find('.ui-datepicker-month').val(startDate.getMonth().toString());
                        $(".dateSelection.end").find('.ui-datepicker-month').change();
                        $(".dateSelection.end").find('.ui-datepicker-year').val(startDate.getFullYear().toString());
                        $(".dateSelection.end").find('.ui-datepicker-year').change();

                    }
                }
            }
            $('.ui-datepicker-header').addClass('color3');

            ensureElementIsInView(dateSelectionContainer);
        }

        function hideDatePicker(startEnd) {

            var dateSelectionContainer = element.find('.dateSelectionContainer.' + startEnd);

            dateSelectionContainer.hide();

        }

        function hideAllDatePickers() {

            var datePickers = $('.datePickerContainerContainer');

            $.each(datePickers, function (index, elem) {

                var dateSelectionContainerStart = $(elem).find('.dateSelectionContainer.start');
                dateSelectionContainerStart.hide();

                var dateSelectionContainerEnd = $(elem).find('.dateSelectionContainer.end');
                dateSelectionContainerEnd.hide();
            });
        }

        function clearSelection(startEnd) {

            element.find(".dateSelection." + startEnd).datepicker("setDate", null);

        }

        function setDisable(startEnd) {

            var dateDisplay = element.find('.dateDisplay.' + startEnd);
            dateDisplay.removeClass('dateDisplay_enabled');
            dateDisplay.addClass('dateDisplay_disabled');

        }

        function setEnabled(startEnd) {

            var dateDisplay = element.find('.dateDisplay.' + startEnd);
            dateDisplay.removeClass('dateDisplay_disabled');
            dateDisplay.addClass('dateDisplay_enabled');

        }

        function isDate(testDate) {
            try {
                return !(new Date(testDate.toString()) == 'Invalid Date' || new Date(testDate.toString()) == 'NaN')
            }
            catch (e) {
                return false;
            }
        }

        function getDateText(d) {
            var dd = d.getDate();
            var mm = d.getMonth() + 1; //January is 0!
            var yyyy = d.getFullYear();

            if (dd < 10) {
                dd = '0' + dd;
            }

            if (mm < 10) {
                mm = '0' + mm;
            }

            return mm + '/' + dd + '/' + yyyy;

        }

        scope.setDatePickerBackDate = function () {
            //Control Events

            var startDate = new Date(getPickerDate('start'));
            startDate.setDate(startDate.getDate() - 1);
            startDate = new Date(startDate);

            setPickerDate(getDateText(startDate), 'start');
        }

        scope.setDatePickerForwardDate = function () {

            var startDate = new Date(getPickerDate('start'));
            startDate.setDate(startDate.getDate() + 1);
            startDate = new Date(startDate);

            setPickerDate(getDateText(startDate), 'start');
        }

        scope.dateSelectionClear = function ($event) {

            var startEnd = getStartEnd($event.target);

            clearSelection(startEnd)

            setPickerDate('', startEnd);

            hideDatePicker(startEnd)
        }

        scope.dateSelectionClose = function ($event) {
            var startEnd = getStartEnd($event.target);

            hideDatePicker(startEnd);

        }

        scope.dateDisplay = function ($event) {

            var startEnd = getStartEnd($event.target);

            if (startEnd == 'start' || (startEnd == 'end' && scope.startDate.toString() != '')) {

                if (scope.startDate.toString() == '') {
                    scope.dateSelectionClear($event);
                }
                var dateSelectionContainer = element.find('.dateSelectionContainer.' + startEnd);

                if (dateSelectionContainer.css('display') == 'none') {

                    showDatePicker(startEnd);
                }
                else {

                    hideDatePicker(startEnd);
                }
            }
        }

        scope.dateSelectionUseMonth = function () {

            var selectedMonth = parseInt(element.find('.dateSelectionContainer.start').find('.ui-datepicker-month').val(), 10) + 1
            var selectedYear = parseInt(element.find('.dateSelectionContainer.start').find('.ui-datepicker-year').val(), 10)

            setPickerDate(selectedMonth + '-' + selectedYear, 'start');
            clearSelection('start')


            hideDatePicker('start')
        }


        function attachDatePicker(startEnd) {

            var onFunction = onSelect;

            var beforeShowDayFunction
            var onChangeMonthYearFunction

            if (startEnd == 'start') {
                beforeShowDayFunction = beforeShowDayStart;
            }
            else {
                beforeShowDayFunction = beforeShowDayEnd;
            }
            onChangeMonthYearFunction = onChangeMonthYearStart;
            element.find(".dateSelection." + startEnd).datepicker(
            {
                onSelect: onFunction
                , beforeShowDay: beforeShowDayFunction
                , onChangeMonthYear: onChangeMonthYearFunction
                , changeMonth: true
                , changeYear: true
                , yearRange: "2000:c+10"
            });

        }

        //Jquery Override Functions

        function beforeShowDayStart(date) {

            var endDateString = getPickerDate('end');
            var startDateString = getPickerDate('start');

            var returnArray = new Array();

            if (startDateString != null && !isDate(startDateString)) {
                // month is selected
                returnArray[0] = true;
            }
            else {
                if (endDateString == null) {
                    returnArray[0] = true;
                }
                else {
                    var endDate = new Date(endDateString);
                    var startDate = new Date(startDateString);

                    returnArray[0] = true;
                    returnArray[1] = 'potentialDate';

                    if (date > startDate && date < endDate) {
                        returnArray[1] = 'dateRange';
                    }
                    var ssd = datesAreEqual(date, startDate);
                    if (ssd) {
                        returnArray[1] = 'startDate';
                    }
                    var sed = datesAreEqual(date, endDate);
                    if (sed) {
                        returnArray[1] = 'endDate';
                    }

                }
            }


            returnArray[2] = '';

            return returnArray;
        }

        function beforeShowDayEnd(date) {

            var endDateString = getPickerDate('end');
            var startDateString = getPickerDate('start');

            var returnArray = new Array();

            if (startDateString == null) {
                returnArray[0] = true;
            }
            else {
                var startDate = new Date(startDateString);
                if (endDateString == null) {
                    endDateString = startDateString;
                }
                var endDate = new Date(endDateString);

                if (date < startDate) {
                    returnArray[0] = false;
                    returnArray[1] = 'inactiveDate';
                }
                else {
                    returnArray[0] = true;
                    returnArray[1] = 'potentialDate';

                    if (date > startDate && date < endDate) {
                        returnArray[1] = 'dateRange';
                    }
                    var esd = datesAreEqual(date, startDate);
                    if (esd) {
                        returnArray[1] = 'startDate';
                    }
                    var eed = datesAreEqual(date, endDate);
                    if (eed) {
                        returnArray[1] = 'endDate';
                    }

                }

            }

            returnArray[2] = '';

            return returnArray;

        }

        function onChangeMonthYearStart(year, month, item) {
            $('.ui-datepicker-header').addClass('color3');
            setTimeout(function () { $('.ui-datepicker-header').addClass('color3') }, 1);

        }

        function onSelect(dateText, item) {

            scope.$apply(function () {

                var datePickerParent = item.input.parent()

                var startEnd = getStartEnd(datePickerParent)

                setPickerDate(dateText, startEnd)


                hideDatePicker(startEnd)

            })

        }

        //Helper Functions

        function datesAreEqual(date1, date2) {

            var isEqual = false;

            try {
                if (date1.getDate() == date2.getDate() && date1.getMonth() == date2.getMonth() && date1.getFullYear() == date2.getFullYear()) {
                    isEqual = true;
                }
            }
            catch (err) {
                //one of the variables likely wasn't a date
            }

            return isEqual;

        }



        scope.$watch('[startDate,endDate]', function (newVal, oldVal) {

            initializeDatePicker();

        }, true)

    }
    return {
        scope: {
            showStartDateArrows: '=',
            hideDateClear: '=',
            allowMonthSelection: '=',
            showEndDate: '=',
            allowTimeSelectionForSingleDayOnly: '=',
            startDate: '=',
            endDate: '=?',
            year: '=',
        },
        templateUrl: SITEBASEURL + 'core/content/angular/templates/datePicker.html',
        link: link

    }

});

saffire.admin.angular.app.directive('sfGeneralImage', function () {

    function link(scope, element, attrs) {

        scope.btnAddImage = false;
        scope.btnDeleteImage = false;
        scope.addImageControl = false;
        scope.existingFile = false;

        initialize();

        function initialize() {
            if (scope.currentFileModuleDataItemId == null) {
                scope.imagePanel = false;
                scope.existingFile = false;
                scope.btnAddImage = true;
                scope.btnDeleteImage = false;
            }
            else {
                scope.imagePanel = true;
                scope.existingFile = true;
                scope.btnDeleteImage = true;
                scope.btnAddImage = false;
            };
        };

        scope.congfigureToAdd = function () {
            scope.existingFile = false;
            scope.imagePanel = true;
            scope.btnAddImage = false;
            scope.btnDeleteImage = true;
        };

        scope.delete = function () {
            scope.currentFileModuleDataItemId = null;
            initialize();
        };

        scope.cropImage = function () {

            OpenWindowModal('modalUCLoader.aspx?controlPath=~/Core/General/general_cropImage.ascx&overwriteOriginal=false&img= ' + scope.currentFileName + ', 630, 490');
            //, { 'OnClosePostBack': true, 'OnCloseTargetID': 'croppedImage' }
        }


    };

    return {
        scope: {
            currentFileModuleDataItemId: '=',
            currentFileNameWithUrl: '=',
            currentFileName: '=',
            buttonText: '&',
            buttonClass: '&',
            maxFileSize: '&',
            allowFileTypes: '&'
        },
        templateUrl: SITEBASEURL + 'core/content/angular/templates/generalImage.html',
        link: link

    }

});

saffire.admin.angular.app.directive('sfGeneralTimePicker', function () {

    function link(scope, element, attrs) {

        function initializeTimePicker(timePickerID) {

            loadTimePicker('start')

            if (scope.showEndTime) {
                loadTimePicker('end')
            }

            if (!scope.showEndTime) {
                element.find('.timePickerContainer.end').hide();
                element.find('#div_timePickerHyphen').hide();
            }

            if (!scope.$root.timePickerDelegateAdded) {

                scope.$root.timePickerDelegateAdded = true;

                $(document).delegate('*', 'click', function (e) {
                    var elementWithinDateSelector = $(this).hasClass('timePickerContainer') || $(this).parents('.timePickerContainer').length > 0

                    if (!elementWithinDateSelector) {

                        hideAllTimePickers();
                    }

                    e.stopPropagation();
                });
            }

            
        }

        function setPickerTime(value, startEnd) {

            if (startEnd == 'start') {
                scope.startTime = value;
            }
            else {
                scope.endTime = value;
            }

            var timeDisplay = element.find('.timeDisplay.' + startEnd);

            if (value == null || value == '') {
                timeDisplay.html('HH:MM');

                if (startEnd == 'start') {
                    clearPickerSelection('end');
                    setPickerTime('', 'end');
                    setTimePickerDisable('end')
                }
            }
            else {
                if (value.toString().length == 3) {
                    value = '0' + value;
                }
                else if (value.toString().length == 2) {
                    value = '00' + value;
                }
                else if (value.toString().length == 1) {
                    value = '000' + value;
                }

                var pickerMinutes = value.toString().substr(2, 2);
                var pickerHours = parseInt(value.toString().substr(0, 2), 10);

                var pickerTimeDisplay = ''

                if (pickerHours == 0) {
                    pickerTimeDisplay = '12:' + pickerMinutes + ' AM'
                }
                else if (pickerHours < 12) {
                    pickerTimeDisplay = pickerHours + ':' + pickerMinutes + ' AM'
                }
                else if (pickerHours == 12) {
                    pickerTimeDisplay = '12:' + pickerMinutes + ' PM'
                }
                else {
                    pickerTimeDisplay = (pickerHours - 12) + ':' + pickerMinutes + ' PM'
                }

                timeDisplay.html(pickerTimeDisplay);

                if (startEnd == 'start') {
                    setTimePickerEnabled('end')
                }
            }

        }

        function loadTimePicker(startEnd) {

            var pickerLoadTime = ''

            if (startEnd == 'start') {
                pickerLoadTime = scope.startTime ? scope.startTime.toString() : '';
            }
            else {
                pickerLoadTime = scope.endTime ? scope.endTime.toString() : '';
            }

            if (pickerLoadTime != '') {
                if (pickerLoadTime.length == 3) {
                    pickerLoadTime = '0' + pickerLoadTime;
                }
                else if (pickerLoadTime.length == 2) {
                    pickerLoadTime = '00' + pickerLoadTime;
                }
                else if (pickerLoadTime.length == 1) {
                    pickerLoadTime = '000' + pickerLoadTime;
                }
            }

            if (pickerLoadTime == '') {
                clearPickerSelection(startEnd)
                if (startEnd == 'start') {
                    setTimePickerDisable('end')
                }
            }
            else {
                var pickerMinutes = pickerLoadTime.substr(2, 2);
                var pickerHours = parseInt(pickerLoadTime.substr(0, 2), 10);
                var pickerAmpm = ''

                if (pickerHours == 0) {
                    pickerHours = 12;
                    pickerAmpm = 'AM';
                }
                else if (pickerHours < 12) {
                    pickerAmpm = 'AM'
                }
                else if (pickerHours == 12) {
                    pickerAmpm = 'PM'
                }
                else {
                    pickerHours -= 12
                    pickerAmpm = 'PM'
                }


                selectTimeSelection(element.find(".timeSelection." + startEnd + ".hours").filter(function (index) { return $(this).html() == pickerHours }), 'hours', startEnd);
                selectTimeSelection(element.find(".timeSelection." + startEnd + ".minutes:contains('" + pickerMinutes + "')"), 'minutes', startEnd);
                selectTimeSelection(element.find(".timeSelection." + startEnd + ".ampm:contains('" + pickerAmpm + "')"), 'ampm', startEnd);

                if (startEnd == 'start') {
                    setTimePickerEnabled('end')
                }
            }


            setPickerTime(pickerLoadTime, startEnd)
        }

        function getPickerTime(startEnd) {

            var pickerTime = ''

            var hourSelector = element.find('.timeSelection.' + startEnd + '.' + 'hours.timeSelection_on')
            var minutesSelector = element.find('.timeSelection.' + startEnd + '.' + 'minutes.timeSelection_on')
            var ampmSelector = element.find('.timeSelection.' + startEnd + '.' + 'ampm.timeSelection_on')

            if (hourSelector.length > 0 && minutesSelector.length > 0 && ampmSelector.length > 0) {
                pickerTime = parseInt(hourSelector.html() + minutesSelector.html(), 10);

                if (ampmSelector.html() == "PM") {

                    if (hourSelector.html() != '12') {
                        pickerTime += 1200;
                    }
                }

                if (ampmSelector.html() == "AM" && hourSelector.html() == '12') {
                    pickerTime -= 1200;
                }

            }

            return pickerTime;

        }

        function selectTimeSelection(item, column, startEnd) {

            element.find('.timeSelection.' + column + '.' + startEnd).removeClass('timeSelection_on')
            element.find('.timeSelection.' + column + '.' + startEnd).addClass('timeSelection_off')
            if (item != null) {
                $(item).removeClass('timeSelection_off')
                $(item).addClass('timeSelection_on')
            }

        }

        function getStartEnd(item) {

            var startEnd

            if ($(item).hasClass('start')) {
                startEnd = 'start'
            }
            else {
                startEnd = 'end'
            }

            return startEnd

        }

        function showTimePicker(startEnd) {

            hideAllTimePickers();

            var timeSelectionContainer = element.find('.timeSelectionContainer.' + startEnd);

            timeSelectionContainer.show();

        }

        function hideTimePicker(startEnd) {

            var timeSelectionContainer = element.find('.timeSelectionContainer.' + startEnd);

            timeSelectionContainer.hide();

        }

        function hideAllTimePickers() {

            var timePickers = $('.timePickerContainerContainer');

            $.each(timePickers, function (index, elem) {

                var timeSelectionContainerStart = $(elem).find('.timeSelectionContainer.start');
                timeSelectionContainerStart.hide();

                var timeSelectionContainerEnd = $(elem).find('.timeSelectionContainer.end');
                timeSelectionContainerEnd.hide();
            });
        }

        function clearPickerSelection(startEnd) {

            selectTimeSelection(null, 'hours', startEnd);
            selectTimeSelection(null, 'minutes', startEnd);
            selectTimeSelection(null, 'ampm', startEnd);
        }

        function setTimePickerDisable(startEnd) {

            var timeDisplay = element.find('.timeDisplay.' + startEnd);
            timeDisplay.removeClass('timeDisplay_enabled');
            timeDisplay.addClass('timeDisplay_disabled');

        }

        function setTimePickerEnabled(startEnd) {

            var timeDisplay = element.find('.timeDisplay.' + startEnd);
            timeDisplay.removeClass('timeDisplay_disabled');
            timeDisplay.addClass('timeDisplay_enabled');

        }

        scope.timeSelection = function ($event) {

            var startEnd = getStartEnd($event.target);
            var $this = $event.target;

            if ($($this).hasClass('hours')) {
                selectTimeSelection($this, 'hours', startEnd)
            }
            else if ($($this).hasClass('minutes')) {
                selectTimeSelection($this, 'minutes', startEnd)
            }
            else {
                selectTimeSelection($this, 'ampm', startEnd)
            }

            var pickerTime = getPickerTime(startEnd)

            setPickerTime(pickerTime, startEnd);

            if (pickerTime.toString() != '') {
                hideTimePicker(startEnd);
            }

        };

        scope.timeSelectionClear = function ($event) {

            var startEnd = getStartEnd($event.target);

            clearPickerSelection(startEnd)

            setPickerTime('', startEnd);

            hideTimePicker(startEnd)
        };

        scope.timeDisplay = function ($event) {

            var startEnd = getStartEnd($event.target);
            var timeSelectionContainer = element.find('.timeSelectionContainer.' + startEnd);

            if (startEnd == 'start' || (startEnd == 'end' && scope.startTime != null)) {

                if (timeSelectionContainer.css('display') == 'none') {
                    showTimePicker(startEnd);
                }
                else {
                    hideTimePicker(startEnd);
                }
            }
        };

        scope.timeSelectionClose = function ($event) {

            var startEnd = getStartEnd($event.target);

            hideTimePicker(startEnd);

        };



        scope.$watch('[startTime,endTime]', function (newVal, oldVal) {

            initializeTimePicker();

        }, true)

    }

    return {
        scope: {
            showEndTime: '=',
            startTime: '=',
            endTime: '=?'
        },
        templateUrl: SITEBASEURL + 'core/content/angular/templates/timePicker.html',
        link: link

    };

})

saffire.admin.angular.app.directive('sfGeneralDateStartEnd', ['saffire.admin.angular.dateTimeService', function (dateTimeService) {
    function link(scope, element, attrs) {

        scope.$watch('[startDate, endDate,startTime, endTime, isRequired, validationEnabled,minDateTime,maxDateTime]', function (newVal, oldVal) {

            var isValid = true
            var messages = [];
            var errorMessages = [];
            var validationEnabled = scope.validationEnabled == undefined ? true : scope.validationEnabled;
            var validationTitle = scope.validationTitle ? scope.validationTitle + " " : "";
            var isEndDateRequired = scope.isEndDateRequired == undefined ? true : scope.isEndDateRequired;

            if (scope.validationObject) {

                var startDate = new Date(scope.startDate);
                var endDate = new Date(scope.endDate);

                if (validationEnabled) {
                    if (scope.isRequired) {
                        if (!scope.startDate) {
                            isValid = false;
                            errorMessages.push(validationTitle + 'Start Date is required');
                        }

                        if (!scope.endDate && isEndDateRequired) {
                            isValid = false;
                            errorMessages.push(validationTitle + 'End Date is required');
                        }
                    }

                    if (startDate > endDate) {
                        isValid = false;
                        errorMessages.push(validationTitle + 'End Date must be greater than Start Date');
                    }
                    else if (scope.startDate == scope.endDate && (scope.startTime != "" && scope.endTime != "")) {
                        if (scope.startTime > scope.endTime) {
                            isValid = false;
                            errorMessages.push(validationTitle + 'End Date must be greater than Start Date');
                        }
                    }

                    if (scope.minDateTime && scope.startDate) {
                        var startdateTime = dateTimeService.getDateTimeString(scope.startDate, scope.startTime);
                        if (scope.minDateTime > startdateTime) {
                            isValid = false;
                            errorMessages.push(validationTitle + "Start date/time must be on or after the product's sellable start date/time");
                        }
                    }

                    if (scope.maxDateTime && scope.endDate) {
                        var enddateTime = dateTimeService.getDateTimeString(scope.endDate, scope.endTime, true);
                        if (scope.maxDateTime < enddateTime) {
                            isValid = false;
                            errorMessages.push(validationTitle + "End date/time must be on or before the product's sellable end date/time");
                        }
                    }
                }

                messages.push(errorMessages.join("<br />"));

                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }
        }, true);

    };
    return {
        scope: {
            startDate: '=',
            endDate: '=',
            startTime: '=',
            endTime: '=',
            showCountdown: '=?',
            includeCountdown: '=?',
            validationTitle: '@',
            isRequired: '=',
            isEndDateRequired: '=?',
            validationEnabled: '=',
            validationObject: '=?',
            showValidationIcon: '=?',
            minDateTime: '=',
            maxDateTime: '=',
        },
        templateUrl: SITEBASEURL + 'core/content/angular/templates/dateStartEnd.html',
        link: link
    }

}]);

saffire.admin.angular.app.directive('sfGeneralDateTimeRange', ['saffire.admin.angular.dateTimeService', function (dateTimeService) {
    function link(scope, element, attrs) {

        scope.$watch('[startDate, endDate, startTime, endTime, isRequired, validationEnabled, minDateTime, maxDateTime]', function (newVal, oldVal) {

            let isValid = true
            let messages = [];
            let errorMessages = [];
            let validationEnabled = scope.validationEnabled == undefined ? true : scope.validationEnabled;
            let validationTitle = scope.validationTitle ? scope.validationTitle + " " : "";
            let isEndDateRequired = scope.isEndDateRequired == undefined ? true : scope.isEndDateRequired;

            if (scope.validationObject) {

                let startDate = new Date(scope.startDate);
                let endDate = new Date(scope.endDate);

                if (validationEnabled) {
                    if (scope.isRequired) {
                        if (!scope.startDate) {
                            isValid = false;
                            errorMessages.push(validationTitle + 'Start Date is required');
                        }

                        if (!scope.endDate && isEndDateRequired) {
                            isValid = false;
                            errorMessages.push(validationTitle + 'End Date is required');
                        }
                    }

                    if (startDate > endDate) {
                        isValid = false;
                        errorMessages.push(validationTitle + 'End Date must be greater than Start Date');
                    }

                    else if (scope.startDate && scope.endDate && scope.startDate.getTime() === scope.endDate.getTime() &&
                        (scope.startTime != "" && scope.endTime != "") &&
                        (scope.startTime > scope.endTime)) {
                        isValid = false;
                        errorMessages.push(validationTitle + 'End Date must be greater than Start Date');
                    }

                    if (scope.minDateTime && scope.startDate) {
                        let startdateTime = dateTimeService.getDateTimeString(scope.startDate, scope.startTime);
                        if (scope.minDateTime > startdateTime) {
                            isValid = false;
                            errorMessages.push(validationTitle + "Start date/time must be on or after the product's sellable start date/time");
                        }
                    }

                    if (scope.maxDateTime && scope.endDate) {
                        let enddateTime = dateTimeService.getDateTimeString(scope.endDate, scope.endTime, true);
                        if (scope.maxDateTime < enddateTime) {
                            isValid = false;
                            errorMessages.push(validationTitle + "End date/time must be on or before the product's sellable end date/time");
                        }
                    }
                }

                messages.push(errorMessages.join("<br />"));

                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }
        }, true);
    };
    return {
        scope: {
            startDate: '=',
            endDate: '=',
            startTime: '=',
            endTime: '=',
            validationTitle: '@',
            isRequired: '=',
            isEndDateRequired: '=?',
            validationEnabled: '=',
            validationObject: '=?',
            showValidationIcon: '=?',
            minDateTime: '=',
            maxDateTime: '=',
            showSeconds: '='
        },
        templateUrl: SITEBASEURL + 'core/content/angular/templates/generalDateTimeRange.html',
        link: link
    }

}]);

saffire.admin.angular.app.directive('sfDropDownSelector', function () {

    function link(scope, element, attrs) {

        scope.isInitialized = false;

        scope.$watch('[recordId, ddDataSource]', function (newVal, oldVal) {

            //set initial Values

            //if (!scope.isInitialized) {

            if (scope.ddDataSource) {
                var template = $.grep(scope.ddDataSource, function (tt) {
                    return tt.ID == scope.recordId
                })

                if (template.length) {
                    scope.selectedRecord = template[0];
                }
                else
                    scope.selectedRecord = null;

                scope.isInitialized = true;
            }

            //}


        }, true)

        scope.$watch('selectedRecord', function (newVal, oldVal) {

            if (scope.isInitialized) {

                scope.recordId = newVal ? scope.selectedRecord.ID : null;

            }

        }, true)

        scope.$watch('[recordId,isRequired,validationObject]', function (newVal, oldVal) {

            var isValid = true;
            var messages = [];

            if (scope.isRequired && valueIsNullOrUndefinedOrEmpty(scope.recordId) || scope.recordId == 0) {
                isValid = false;
                messages.push(scope.labelText + ' is required')
            }          

            if (scope.validationObject) {
                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }
        }, true)
    };

    return {
        scope: {
            recordId: '=',
            ddDataSource: '=',
            showSelectLabel: '=',
            selectLabel: '@',
            labelText: '@',
            isRequired: '=',
            validationObject: '='
        },
        templateUrl: SITEBASEURL + 'core/content/angular/templates/dropdownSelector.html',
        link: link

    }

});

saffire.admin.angular.app.directive('sfValidationInputText', function () {

    function link(scope, element, attrs) {

        function isAlphanumeric(str) {
            return /^[a-zA-Z0-9]+$/.test(str);
        }

        scope.$watch('[value,isRequired,validationObject,isNumeric,isAlphanumeric,validationEnabled]', function (newVal, oldVal) {

            var validationEnabled = scope.validationEnabled == undefined ? true : scope.validationEnabled;

            var isValid = true;
            var messages = [];

            if (validationEnabled) {

                if (scope.isRequired && (valueIsNullOrUndefinedOrEmpty(scope.value))) {
                    isValid = false;
                    messages.push(scope.labelText + ' is required');
                }
                else if (scope.value && scope.isNumeric && !IsNumeric(scope.value)) {
                    isValid = false;
                    messages.push(scope.labelText + ' is invalid');
                } else if (scope.value && scope.isAlphanumeric && !isAlphanumeric(scope.value)) {
                    isValid = false;
                    messages.push(scope.labelText + ' must be alphanumeric');
                }
            }

            //validation

            if (scope.validationObject) {
                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }

        }, true)

    };

    return {
        scope: {
            value: '=',
            description: '=',
            isRequired: '=',
            validationObject: '=',
            validationEnabled: '=',
            showValidationIcon: '=',
            labelText: '@',
            showLabelText: '=',
            cssClass: '@',
            isCritical: '=',
            maxLen: '=',
            minLen: '=',
            disabled: '=',
            placeholder: '@',
            isNumeric: '=',
            charCounterEnabled: '=',
            charCounterMaxChars: '=',
            isAlphanumeric: '=',
            allowSpaces: '=?',
            helpText: '@',
            showHelpText: '='
        },
        templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputText.html',
        link: link
    }
});

saffire.admin.angular.app.directive('sfValidationInputNumber', function () {

    function link(scope, element, attrs, ctrl) {

        scope.numberIsValid = true;

        scope.$watch('[value, isRequired, numberIsValid, groupVar, min, max, validationEnabled]', function (newVal, oldVal) {

            var isValid = true;
            var messages = [];
            var validationEnabled = scope.validationEnabled == undefined ? true : scope.validationEnabled;

            if (scope.allowZero == undefined) {
                scope.allowZero = false;
            }

            if (scope.allowNegative == undefined) {
                scope.allowNegative = false;
            }

            if (validationEnabled) {
                scope.isValid = scope.numberIsValid;

                if (scope.isRequired && valueIsNullOrUndefinedOrEmpty(scope.value)) {
                    isValid = false;
                    messages.push(scope.labelText + " is required")
                }
                else if (!valueIsNullOrUndefinedOrEmpty(scope.value) && scope.numberIsValid) {
                    if (scope.min && scope.value < scope.min) {
                        isValid = false;

                        if (scope.minValidationMessage) {
                            messages.push(scope.minValidationMessage);

                        } else {
                            messages.push("Please input a number greater than " + scope.min);
                        }
                    }

                    if (scope.max && scope.value > scope.max) {
                        isValid = false;

                        if (scope.maxValidationMessage) {
                            messages.push(scope.maxValidationMessage);

                        } else {
                            messages.push("Please input a number no greater than " + scope.max);
                        }
                    }
                }
                else if (!scope.numberIsValid) {
                    isValid = false;
                    messages.push(scope.labelText + " is invalid");
                }

                if (scope.customValidateFunc) {

                    var customValidationObject = scope.customValidateFunc();

                    if (!customValidationObject.isValid) {
                        isValid = customValidationObject.isValid;

                        angular.forEach(customValidationObject.messages, function (m) {
                            messages.push(m);
                        })
                    }
                }                
            }

            if (scope.validationObject) {
                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }
        }, true)
    };

    return {
        scope: {
            value: '=',
            isRequired: '=',
            validationObject: '=',
            validationEnabled: '=',
            showValidationIcon: '=',
            labelText: '@',
            showLabelText: '=',
            cssClass: '@',
            maxLen: '=',
            minLen: '=',
            min: '=?',
            minValidationMessage: '@',
            max: '=?',
            maxValidationMessage: '@',
            disabled: '=',
            placeholder: '@',
            integerOnly: '=?',
            allowZero: '=?',
            allowNegative: '=?',
            isValid: '=?',
            customValidateFunc: '=',
            groupVar: '=',
            width: '@',
            display: '@',
            allowLeadingZero: '='
        },
        templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputNumber.html',
        link: link
    }
});

saffire.admin.angular.app.directive('sfValidationInputDate', function () {

    function link(scope, element, attrs) {

        scope.$watch('[value,isRequired,validationObject]', function (newVal, oldVal) {

            var isValid = true;
            var messages = [];

            if (scope.isRequired && (valueIsNullOrUndefinedOrEmpty(scope.value))) {
                isValid = false;
                messages.push(scope.labelText + ' is required')
            }

            //validation

            if (scope.validationObject) {
                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }

        }, true)
    };

    return {
        scope: {
            value: '=',
            description: '=',
            isRequired: '=',
            validationObject: '=',
            showValidationIcon: '=',
            labelText: '@',
            showLabelText: '=',
            cssClass: '@',
            isCritical: '=',
            max: '=',
            min: '=',
            disabled: '=',
            placeholder: '@'
        },
        templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputDate.html',
        link: link
    }
});

saffire.admin.angular.app.directive('sfValidationInputPassword', function () {

    function link(scope, element, attrs) {

        scope.$watch('[value,isRequired,validationObject]', function (newVal, oldVal) {

            var isValid = true;
            var messages = [];

            if (scope.isRequired && (valueIsNullOrUndefinedOrEmpty(scope.value))) {
                isValid = false;
                messages.push(scope.labelText + ' is required')
            }

            //validation

            if (scope.validationObject) {
                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }

        }, true)
    };

    return {
        scope: {
            value: '=',
            description: '=',
            isRequired: '=',
            validationObject: '=',
            showValidationIcon: '=',
            labelText: '@',
            showLabelText: '=',
            cssClass: '@',
            isCritical: '=',
            maxLen: '=',
            disabled: '=',
            placeholder: '@'
        },
        templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputPassword.html',
        link: link
    }
});

saffire.admin.angular.app.directive('sfValidationInputTextArea', function () {

    function link(scope, element, attrs) {

        scope.$watch('[value,isRequired,validationObject]', function (newVal, oldVal) {

            var isValid = true;
            var messages = [];

            if (scope.isRequired && (valueIsNullOrUndefinedOrEmpty(scope.value))) {
                isValid = false;

                if (!valueIsNullOrUndefinedOrEmpty(scope.requiredValidationMessage)) {
                    messages.push(scope.requiredValidationMessage);
                } else {
                    messages.push(scope.labelText + ' is required');
                }
            }

            //validation

            if (scope.validationObject) {
                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }

        }, true)
    };

    return {
        scope: {
            value: '=',
            description: '=',
            isRequired: '=',
            requiredValidationMessage: '@',
            validationObject: '=',
            showValidationIcon: '=',
            labelText: '@',
            showLabelText: '=',
            cssClass: '@',
            isCritical: '=',
            maxLen: '=',
            rows: '=',
            cols: '='
        },
        templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputTextArea.html',
        link: link
    }
});

saffire.admin.angular.app.directive('sfValidationInputPhone', function () {

    function link(scope, element, attrs) {

        var phoneInput = element.children().find('input')[0];
        phoneInput.focus();
        phoneInput.addEventListener('keypress', function (e) {
            if (e.which == 13) e.preventDefault();
        }, false);

        scope.$watch('[value,isRequired,validationObject]', function (newVal, oldVal, $scope) {

            var inputIsUntouched = angular.element(phoneInput).hasClass('ng-pristine');
            var isValid = true;
            var messages = [];

            if (!inputIsUntouched && scope.isRequired && (valueIsNullOrUndefinedOrEmpty(scope.value))) {
                isValid = false;
                messages.push(scope.labelText + ' is required')
            }

            //validation
            var rePhoneNumber = /^[0-9]{3}[0-9]{3}[0-9]{4}$/;

            if (!inputIsUntouched && !stringIsNullOrEmpty(scope.value && scope.value.toString()) && !(rePhoneNumber.test(scope.value))) {
                isValid = false;
                messages.push(scope.labelText + ' is invalid');
            }

            if (scope.validationObject) {
                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }

        }, true)
    };

    return {
        scope: {
            value: '=',
            description: '=',
            isRequired: '=',
            validationObject: '=',
            showValidationIcon: '=',
            labelText: '@',
            showLabelText: '=',
            cssClass: '@',
            isCritical: '=',
            maxLen: '=',
            placeholder: '@',
            mask: '@'
        },
        templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputPhone.html',
        link: link
    }
});

saffire.admin.angular.app.directive('sfValidationInputEmail', function () {

    function link(scope, element, attrs) {

        scope.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,}))$/;

        scope.$watch('[value,isRequired,validationObject]', function (newVal, oldVal) {

            var isValid = true;
            var messages = [];

            if (scope.isRequired) {

                if (valueIsNullOrUndefinedOrEmpty(scope.value)) {
                    isValid = false;
                    messages.push(scope.labelText + ' is required')
                }
                else if (!(scope.re.test(scope.value))) {
                    isValid = false;
                    messages.push(scope.labelText + ' is invalid')
                }
            }

            else if ((scope.isRequired == undefined || scope.isRequired == false) && !valueIsNullOrUndefinedOrEmpty(scope.value) && !(scope.re.test(scope.value))) {
                isValid = false;
                messages.push(scope.labelText + ' is invalid')
            }

            if (scope.validationObject) {
                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }
        }, true)
    };

    return {
        scope: {
            value: '=',
            description: '=',
            isRequired: '=',
            validationObject: '=',
            showValidationIcon: '=',
            labelText: '@',
            showLabelText: '=',
            cssClass: '@',
            isCritical: '='
        },
        templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputEmail.html',
        link: link
    }
});

saffire.admin.angular.app.directive('sfValidationInputDropDown', function () {

    function link(scope, element, attrs) {

        scope.isInitialized = false;

        if (!scope.selectLabel)
            scope.selectLabel = "Select";

        scope.getDisplayName = function (recordData) {
            if (scope.displayNameFunc) {
                return scope.displayNameFunc({ entity: recordData });
            }

            return recordData.Name;
        }

        scope.getDisplayNameWithHTMLStripped = function (recordData) {

            var displayName = scope.getDisplayName(recordData);

            return displayName.replace(/<\/?[^>]+(>|$)/g, "")
        }

        scope.isDisabled = function (recordData) {
            if (scope.isDisabledFunc) {
                return scope.isDisabledFunc({ entity: recordData });
            }

            return false;
        }

        scope.$watch('[recordId, recordName, ddDataSource]', function (newVal, oldVal) {

            if (scope.ddDataSource) {
                var template = [];

                if (scope.nameSelection) {
                    template = $.grep(scope.ddDataSource, function (tt) {
                        return tt.Name == scope.recordId
                    })
                } else {
                    template = $.grep(scope.ddDataSource, function (tt) {
                        return tt.ID == scope.recordId
                    })
                }

                if (template.length) {
                    scope.selectedRecord = template[0];
                }
                else
                    scope.selectedRecord = null;

                scope.isInitialized = true;
            }

        }, true)

        scope.$watch('selectedRecord', function (newVal, oldVal) {

            if (scope.isInitialized) {

                if (scope.nameSelection)
                    scope.recordId = newVal ? scope.selectedRecord.Name : '';
                else
                    scope.recordId = newVal ? scope.selectedRecord.ID : null;

                scope.selectedName = newVal ? scope.selectedRecord.Name : '';
            }

        }, true)

        scope.$watch('[recordId,isRequired,validationObject,ids]', function (newVal, oldVal) {

            var isValid = true;
            var messages = [];

            if (scope.isRequired && valueIsNullOrUndefinedOrEmpty(scope.recordId)) {
                isValid = false;
                messages.push(scope.labelText + ' is required')
            }

            if (scope.ids && scope.recordId) {
                var isDuplicate = $.grep(scope.ids, function (id) {
                    return id == scope.recordId;
                }).length > 1;

                if (isDuplicate) {
                    isValid = false;
                    messages.push('Duplicate ' + scope.labelText + ' selected')
                }
            }

            if (scope.validationObject) {
                scope.validationObject.isValid = isValid;
                scope.validationObject.messages = messages;
            }
        }, true)
    };

    return {
        scope: {
            recordId: '=',
            selectedRecord: '=?',
            selectedName: '=?',
            nameSelection: '=?',
            ddDataSource: '=',
            showSelectLabel: '=',
            isRequired: '=',
            validationObject: '=',
            showValidationIcon: '=',
            selectLabel: '@',
            labelText: '@',
            disabled: '=',
            showLabelText: '=',
            ids: '=',
            displayNameFunc: '&?',
            isDisabledFunc: '&?'
        },
        templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputDropDown.html',
        link: link

    }
});

saffire.admin.angular.app.directive('sfValidationInputCustomField', function () {

    function link(scope, element, attrs) {

    };

    return {
        scope: {
            value: '=',
            description: '=',
            isRequired: '=',
            labelText: '@',
            showLabelText: '=',
            cssClass: '@',
            maxLen: '=',
            minLen: '=',
            disabled: '=',
            placeholder: '@',
            isNumeric: '=',
            validationObject: '=',
            showValidationIcon: '='
        },
        templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputCustomField.html',
        link: link
    }
});

saffire.admin.angular.app.directive('sfValidationIcon', function () {

    function link(scope, element, attrs) {


    };

    return {
        scope: {
            validationObject: '=',
        },
        templateUrl: SITEBASEURL + 'core/content/angular/templates/validationIcon.html',
        link: link
    }
});

saffire.admin.angular.app.directive('sfUserAddressForm', ['validationService', function (validationService) {

    function link(scope, element, attrs) {

        scope.filteredStates = [];
        scope.userAddress = {};

        scope.displayName = {
            country: ''
        }

        scope.firstNameValidation = validationService.createValidationObject();
        scope.lastNameValidation = validationService.createValidationObject();
        scope.organizationValidation = validationService.createValidationObject();
        scope.addressValidation = validationService.createValidationObject();
        scope.cityValidation = validationService.createValidationObject();
        scope.postalCodeValidation = validationService.createValidationObject();
        scope.phoneValidation = validationService.createValidationObject();
        scope.stateValidation = validationService.createValidationObject();
        scope.countryValidation = validationService.createValidationObject();

        //Validation
        scope.$watch('[firstNameValidation,lastNameValidation,organizationValidation,addressValidation,cityValidation,postalCodeValidation,phoneValidation,stateValidation,countryValidation,isRequired,customerDetails.billingSameAsShipping,isModalOpened]', function (newVal, oldVal) {

            var isValid = true;
            var messages = [];

            scope.validationObject.isValid = isValid;
            scope.validationObject.messages = messages;

            if (scope.isRequired) {

                validationService.sumValidationObjectsWithObject(scope.validationObject,
                    [scope.firstNameValidation, scope.lastNameValidation, scope.organizationValidation, scope.addressValidation, scope.cityValidation, scope.postalCodeValidation, scope.phoneValidation, scope.stateValidation, scope.countryValidation])
            }
        }, true)

        scope.$watch('[isModalOpened]', function (newValue, oldValue) {

            if (scope.isModalOpened) {
                scope.userAddress = angular.fromJson(angular.toJson(scope.address));
            }
        }, true);

        scope.$watch('userAddress', function (newValue, oldValue) {

            if (scope.userAddress) {
                scope.hasChanges = angular.toJson(scope.userAddress) != angular.toJson(scope.address);
            }
        }, true);

        scope.$watch('[states, userAddress.Country, defaultCountryId]', function (newVal, oldVal) {

            if (scope.states) {

                scope.filteredStates = $.grep(scope.states, function (s) {
                    return s.CountryID == scope.defaultCountryId;
                })

                if (scope.userAddress && scope.userAddress.Country && scope.internationalEnabled) {
                    scope.filteredStates = $.grep(scope.states, function (s) {
                        return s.CountryID == scope.userAddress.Country
                    })
                }

                scope.isInitialized = true;
            }
        }, true)


        scope.saveAddress = function () {
            if (scope.validationObject.isValid) {
                scope.saveAddEditAddress({ address: scope.userAddress });
            } else {
                scope.showMessage({
                    autoHide: false,
                    message: scope.validationObject.messages.join('<br />'),
                    buttons: [
                        {
                            text: 'Close',
                            onClick: function () {
                                scope.closeMessage();
                            }
                        }
                    ],
                });
            }
        }
    };

    return {
        scope: {
            address: '=',
            countries: '=',
            states: '=',
            internationalEnabled: '=',
            isRequired: '=',
            defaultCountryId: '=',
            validationObject: '=',
            isModalOpened: '=',
            closeAddEditAddress: '&',
            saveAddEditAddress: '&',
            showMessage: '=',
            closeMessage: '=',
            saveButtonText: '@',
            hasChanges: '=?'
        },
        templateUrl: SITEBASEURL + 'core/content/angular/Templates/userAddressForm.html',
        link: link

    }

}]);

saffire.admin.angular.app.directive('sfPager', [ function () {

    function link(scope, element, attrs) {
        // init
        scope.showPagination = false;
        scope.displayPages = [];
        scope.maxPagesToDisplay = 8;
        scope.pageGroupIndex = 0;
        scope.maxPageGroupIndex = 0;
        scope.pageCount = 0;
        scope.originalPageSize = null;
        scope.viewAll = false;
        scope.viewActionText = "View All"

        if (scope.showViewToggle == undefined) {
            scope.showViewToggle = true;
        }

        scope.setPage = function (page) {
            scope.paginationObject.pageIndex = page;
        }

        scope.previousPageGroup = function () {
            scope.pageGroupIndex -= 1
        }

        scope.nextPageGroup = function () {
            scope.pageGroupIndex += 1
        }

        scope.toggleView = function () {
            scope.viewAll = !scope.viewAll

            if (scope.paginationObject.viewAll != undefined) {
                scope.paginationObject.viewAll = scope.viewAll;
            }

            if (scope.viewAll) {
                scope.originalPageSize = scope.paginationObject.pageSize;
                if (scope.paginationObject.originalPageSize != undefined) {
                    scope.paginationObject.originalPageSize = scope.paginationObject.pageSize;
                }                
                scope.paginationObject.pageIndex = 0;
                scope.paginationObject.pageSize = 9999999;
                scope.viewActionText = "View " + scope.originalPageSize.toString() + ' per page';

            }
            else {
                scope.viewActionText = "View All";
                scope.paginationObject.pageSize = scope.originalPageSize;
            }

        }

        scope.$watch('pageGroupIndex', function whenValueChanges(newVal, oldVal) {

            if (scope.pageGroupIndex != undefined) {
                scope.paginationObject.pageIndex = (scope.pageGroupIndex * scope.maxPagesToDisplay);
            }

        }, true)

        scope.$watch('paginationObject.viewAll', function whenValueChanges(newVal, oldVal) {

            if (scope.paginationObject.viewAll != undefined) {
                scope.viewAll = scope.paginationObject.viewAll;

                if (scope.viewAll) {
                    scope.originalPageSize = (scope.originalPageSize ? scope.originalPageSize : (scope.paginationObject.originalPageSize ? scope.paginationObject.originalPageSize : scope.paginationObject.pageSize));
                    scope.viewActionText = "View " + scope.originalPageSize.toString() + ' per page';
                }
                else {
                    scope.viewActionText = "View All";
                    if (scope.originalPageSize != undefined) {
                        scope.paginationObject.pageSize = scope.originalPageSize;
                    }
                }
            }
        }, true) 

        scope.$watch('[paginationObject,recordCount]', function whenValueChanges(newVal, oldVal) {

            var showPagination = true;
            var pageCount = 0;
            var maxPageGroupIndex = 0;
            displayPages = []; 

            if (scope.paginationObject && scope.paginationObject.pageSize > 0 && scope.recordCount && ((scope.recordCount > scope.paginationObject.pageSize) || scope.viewAll)) {

                scope.pageGroupIndex = Math.floor(scope.paginationObject.pageIndex / scope.maxPagesToDisplay);

                pageCount = Math.floor(scope.recordCount / scope.paginationObject.pageSize) + (scope.recordCount % scope.paginationObject.pageSize > 0 ? 1 : 0)

                var startPage = Math.floor(scope.paginationObject.pageIndex / scope.maxPagesToDisplay) * scope.maxPagesToDisplay

                for (p = startPage; p < pageCount && p < startPage + scope.maxPagesToDisplay; p++) {
                    displayPages.push(p)
                }

                maxPageGroupIndex = Math.floor(scope.recordCount / (scope.maxPagesToDisplay * scope.paginationObject.pageSize)) + (scope.recordCount % (scope.maxPagesToDisplay * scope.paginationObject.pageSize) > 0 ? 1 : 0) - 1

                showPagination = true;

            }
            else {
                scope.pageGroupIndex = 0;
                scope.paginationObject.pageIndex = 0;
                pageCount = 0;
                maxPageGroupIndex = 0;
                showPagination = false;
            }

            scope.showPagination = scope.viewAll ? true : showPagination;
            scope.displayPages = displayPages;
            scope.pageCount = pageCount;
            scope.maxPageGroupIndex = maxPageGroupIndex;

        }, true)

    };

    return {
        scope: {
            paginationObject: '=',
            recordCount: '=',
            showViewToggle: '=?'
        },

        templateUrl: SITEBASEURL + 'core/content/angular/Templates/Pager.html',
        link: link

    }

}]);

saffire.admin.angular.app.directive('sfEditorTabHeader', function () {

    function link(scope, element, attrs) {

    };

    return {
        scope: {
            tab: '=',
            validationObject: '=',
            selectTab: '&'
        },
        templateUrl: SITEBASEURL + 'core/content/angular/editorTabHeader.html',
        link: link
    }
});

saffire.admin.angular.app.directive('sfEditorAdvancedOptionsPanel', function () {

    function link(scope, element, attrs) {

        scope.toggleShowPanel = function () {
            scope.showAdvancedOptions = !scope.showAdvancedOptions;
        }

    };

    return {
        restrict: 'E',
        scope: {
            showAdvancedOptions: '=?showOnInit'
        },
        transclude: true,
        templateUrl: SITEBASEURL + 'core/content/angular/editoradvancedoptionspanel.html',
        link: link,
    }

});

/**************************************/
/*Instantiatable Classes              */
/**************************************/

saffire.admin.angular.app.factory('LinkDTO', [function LinkDTO() {

    function LinkDTO() {
        this.Title = '';
        this.Target = '_self';
        this.URL = '';
        this.Note = '';
        this.Enabled = true;
    }

    return LinkDTO;

}]);


/**************************************/
/*Pinned Pages                        */
/**************************************/

saffire.admin.angular.app.service('saffire.admin.angular.pinnedPagesService', ['$rootScope', function ($rootScope) {

    var refreshFunction;

    //Public API

    return ({
        refreshWidget: refreshWidget,
        broadcastChange: broadcastChange
    })

    //Public Functions

    function refreshWidget() {
        $rootScope.$broadcast('refreshPinnedPagesWidget')
    }

    function broadcastChange(entityID, entityType, isPinned) {

        $rootScope.$broadcast('pagePinUpdate', {
            entityID: entityID,
            entityType: entityType,
            isPinned: isPinned
        })
    }

}]);

/*Pinned Pages Controller*/
saffire.admin.angular.app.controller('saffire.admin.angular.pinnedPagesController', ['$scope', 'saffire.admin.angular.webServices', 'saffire.admin.angular.webPoster', 'saffire.admin.angular.pinnedPagesService', function pinnedPagesController($scope, webServices, webPoster, pinnedPagesService) {

    $scope.showPinCurrentPage = function () {

        if ($scope.entityID == 0) {
            return false;
        }

        if ($scope.dataItems) {
            var existingItem = $.grep($scope.dataItems.PinnedPages, function (history) {
                return history.EntityID == $scope.entityID && history.EntityType == $scope.entityType
            });

            if (existingItem.length > 0) {
                return false
            }
        }

        return true;

    }

    $scope.showRecentlyVisited = function () {
        if ($scope.dataItems) {
            return $scope.dataItems.RecentlyViewed.length > 0;
        }
        return false;
    }

    $scope.getPinRowClass = function (history) {
        if (history.EntityType == 0) {
            return 'pagePinRow'
        }
        else if (history.EntityType == 1) {
            return 'eventPinRow'
        }
        else if (history.EntityType == 2) {
            return 'formPinRow'
        }

        return ''
    }

    $scope.getStatusClass = function (history) {
        if (!history.IsActive) {
            return 'inactivePinRow';
        }

        return ''
    }

    $scope.unpin = function (history) {
        webPoster.postWebRequest($scope.siteBaseURL + 'api/pages/pin-page'
                                        , JSON.stringify({
                                            'userID': $scope.userID,
                                            'entityID': history.EntityID,
                                            'entityType': history.EntityType,
                                            'isPinned': false
                                        })
                                        ).then(
                                            function (returnedData) {
                                                //Do we need to do anything after update

                                                $scope.loadHistory();
                                                pinnedPagesService.broadcastChange(history.EntityID, history.EntityType, false);
                                            });
    }

    $scope.pin = function (history) {
        webPoster.postWebRequest($scope.siteBaseURL + 'api/pages/pin-page'
                                        , JSON.stringify({
                                            'userID': $scope.userID,
                                            'entityID': history.EntityID,
                                            'entityType': history.EntityType,
                                            'isPinned': true
                                        })
                                        ).then(
                                            function (returnedData) {
                                                //Do we need to do anything after update

                                                $scope.loadHistory();
                                                pinnedPagesService.broadcastChange(history.EntityID, history.EntityType, true);
                                            });
    }

    $scope.pinCurrentPage = function () {
        webPoster.postWebRequest($scope.siteBaseURL + 'api/pages/pin-page'
                                        , JSON.stringify({
                                            'userID': $scope.userID,
                                            'entityID': $scope.entityID,
                                            'entityType': $scope.entityType,
                                            'isPinned': true
                                        })
                                        ).then(
                                            function (returnedData) {
                                                //Do we need to do anything after update

                                                $scope.loadHistory();
                                                pinnedPagesService.broadcastChange($scope.entityID, $scope.entityType, true);
                                            });
    }

    $scope.loadHistory = function () {
        webServices.getWebRequest($scope.siteBaseURL + 'api/pages/pinned/' + $scope.userID)
        .then(
            function (returnedData) {
                $scope.dataItems = returnedData;
            }
        )
    }

    $scope.$on('refreshPinnedPagesWidget', function (event, args) {

        $scope.loadHistory();

    });

    this.init = function (userID, entityID, entityType) {

        $scope.userID = userID;
        $scope.entityID = entityID;
        $scope.entityType = entityType;

        $scope.loadHistory();
    }

}]);

