/**
*	Controller for app
*	
**/

POP.FB.Controller = (function () {

    //Private Properties
    var CTLR = {},
		_UserAttending = null,
		_jobs = [],
		_userHasInteracted = false,
		_fbDialogBox = null,
		_isModalOpen = false;

    function __init(obj) {
        //get reference to self 
        CTLR = POP.FB.Controller;
        CTLR.EL = $('#fb-friends-attending');
        if (CTLR.EL.length < 1) { return; }
        log(obj.appUrl);
        //APP_URL = obj.appUrl;
        // DG [1/9/2012]: EVENT_URL confused with APP_URL and CTLR.APP_URL -- since both are used (CTLR.APP_URL as the base domain for links, APP_URL for the event's URL) added new property EVENT_URL on Controller
        CTLR.EVENT_URL = obj.appUrl;
        //create events array once from json/source
        for (var key in CTLR.EVENTS_OBJECT) {
            CTLR.EVENTS_ARRAY.push(key);
        }
        CTLR.EVENTS_STRING = CTLR.EVENTS_ARRAY.join(',');
        _UserAttending = new POP.FB.UserAttending(CTLR);
        //Queue up _UserAttending for when all the necessary facebook info comes back 
        __queueJob({ callback: _UserAttending.update });
        __addEventListeners();
        __fbGetLoginStatus(false);
    }

    function __onUpdate() { }

    function __onAppReady() { }

    function __addEventListeners() { }

    //So many processes are dependant on user login and such... this queues em up
    function __queueJob(job) {
        if (typeof job.callback == 'function') {
            log(job, 'queue job');
            _jobs.push(job);
        }
    }

    //This processes any jobs waiting on facebook login
    function __processJobQueue() {
        for (var i = 0, len = _jobs.length; i < len; i++) {
            if (_jobs[i] !== null) {
                log(_jobs[i], 'Process Job');
                _jobs[i].ctx = _jobs[i].ctx || CTLR;
                _jobs[i].callback.call(_jobs[i].ctx, _jobs[i].callbackParams);
                _jobs[i] = null;
            }
        }
    }

    function __fbEventSubScribe(str, func) {
        FB.Event.subscribe(str, func);
    }

    function __fbGetLoginStatus(bool, callback, callbackParams, ctx) {
        //log(callback, '__fbGetLoginStatus')
        FB.getLoginStatus(function (response) {
            log(response, 'FB.getLoginStatus')
            if (bool) { //prompt the facebook dialog(s)
                __fbLogin(callback, callbackParams, ctx);
            } else {
                // DG [1/9/2012]: OAuth2 spec deprecates response.session -- instead, use response.authResponse
                if (response.authResponse) {
                    //User changed or is initial load 
                    log(CTLR.SESSION_KEY, 'CTLR.SESSION_KEY')
                    // DG [1/9/2012]: OAuth2 deprecates response.session and session_key -- instead, use response.authResponse.accessToken
                    log(response.authResponse.accessToken, 'response.authResponse.accessToken')
                    if (CTLR.SESSION_KEY !== response.authResponse.accessToken) {
                        CTLR.SESSION_KEY = response.authResponse.accessToken;
                        //Queue up callback if any
                        if (typeof callback === 'function') {
                            var job = {};
                            job.callback = callback;
                            job.callbackParams = callbackParams;
                            job.ctx = ctx;
                            __queueJob(job);
                        }
                        //get and then store user info
                        //STEP 1:
                        __fbGetUserInfo();
                        //$.event.trigger('POP.FB.CTLR:userLoggedInUpdated');
                    } else {
                        if (typeof callback === 'function') {
                            ctx = ctx || CTLR;
                            callback.apply(ctx, callbackParams);
                        }
                    }
                } else if (response.error) {
                    //Something is wrong with facebook, the config, etc, let's kill the app
                    __selfDestruct();
                } else {
                    log('', 'Not logged in, but not going to prompt login yet')
                }
            }
        }, true); //set true to force a new live check to facebook if logged in, prevents facebook errors
    }

    function __fbLogin(callback, callbackParams, ctx) {
        log(callback, '__fbLogin')
        FB.login(
			function (response) {
			    // DG [1/9/2012]: OAuth2 deprecates response.session -- instead, use response.authResponse
			    if (response.authResponse) {
			        log(response, '__fbLogin response')
			        __fbGetLoginStatus(false, callback, callbackParams, ctx);
			    } else if (response.error) {
			        //Something is wrong with facebook, the config, etc, let's kill the app
			        __selfDestruct();
			    }
			},
        //permissions opts
        // DG [1/6/2012]: OAuth2 spec deprecates 'perms' -- instead, use 'scope'
			{
			scope: 'user_events, friends_events, rsvp_event, create_event, offline_access'
}
		);
    }

    function __fbGetUserInfo() {
        FB.api(
			'/me',
			function (response) {
			    __fbSetUserInfo(response);
			    // STEP 2: 
			    __fbGetAllFriendsArray();
			}
		);
    }

    function __fbSetUserInfo(obj) {
        log(obj, '__fbSetUserInfo');
        CTLR.USER_INFO = obj;
        CTLR.USER_INFO.picture = 'http://graph.facebook.com/' + CTLR.USER_INFO.id + '/picture';
        //$.event.trigger('POP.FB.CTLR:fbSetUserInfoUpdated');
    }

    function __fbGetAllFriendsArray() {
        var frds;

        /*FB.api(
        '/me/friends',
        function (response) {
        __fbSetAllFriendsArray(response);
        // STEP 3: 
        __fbGetUsersRsvpEvents();
        }
        );*/

        // DG [1/9/2012]: Opted to use FQL instead of Graph API, so data returned will be sorted.  Off loads sorting onto Facebook, instead of client-side.
        frds = FB.Data.query(
			'SELECT uid, name ' +
			'FROM user ' +
			'WHERE uid IN (SELECT uid2 FROM friend WHERE uid1 = {0})' +
            'ORDER BY name', CTLR.USER_INFO.id
		);
        FB.Data.waitOn(
			[frds],
			function (args) {
			    __fbSetAllFriendsArray(args[0]);
			    // STEP 3: 
			    __fbGetUsersRsvpEvents();
			}
		);
    }

    //function __fbSetAllFriendsArray(obj) {
    // DG [1/9/2012]: Function now expects an array.
    function __fbSetAllFriendsArray(arr) {
        /*
        for (var i = 0, len = obj.data.length; i < len; i++) {
        CTLR.USER_FRIENDS[obj.data[i].id] = obj.data[i].name;
        CTLR.USER_FRIENDS_ARRAY.push(obj.data[i].id);
        }
        */

        // DG [1/9/2012]: Argument passed into function is now an array.
        for (var i = 0, len = arr.length; i < len; i++) {
            CTLR.USER_FRIENDS[arr[i].uid] = arr[i].name;
            CTLR.USER_FRIENDS_ARRAY.push(arr[i].uid);
        }
        CTLR.FRIENDS_LENGTH = CTLR.USER_FRIENDS_ARRAY.length;
        //$.event.trigger('POP.FB.CTLR:fbSetAllFriendsArrayUpdated');
    }

    function __fbGetUsersRsvpEvents() {
        var evts;
        evts = FB.Data.query(
			'SELECT eid, uid, rsvp_status ' +
			'FROM event_member ' +
			'WHERE eid IN (' + CTLR.EVENTS_STRING + ') '
		);
        FB.Data.waitOn(
			[evts],
			function (args) {
			    __fbSetUsersRsvpEvents(args[0]);
			}
		);
    }

    function __fbSetUsersRsvpEvents(arr) {
        CTLR.USER_EVENTS_ARRAY = arr;
        //All info is back from facebook - safe to update other modules
        __processJobQueue();
    }

    function __fbSetEventRsvp(obj, callback, callbackParams, ctx) {
        //Add to USER_EVENTS_ARRAY so don't have to make http request
        CTLR.USER_EVENTS_ARRAY.push({ 'eid': obj.id, 'uid': CTLR.USER_INFO.id, 'rsvp_status': obj.str });
        FB.api(
			'/' + obj.id + '/' + obj.str,
			'post',
			function (response) {
			    if (typeof callback === 'function') {
			        ctx = ctx || CTLR;
			        callback.apply(ctx, callbackParams);
			    }
			    _UserAttending.update();
			    //$.event.trigger('POP.FB.CTLR:fbSetEventRsvpUpdated' , [response]);
			}
		);
    }

    function __fbPostEventToWall(obj, callback, callbackParams, ctx) {
        log(obj, '__fbPostEventToWall');
        FB.ui(
        //opts
		{
		method: 'feed',
		name: CTLR.EVENT_NAME,
		link: CTLR.EVENT_URL,
		picture: CTLR.APP_URL + CTLR.EVENT_PIC,
		properties: {
		    'Date': CTLR.EVENTS_OBJECT[obj],
		    'Venue': CTLR.EVENT_ADDRESS,
		    'Location': CTLR.EVENT_LOCATION
		}
},
        //callback
			function (response) {
			    if (typeof callback === 'function') {
			        ctx = ctx || CTLR;
			        callback.apply(ctx, [callbackParams, response]);
			    }
			    //$.event.trigger('POP.FB.CTLR:fbPostEventToWallUpdated');
			}
		);
    }

    //Send all selected friends an event invite... obj contains an array (arr:friends ids) and event id (eid: id)
    function __fbSendFriendInvite(obj, callback, callbackParams, ctx) {
        FB.api(
			{
			    method: 'events.invite',
			    eid: obj.eid,
			    uids: obj.arr
			},
			function (response) {
			    if (typeof callback === 'function') {
			        ctx = ctx || CTLR;
			        callback.apply(ctx, [callbackParams, response]);
			    }
			    //$.event.trigger('POP.FB.CTLR:fbSendFriendInviteUpdated');
			}
		);
    }

    /**
    *	@params: obj - {title:'', select: '', cancel: ''}
    **/
    function __openFbDialog(obj, callback, callbackParams, ctx) {
        if (_fbDialogBox === null) {
            _fbDialogBox = new POP.FB.FbDialogBox(CTLR);
        }
        _fbDialogBox.open(obj, callback, callbackParams, ctx);
    }

    function __closeFbDialog(callback, callbackParams, ctx) {
        if (_fbDialogBox !== null) {
            _fbDialogBox.close(callback, callbackParams, ctx);
        }
    }

    /**
    *	@params: str - string of html to be added to facebook dialog
    **/
    function __addToFbDialog(str, callback, callbackParams, ctx) {
        log('Add to FbDialog');
        if (_fbDialogBox !== null) {
            _fbDialogBox.appendTo(str, callback, callbackParams, ctx);
        }
    }

    function __clearFbDialogContent(callback, callbackParams, ctx) {
        if (_fbDialogBox !== null) {
            _fbDialogBox.clearContent(callback, callbackParams, ctx);
        }
    }

    function __selfDestruct() {
        log('', 'Self Destruct!!');
        CTLR.EL.remove();
    }

    //Public Properties/Methods
    return {
        //Public Properties
        APP_ID: 146207698825285,
        APP_URL: 'http://staging.roundabouttheatre.org',
        EVENT_NAME: '',
        EVENT_PIC: '',
        EVENTS_OBJECT: {},
        EVENTS_ARRAY: [],
        EVENTS_STRING: '',
        EVENT_LOCATION: '',
        EVENT_ADDRESS: '',
        EVENT_URL: '',
        USER_INFO: {},
        USER_FRIENDS: {},
        USER_FRIENDS_ARRAY: [],
        USER_EVENTS_ARRAY: [],
        SESSION_KEY: null,
        FRIENDS_LENGTH: 0,
        MAX_FRIEND_INVITE: 25,
        EL: [],

        //Exposed Methods
        init: function (obj) {
            __init(obj);
        },
        fbGetLoginStatus: function (bool, callback, callbackParams, ctx) {
            __fbGetLoginStatus(bool, callback, callbackParams, ctx);
        },
        fbPostEventToWall: function (obj) {
            __fbPostEventToWall(obj);
        },
        fbSendFriendInvite: function (obj, callback, callbackParams, ctx) {
            __fbSendFriendInvite(obj, callback, callbackParams, ctx);
        },
        fbSetEventRsvp: function (evt, str) {
            __fbSetEventRsvp(evt, str);
        },
        //'Facebook pseudo' dialog box Methods
        openFbDialog: function (obj, callback, callbackParams, ctx) {
            __openFbDialog(obj, callback, callbackParams, ctx);
        },
        closeFbDialog: function (callback, callbackParams, ctx) {
            __closeFbDialog(callback, callbackParams, ctx);
        },
        addToFbDialog: function (str, callback, callbackParams, ctx) {
            __addToFbDialog(str, callback, callbackParams, ctx);
        },
        clearFbDialogContent: function (callback, callbackParams, ctx) {
            __clearFbDialogContent(callback, callbackParams, ctx);
        },
        setIsModalOpen: function (bool) {
            _isModalOpen = bool;
        },
        getIsModalOpen: function () {
            return _isModalOpen;
        }
    }

} ());

/**
*	User Attending 
**/
POP.FB.UserAttending = function (controller) {
    var _self = this,
		_el = [],
		CTLR = controller, //Get a reference to the POP.FB.controller object
		eventsList = null, //
		eventInviteList = null;

    __init();

    function __init() {
        log('', 'init POP.FB.UserAttending')
        _el = CTLR.EL;
        __addEventListeners();
    }

    function __onUpdate() {
        __buildUserAttendingList(CTLR.USER_EVENTS_ARRAY);
        __buildFriendsAttendingList(CTLR.USER_EVENTS_ARRAY);
    }

    function __addEventListeners() {
        var bool;

        CTLR.EL.delegate('.fb-btn-attend a', 'click', function (e) {
            e.preventDefault();
            bool = CTLR.getIsModalOpen();
            log(bool, 'IS MODAL OPEN');
            if (!bool) {
                __onAttendingBtnClick(e);
            }
        });

        CTLR.EL.delegate('.fb-btn-maybe a', 'click', function (e) {
            e.preventDefault();
            bool = CTLR.getIsModalOpen();
            if (!bool) {
                __onMaybeBtnClick(e);
            }
        });

        CTLR.EL.delegate('a.fb-send-invite', 'click', function (e) {
            e.preventDefault();
            bool = CTLR.getIsModalOpen();
            if (!bool) {
                __onSendEventBtnsClick(e);
            }
        });
    }

    function __onAttendingBtnClick(e) {
        CTLR.fbGetLoginStatus(true, __initEventsList, ['attending'], _self);
    }

    function __onMaybeBtnClick(e) {
        CTLR.fbGetLoginStatus(true, __initEventsList, ['maybe'], _self);
    }

    function __onSendEventBtnsClick(evt) {
        var eid = $(evt.currentTarget).attr('data-id');
        CTLR.fbGetLoginStatus(true, __initInviteList, [eid], _self);
    }

    function __initEventsList(str) {
        //__onUpdate();
        CTLR.openFbDialog({
            title: 'Select Performance',
            sendBtnClass: 'fb-rsvp-btn'
        }, function () {
            eventsList = new POP.FB.EventsList(CTLR, str);
        }, null, _self);
    }

    function __initInviteList(eid) {
        log('', 'Init Invite List');
        CTLR.openFbDialog({
            title: 'Send Performance Invitation',
            sendBtnClass: 'fb-send-invite-btn'
        }, function () {
            eventInviteList = new POP.FB.EventInviteList(CTLR, eid);
        }, null, _self);
    }

    function __setUserQuestion(bool) {
        var el = $('.fb-question-box'),
			img = $('.fb-user-avatar'),
			elText = '';

        if (CTLR.USER_INFO.id !== undefined) {
            img.attr('src', 'https://graph.facebook.com/' + CTLR.USER_INFO.id + '/picture');
        }
        elText += '<span>';
        elText += CTLR.USER_INFO.name;
        elText += ', are you going to see ';
        if (bool) {
            elText += 'another ';
        } else {
            elText += 'a ';
        }
        elText += CTLR.EVENT_NAME;
        elText += ' performance?';
        elText += '</span>';
        el.html($(elText));
    }

    //Populate the list of performances the user is attending (related to current performance)
    function __buildUserAttendingList(arr) {
        log(arr, '__buildUserAttendingList')
        var ul = $('.fb-user-events ul'),
			li = '',
			len = arr.length,
			id = CTLR.USER_INFO.id;

        if (len > 0) {
            __setUserQuestion(true);
        } else {
            __setUserQuestion(false);
        }
        for (var i = 0; i < len; i++) {
            if (arr[i].uid === id && arr[i].rsvp_status !== 'not_replied') {
                li += '<li>You ';
                if (arr[i].rsvp_status === 'attending') {
                    li += 'are ';
                } else {
                    li += 'may be ';
                }
                li += 'attending ';
                li += ' on ';
                li += CTLR.EVENTS_OBJECT[arr[i].eid];
                li += '<a href="" data-id="' + arr[i].eid + '" class="fb-send-invite">Send Event Invitation</a></li>';
            }
        }
        ul.html($(li));
    }

    //adds another list item to those friends attending an event
    function __buildFriendsAttendingList(arr) {
        var ul = $('.fb-friends-list ul'),
			li = '',
			len = arr.length;

        for (var i = 0; i < len; i++) {
            for (var key in CTLR.USER_FRIENDS) {
                if (arr[i].uid === key && arr[i].rsvp_status !== 'not_replied') {
                    li += '<li><div class="fb-user-pic">';
                    li += '<img src="http://graph.facebook.com/' + arr[i].uid + '/picture" />';
                    li += '</div><div class="fb-friend-rsvp">';
                    li += '<a href="http://facebook.com/' + arr[i].uid + '" target="_blank">';
                    li += CTLR.USER_FRIENDS[arr[i].uid];
                    li += '</a> ';
                    if (arr[i].rsvp_status === 'attending') {
                        li += ' is ';
                    } else {
                        li += ' maybe is ';
                    }
                    li += 'attending on ';
                    li += CTLR.EVENTS_OBJECT[arr[i].eid];
                    li += ' </div></li>';
                }
            }
        }
        ul.html($(li));
    }

    //Public Methods/Properties
    return {
        update: function () {
            __onUpdate();
        }
    }
}

/**
*	Event Invite List
**/
POP.FB.EventInviteList = function (controller, eid) {
    //Private Properties
    var _self = this,
		CTLR = controller, //Get a reference to the POP.FB.controller object
		_eid = eid; //comes in as Array need the Int value

    __init();

    //Private Methods
    function __init() {
        log('', 'Init EventInviteList Object');
        __buildFriendsList(CTLR.USER_FRIENDS);
    }

    function __onUpdate() { }

    function __addEventListeners() {
        var sendEventBtn = $('a.fb-send-invite-btn'),
			cancelBtn = $('a.fb-cancel-btn'),
            closeBtn = $('a.fb-modal-close-btn'),
			eventListUl = $('ul.fb-send-invite-friends'),
			bool;

        sendEventBtn.bind('click', function (e) {
            e.preventDefault();
            __onSendEventBtnClick(e);
        });

        cancelBtn.bind('click', function (e) {
            e.preventDefault();
            __onCancelBtnClick(e);
        });

        closeBtn.bind('click', function (e) {
            e.preventDefault();
            __onCancelBtnClick(e);
        });

        eventListUl.delegate('li', 'click', function (e) {
            e.preventDefault();
            __onEventThumbClick(e);
        });
    }

    function __onEventThumbClick(e) {
        $(e.currentTarget).toggleClass('selected');
    }

    function __onSendEventBtnClick(e) {
        var list = $('ul.fb-send-invite-friends li.selected'),
			arr = [];
        list.each(function (i, el) {
            if (i > CTLR.MAX_FRIEND_INVITE - 1) {
                return;
            }
            arr[i] = Number($(el).attr('data-id'));
        });
        //No need to send invite if nothing is selected
        if (arr.length > 0) {
            CTLR.fbSendFriendInvite({ arr: arr, eid: _eid }, __onSendFriendInviteCallback, [], _self);
            CTLR.clearFbDialogContent();
            CTLR.addToFbDialog('<div class="fb-loading-gif">');
        }
    }

    function __onCancelBtnClick(evt) {
        CTLR.closeFbDialog();
    }

    function __buildFriendsList(obj) {
        var ul = $('#fb-send-invite-modal ul'),
			html = '', i = 0;

        html += '<div id="fb-send-invite-modal" class="fb-appended-content"><div class="fb-send-invite-hdr"><div class="fb-user-pic"><img src="http://graph.facebook.com/';
        html += CTLR.USER_INFO.id;
        html += '/picture" /></div><div class="fb-send-invite-hdr-text"><h3>Select Friends to Invite to the ';
        html += CTLR.EVENT_NAME;
        html += ' Performance on ' + CTLR.EVENTS_OBJECT[_eid] + '</h3><h4>Invite up to 25 friends by clicking on the thumbs below.</h4></div><div class="clearfix"></div></div><div class="fb-send-invite-body ';
        if (CTLR.FRIENDS_LENGTH < 13) { //if number of friends is less than 13 then hide scrollbars
            html += 'hideScroll';
        }
        html += '"><ul class="fb-send-invite-friends">';
        for (var key in obj) {
            html += '<li data-id="' + key + '"><img src="http://graph.facebook.com/' + key + '/picture" /><span>' + obj[key] + '</span></li>';
        }
        html += '</ul></div><div class="clearfix"></div></div>';
        CTLR.addToFbDialog(html);
        __addEventListeners();
    }

    function __onSendFriendInviteCallback(obj) {
        CTLR.closeFbDialog();
    }

    //Public Methods/Properties
    return {
        update: function () {
            __onUpdate();
        }
    }
};

/**
*	Events List Dialog
**/
POP.FB.EventsList = function (controller, str) {
    //Private Properties
    var _self = this,
		CTLR = controller, //Get a reference to the POP.FB.controller object
		_str = str; //This is the rsvp string based on button clicked (attending, unsure)

    __init();

    function __init() {
        log('', 'Init POP.FB.EventsList')
        __buildEventsList(CTLR.EVENTS_OBJECT);
    }

    function __onUpdate() { }

    function __addEventListeners() {
        log('', 'Add Listeners Event List')
        var rsvpToEventBtn = $('a.fb-rsvp-btn'),
			cancelBtn = $('a.fb-cancel-btn'),
            closeBtn = $('a.fb-modal-close-btn'),
			bool;

        rsvpToEventBtn.bind('click', function (e) {
            e.preventDefault();
            __onRsvpToEventBtnClick(e);
        });

        cancelBtn.bind('click', function (e) {
            e.preventDefault();
            __onCancelBtnClick(e);
        });

        closeBtn.bind('click', function (e) {
            e.preventDefault();
            __onCancelBtnClick(e);
        });

    }

    function __onRsvpToEventBtnClick(evt) {
        var obj = {},
			select = {},
			selected = {};
        select = $('select.fb-events-select');
        selected = select.find('option:selected');
        obj.id = selected.val();
        obj.str = _str;
        CTLR.fbSetEventRsvp(obj);
        CTLR.closeFbDialog(CTLR.fbPostEventToWall, [obj.id], CTLR);
    }

    function __onCancelBtnClick(evt) {
        CTLR.closeFbDialog();
    }

    function __buildEventsList(obj) {
        var html = '';
        html = '<div id="fb-event-modal" class="fb-appended-content"><div class="fb-user-pic"><img src="http://graph.facebook.com/';
        html += CTLR.USER_INFO.id;
        html += '/picture" /></div><div class="fb-events-list"><h3>Which ';
        html += CTLR.EVENT_NAME;
        html += ' performance are you attending?</h3><select class="fb-events-select">';
        for (var key in obj) {
            html += '<option value="';
            html += key;
            html += '"';
            html += '>';
            html += obj[key];
            html += '</option>';
        }
        html += '</select></div><div class="clearfix"></div></div>';

        CTLR.addToFbDialog(html);
        __addEventListeners();
    }

    //Public Methods/Properties
    return {
        update: function () {
            __onUpdate();
        },
        buildEventsList: function (obj) {
            __buildEventsList(obj);
        }
    }
}

POP.FB.FbDialogBox = function (controller) {
    var _self = this,
		CTLR = controller,
		_options = {
		    containerEl: '#fb-modal'
		},
		_modalHtml = {},
		_animate = (jQuery.support.opacity) ? true : false;

    __init();

    function __init() {
        __addEventListeners();
    }

    function __addEventListeners() {
        $(_options.containerEl).delegate('a.fb-cancel-btn', 'click', function (e) {
            e.preventDefault();
            __close();
        })
    }

    function __positionModal() {
        var el = $(_options.containerEl),
			eh = el.height(),
			ew = el.width(),
		 	wh = $(window).height(),
			ww = $(window).width(),
			mh = Math.ceil(wh - eh) / 2 + $(window).scrollTop() + 'px',
			mw = Math.ceil(ww / 2 - ew / 2) + 'px';

        el.css({
            'left': mw,
            'top': mh,
            'position': 'absolute'
        });
    }

    function __open(obj, callback, callbackParams, ctx) {
        log('', 'FbDialogBox.open()')
        var html;
        CTLR.setIsModalOpen(true);
        html = '';
        html += '<div id="fb-modal" class="fadedOut"><div class="fb-modal-upper-left"></div><div class="fb-modal-upper-mid"></div><div class="fb-modal-upper-right"></div><div class="fb-modal-mid-body"><div class="fb-modal-mid-body-top"><div class="fb-modal-header"><div class="fb-modal-icon ir">Facebook Icon</div><h2>';
        html += obj.title;
        html += '</h2><a href="" class="fb-modal-close-btn cancelBtn">x</a></div><div class="fb-modal-content"><div class="fb-loading-gif"><img src="/_ui/img/facebook/fb-loading.gif"></div></div></div><div class="fb-modal-mid-body-btm"><div class="fb-modal-btns"><ul><li class="fb-modal-submit-btn"><a href="#" class="fb-send-btn ';
        html += obj.sendBtnClass || '';
        html += '">';
        html += obj.sendBtnTxt || 'Send';
        html += '</a></li><li class="fb-modal-cancel-btn"><a href="#" class="fb-cancel-btn ';
        html += obj.cancelBtnClass || '';
        html += '">';
        html += obj.cancelBtnTxt || 'Cancel';
        html += '</a></li></ul></div></div></div><div class="fb-modal-lower-left"></div><div class="fb-modal-lower-mid"></div><div class="fb-modal-lower-right"></div></div>';
        _modalHtml = $(html);
        $('body').append(_modalHtml);
        //__onScroll(); //position window
        __positionModal();
        if (_animate) {
            _modalHtml.fadeIn(200, function () {
                if (typeof callback === 'function') {
                    ctx = ctx || this;
                    callbackParams = callbackParams || [];
                    callback.apply(ctx, callbackParams);
                }
            });
        } else {
            _modalHtml.show();
            if (typeof callback === 'function') {
                ctx = ctx || this;
                callbackParams = callbackParams || [];
                callback.apply(ctx, callbackParams);
            }
        }
    }

    function __close(callback, callbackParams, ctx) {
        CTLR.setIsModalOpen(false);
        if (_animate) {
            _modalHtml.fadeOut(200, function () {
                $(this).remove();
                if (typeof callback === 'function') {
                    ctx = ctx || this;
                    callback.apply(ctx, callbackParams);
                }
            });
        } else {
            _modalHtml.hide();
            _modalHtml.remove();
            if (typeof callback === 'function') {
                ctx = ctx || this;
                callback.apply(ctx, callbackParams);
            }
        }
    }

    function __appendTo(str, callback, callbackParams, ctx) {
        var contentEl = $(_modalHtml.find('.fb-modal-content'));
        contentEl.html($(str));
        if (typeof callback === 'function') {
            ctx = ctx || this;
            callback.apply(ctx, callbackParams);
        }
    }

    function __clearContent(callback, callbackParams, ctx) {
        var contentEl = $(_modalHtml.find('.fb-modal-content'));
        contentEl.empty();
        if (typeof callback === 'function') {
            ctx = ctx || this;
            callback.apply(ctx, callbackParams);
        }
    }

    return {
        open: function (obj, callback, callbackParams, ctx) {
            __open(obj, callback, callbackParams, ctx);
        },
        close: function (callback, callbackParams, ctx) {
            __close(callback, callbackParams, ctx);
        },
        appendTo: function (obj, callback, callbackParams, ctx) {
            __appendTo(obj, callback, callbackParams, ctx);
        },
        clearContent: function (callback, callbackParams, ctx) {
            __clearContent(callback, callbackParams, ctx)
        }
    }
};
