/**
 * General layout - not everything conforms to this yet
 *
 * s.foo.elements - {} - cache jQ elements
 * s.foo.init - function - called on page load - setup live events and those relating to containters etc
 * s.foo.activate - function - called when relavent content is ajax loaded - for things that can't be live events (eg. tabs)
 * s.foo.scrollPane_settings - {} - settings for custom scrollbars in this area, usually same as global default
 * s.foo.show - function - triggered by fragment changes, handles loading, showing and activating foo
 * s.foo.hide - function - triggered by fragment changes
 * s.foo.resize - function - trigger when window is resized and current view is foo
 * ...
 * other foo-spectific functions
 *
 * where possible, all navigation should use s.view.go(params);
 * this calls s.view.get_url to set the url, change the hash and the read it back via s.view.process_fragment
 */

(function(window, undefined) {
    var loaded = false, // changed on global init, used to get around IE fail
    s = {};

    s.api_url = '/api';
    s.base_url = 'http://sna.pr/';
    s.foursquare_url="https://foursquare.com/oauth2/authenticate?client_id=KVSW2BKWNBV43Q2ZOU1UUJET2SO2ASL1OZOTA5KA3GJA3PER&response_type=code&redirect_uri=https%3a%2f%2fsna.pr%2fvweb%2ffoursquare_return%2f";

    // a bit of a haxy override for now
    // $.data interprets data-code="10E6" as an int in scientific notation without this
    $.isNaN = function( obj ) {
        return obj == null || !/\d/.test( obj ) || /E+/.test( obj ) || isNaN( obj );
    }

    // use $.cookie('log', true) and $.cookie('log', false) to turn logging to console on and off.
    var log = $.cookie('log') && console.log ||
    function() {};

    $.ajaxSetup({
        dataType: 'json'
    });

    $.validator.setDefaults({
        success: function(label) {
            $(label).parent().addClass('validate-success').removeClass('validate-loading')
            .find('label.error').remove();
        },
        highlight: function(element, errorClass) {
            $(element).parent().removeClass('validate-success').removeClass('validate-loading');
        }
    });

    /**
 * Cache jQ element after first get(), don't cache if it's not in the DOM anymore, unless 'sticky'
 */
    function cached_jQ(selector, sticky) {
        this.elements = null;
        // element cache
        this.selector = selector;
        this.sticky = !!sticky;
        this.get = function() {
            // clear cache if element isn't in the DOM anymore.
            if (!sticky && this.elements && !this.elements.parents().eq( - 1).parent().length) {
                this.elements = null
            }
            this.elements = this.elements || $(this.selector);
            return this.elements;
        }
        this.clear_cache = function() {
            this.elements = null;
        };
    };

    function api_date(javascript_date) {
        //yyyy-MM-dd HH:mm:ss
        return $.PHPDate('Y-m-d H:i:s', javascript_date);
    }

    s.scrollPane_settings = {
        showArrows: true
    };
    s.static_bubbles = [
    //#/<static_bubbles word> triggers bubble
    'api-test',
    'oauth',
    'oauth/my-apps',
    'info',
    'terms',
    'contact',
    'pages',
    'settings',
    'iphone',
    'android',
    'platform',
    'news',
    'support',
    'feedback',
    'my-maps',
    'my-map',
    'my-snaps',
    'mobile',
    'my-account',
    'my-settings',
    'about',
    'my-bookmarks',
    'my-favorites',
    'my-friends',
    'my-contacts',
    'my-followers',
    'my-groups',
    'groups',
    'slideshow',
    'my-slideshows',
    'ar',
    'tag',
    'tags',
    'my-tags',
    'share',
    'stories',
    'maps',
    'people',
    'popular',
    'advanced-search',
    'profile',
    'edit',
    'now',
    'world',
    'latest',
    'zoom',
    'play',
    'time',
    'player',
    'place',
    'places',
    'location',
    'type',
    'bookmarks',
    'game',
    'games',
    'my-games',
    'join-snapr',
    'forgot-password',
    'no-snaps',
    'login'
    ];

    s.reserved_words = s.static_bubbles.concat([
    // words that can't be usernames
    'followers',
    'favorites',
    'search',
    'cities'
    ]);

    load_bubble = null;

    // handles changing and interpreting changes to the fragment (url hash)
    // also responsible for determining how to act on the change
    s.view = {

        'view': 'map',
        // map or dash
        'subview': null,
        // null, photo, gallery, thumbs, people, static bubble
        // list controls search params when a list of photo is shown, eg "more images" list in photo_bubble
        //
        // area - map area - s.map.map.getBounds().toUrlValue(5), s.map.date
        // user - user's photos - s.user.username
        // search - tag search - s.search.query
        // stream - a dash stream's photos - dashboard.stream
        // favs - favorites list - s.user.username
        // feed - user's following feed - s.user.username
        'list': 'area',

        'default_params': function() {
            return {
                'view': 'map',
                'subview': null,
                'list': 'area',
                'bubble': null,
                'bubble_tab': null,
                'code': null,
                'username': null,
                'filter_map': false,
                'search': null,
                'filter_people': null,
                'user_search': null,
                'stream': null,
                'map': {
                    'location': null,
                    'zoom': null,
                    'date': null
                }
            }
        },

        'get_current_params': function() {
            return {
                'view': s.view.view,
                'subview': s.view.subview,
                'list': s.view.list,
                'bubble': s.bubble.path,
                'bubble_tab': s.bubble.tab,
                'code': s.image.code,
                'username': s.user.username,
                'filter_map': s.map.filter,
                'search': s.search.query,
                'filter_people': s.people.filter,
                'user_search': s.people.query,
                'stream': s.dashboard.current_stream,
                'map': {
                    'location': s.map.location,
                    'zoom': s.map.zoom,
                    'time': s.map.date
                },
                'city': s.cities.city
            };
        },

        /**
     * changes the url to reflect the updated parameters in params
     * parameters not supplied in params are left unchanged.
     * @argument params {object} params to change in new url
     */
        'go': function(params) {
            log('view.go new params: ', params);
            window.location.hash = s.view.get_url($.extend(s.view.get_current_params(), params));
        },

        /**
     * calls on process_fragment to compare new url to current state and make
     * changes to move to new state
     * @argument e {hashchange event} optional, not used currently.
     * @argument first_run {bool} optional, true if this is the first call of
     * this function. Some things need to be done when entering the page at a
     * specific url, eg. changing the map filtering display.
     */
        'change': function(e, first_run) {
            // console.warn('change',e,first_run);
            var new_params = s.view.process_fragment();
            // console.log('new_url_params', new_params);

            // if (!s.auth.username &&
            //     (new_params.view == 'dash') &&
            //     (['login', 'terms', 'about', 'join-snapr'].indexOf(new_params.bubble) == -1)) {
            //     //not logged in -- redirect all dash urls except those above to login if the user is not logged in
            //     // console.warn('redirect');
            //     // s.auth.redirect.destination.url = window.location.hash;
            //     s.auth.redirect.destination.message = "Please log in or sign up to see your dashboard.";
            //     if (new_params.view === 'dash') {
            //         s.dashboard.toggle = s.dashboard.show;
            //     } else {
            //         // console.warn('s.dashboard.toggle = s.dashboard.hide')
            //         s.dashboard.toggle = s.dashboard.hide;
            //     }
            //     window.location.hash = '#/dash/';
            //     s.view.change();
            // } else {
                //logged in

                // view
                if (s.view.view !== new_params.view) {
                    // if changed
                    // console.log('view changed from', s.view.view, 'to', new_params.view);
                    if (new_params.view === 'dash') {
                        s.dashboard.toggle = s.dashboard.show;
                    } else {
                        // console.warn('s.dashboard.toggle = s.dashboard.hide')
                        s.dashboard.toggle = s.dashboard.hide;
                    }
                    s.view.view = new_params.view;
                }
                if (s.view.view == new_params.view && new_params.view == 'dash'){
                    // this happens when we're on the dash and we refresh the page
                    s.dashboard.show();
                }

                // if code changed, refresh gallery and/or bubble if open
                if (new_params.code && s.image.code !== new_params.code) {
                    log('new image: ', new_params.code);
                    if (s.view.subview === new_params.subview && new_params.subview === 'gallery') {
                        log('load new gallery photo');
                        s.gallery.show(new_params.code);
                    } else if (s.view.subview === new_params.subview && new_params.subview === 'photo') {
                        log('load new photo bubble');
                        s.image.load(new_params.code);
                    } else if (s.view.subview === new_params.subview && new_params.subview === 'video') {
                        log('load new video');
                        s.video.show(new_params.code);
                    }
                }
                s.image.code = new_params.code;

                // if if username changed reload bubble, thumbs, gallery, map
                if (new_params.username && new_params.username !== s.user.username || s.user.username && !new_params.username || new_params.username && !new_params.code) {
                    console.log('user', s.user.username, new_params.username);
                    if(s.view.list == 'user' && new_params.list == 'user'){
                        s.view.list = '';
                        new_params.list = 'user';
                    }
                }
                s.user.username = new_params.username;

                // stream
                if (new_params.list == 'popular') {
                    var query_string = 'sort=favorite_count';
                }
                if (new_params.list == 'feed') {
                    var query_string = 'group=following&not_by=.';
                }
                if (new_params.search) {
                    var query_string = 'keywords=' + new_params.search;
                }
                if (new_params.username) {
                    var query_string = 'username=' + new_params.username;
                }

                s.dashboard.current_stream = query_string || null;

                if (new_params.search && s.search.query !== new_params.search) {
                    // refresh current view
                    }
                s.search.query = new_params.search;

                // if list changed reload bubble or thumbs if open and map
                if (new_params.list && s.view.list !== new_params.list) {
                    log('new list: ', new_params.list);
                    s.view.list = new_params.list;

                    if (s.view.subview === new_params.subview && (new_params.subview === 'thumbs')) {
                        log('load new thumbs list');
                        if(new_params.map.date){
                            s.thumbs.show(new_params.map.date);
                        }else{
                            s.thumbs.show();
                        }
                    } else if (s.view.subview === new_params.subview && new_params.subview === 'photo') {
                        log('load new more_images');
                        s.image.show(new_params.code);
                    }
                } else {
                    if (new_params.list == 'search' && new_params.search && new_params.subview == 'thumbs') {
                        if(new_params.map.date){
                            s.thumbs.show(new_params.map.date);
                        }else{
                            s.thumbs.show();
                        }
                    }
                }

                // if filter map changed update map
                if (new_params.filter_map) {
                    switch (new_params.filter_map.type) {
                    case 'user':
                        $('.x-map-people-select').attr('value', new_params.filter_map.username);
                        break;
                    case 'following':
                        $('.x-map-people-select').attr('value', 'Following');
                        break;
                    }
                } else {
                    $('.x-map-people-select').attr('value', 'Everyone');
                }
                // s.map.filter = new_params.filter_map;
                if (s.map.filter !== new_params.filter_map) {
                    s.map.filter = new_params.filter_map;
                    s.map.get_images();
                }

                if(new_params.map.date){
                    s.map.time.set(new_params.map.date)
                }

                // if bubble changed, reload bubble
                if (new_params.bubble && s.bubble.path !== new_params.bubble) {
                    s.image.hide();
                    //console.log('bubble show: ', new_params.bubble,new_params)
                    s.bubble.path = new_params.bubble;
                    s.bubble.show();
                }
                if (s.bubble.path && !new_params.bubble) {
                    $('#modal').dialog('close');
                    $('#modal').empty();
                }
                // TODO
                // if map changed, move
                // console.warn((s.view.list !== 'area'),s.view.list);
                if (s.map.map && new_params.map && (s.view.list == 'area')) {
                    var ll = s.map.map.getCenter().toUrlValue(5).split(',');
                    if (new_params.map.location && new_params.map.zoom) {
                        var nll = new_params.map.location.split(',');
                        if ((ll[0] !== nll[0]) || (ll[1] !== nll[1]) || (s.map.zoom !== new_params.map.zoom)) {
                            s.map.go_to(nll[0], nll[1], new_params.map.zoom);
                        }
                    } else {
                        if (new_params.map.zoom) {
                            s.map.go_to(ll[0], ll[1], new_params.map.zoom);
                        }
                    }
                }else{
                    if (s.map.map && new_params.map && new_params.map.zoom != s.map.zoom) {
                        s.map.go_to(false, false, new_params.map.zoom);
                    }
                }

                // if search changed, reload bubble, thumbs, gallery, map
                // if filter prople or user_search changed, reload people bubble
                // if subview changed
                if (s.view.subview !== new_params.subview) {
                    log('subview changed from', s.view.subview, 'to', new_params.subview);
                    // from
                    switch (s.view.subview) {
                    case 'gallery':
                        s.gallery.query = null;
                        s.gallery.hide();
                        break;
                    case 'thumbs':
                        s.thumbs.hide();
                        break;
                    case 'video':
                        s.video.hide();
                        break;
                    case 'static bubble':
                        s.bubble.path = null;
                        s.bubble.hide();
                        break;
                    case 'people':
                        s.people.hide();
                        break;
                    case 'cities':
                        s.cities.hide();
                        break;
                    }
                    // to
                    switch (new_params.subview) {
                    case 'gallery':
                        s.gallery.show(new_params.code);
                        break;
                    case 'cities':
                        s.cities.city = new_params.city;
                        s.cities.show(s.cities.city);
                        break;
                    case 'thumbs':
                        s.view.list = new_params.list;
                        if(first_run && s.view.list == 'area'){
                            var listenerHandle = google.maps.event.addListener(s.map.map, 'bounds_changed', function() {
                            console.warn('first run')
                            if(new_params.map.date){
                                s.thumbs.show(new_params.map.date);
                            }else{
                                s.thumbs.show();
                            }
                            google.maps.event.removeListener(listenerHandle);
                            });
                        }else{
                            if(new_params.map.date){
                                s.thumbs.show(new_params.map.date);
                            }else{
                                s.thumbs.show();
                            }
                        }
                        break;
                    case 'video':
                        s.video.show(new_params.code);
                        break;
                    case 'photo':
                        if(first_run && !new_params.map.zoom){
                            s.map.map.zoom = 15;
                        }
                        s.image.show(new_params.code);
                        break;
                    case 'people':
                        s.image.hide();
                        s.people.show();
                        break;
                    case false:
                    case null:
                    case undefined:
                    case '':
                        s.image.hide();
                        break;
                    }

                    s.view.subview = new_params.subview;
                }

                if (s.view.subview == 'cities' && s.cities.city !== new_params.city) {
                    s.cities.city = new_params.city;
                    s.cities.show(s.cities.city);
                }

                if (s.dashboard.toggle && !first_run) {
                    // console.warn('toggle',s.dashboard.toggle);
                    s.dashboard.toggle();
                    s.dashboard.toggle = null;
                }else{
                    // console.warn('don\'t toggle', s.dashboard.toggle, new_params)
                    s.dashboard.toggle = null;
                }

                if (e) {
                    return new_params;
                }

            // }

        },

        /**
     * generates a url from a full list of view parameters
     * @argument params {object} full list of view parameters parameters
     * @returns {string} fragment (url hash) beginning with #/
     */
        'get_url': function(params) {
            var url = '#/';
            // dash/
            url += params.view == 'dash' && 'dash/' || '';
            // username/
            url += params.username && params.username.toLowerCase() + '/' || '';
            // feed/
            url += params.list == 'feed' && 'feed/' || '';
            // favorites/
            url += params.list == 'favorites' && 'favorites/' || '';
            // popular/
            url += params.list == 'popular' && 'popular/' || '';
            // following
            url += params.list == 'following' && 'following/' || '';
            // followers/
            url += params.filter_people == 'followers' && 'followers/' || '';
            // user-search/query/
            url += params.user_search && 'people/' || '';
            // url += params.user_search && 'user-search/'+params.user_search+'/' || '';
            // search/query/
            url += params.search && 'search/' + params.search + '/' || '';
            // map filter
            // url += params.filter_map && 'map/' || '';
            // CODE/
            url += params.subview && params.code && params.code + '/' || '';
            // cities
            url += params.subview == 'cities' && 'cities/' || '';
            url += params.city && params.subview == 'cities' && params.city + '/' || '';
            // gallery/
            url += params.subview == 'gallery' && 'gallery/' || '';
            // thumbs/
            url += params.subview == 'thumbs' && 'thumbs/' || '';
            // video/
            url += params.subview == 'video' && 'video/' || '';

            // reserved_word/#tab
            url += params.bubble && params.bubble + '/' || '';
            url += params.bubble_tab && params.bubble_tab.length && params.bubble_tab.join('/') + '/' || '';

            // map params
            if (s.map.map) {
                if (params.view == 'map' && params.subview !== 'cities' && (params.map.location || params.map.zoom || params.map.date || params.filter_map || params.map.area)) {
                    var filters = [];
                    if (params.view === 'map' && params.subview == 'thumbs' && params.map.area) {
                        filters.push('area=' + params.map.area);
                    }else if (params.map.location && !params.map.area) {
                        if (params.view === 'map' && !params.code && params.map.location) {
                            filters.push('ll=' + params.map.location);
                        }
                    }

                    if (params.map.zoom) {
                        filters.push('z=' + params.map.zoom);
                    }
                    if (params.map.date && (params.view === 'map' && !params.subview || params.subview == 'thumbs')) {
                        filters.push('t=' + encodeURIComponent(params.map.date));
                    }

                    // following/user filter
                    if (params.filter_map) {
                        switch (params.filter_map.type) {
                        case 'user':
                            filters.push('username=' + params.filter_map.username);
                            break;
                        case 'group':
                            filters.push('group=' + params.filter_map.group);
                            break;
                        }
                    }
                }
            }

            var filters = filters || [];

            if (params.view == 'dash' && params.map.date && params.subview == 'thumbs') {
                filters.push('t=' + escape(params.map.date));
            }
            if (filters.length && params.subview !== 'video' && params.subview !== 'gallery') {
                url += '?' + filters.join('&')
            }

            return url
        },
        /**
     * NOT YET IMPLIMENTED
     * generates window title from a full list of view parameters
     * @argument params {object} full list of view parameters parameters
     * @returns {string} window title
     */
        'get_title': function(params) {
            var title = 'snapr: ';

            return title;
        },

        /**
     * process fragemnt into view params
     * @argument fragment {string} optional, defaults to current fragment
     * @returns {object} view params
     */
        'process_fragment': function(fragment) {

            // It may be possible/more efficient to do this with regex but an expression like
            // ^#(?:/(dash))?(?:/([a-z][a-z0-9]+))?(?:/(map)/)?(?:/([A-Z0-9]+))?(?:/(gallery))?(?:/(thumbs))?/?$
            // will match /dash/thumbs/ as if thumbs was a username.
            fragment = fragment || window.location.hash;

            var processed = $.extend(true, {},
            s.view.default_params()),
            parts = fragment.split('?');
            // seperate path from params
            // get map params if they exist
            if (parts.length > 1) {
                $.each(parts[1].split('&'),
                function(i, v) {
                    var kv = v.split('=');
                    switch (kv[0]) {
                    case 'll':
                        processed.map.location = kv[1].replace('%2C', ',');
                        break;
                    case 'z':
                        processed.map.zoom = parseInt(kv[1], 10);
                        break;
                    case 't':
                        processed.map.date = decodeURIComponent(kv[1]);
                        break;
                    case 'group':
                        processed.filter_map = {
                            'type': 'group',
                            'group': kv[1]
                        };
                        break;
                    case 'username':
                        processed.filter_map = {
                            'type': 'user',
                            'username': kv[1]
                        };
                        break;
                    }
                });
            }

            // get path
            path = [];
            $.each(parts[0].split('/'),
            function(i, v) {
                if ((v !== '') && (v !== '#')) {
                    path.push(v);
                }
            });

            if (!path.length) {
                return processed;
            }

            // view
            processed.view = path[0] === 'dash' && path.shift() || 'map';
            if (!path.length) {
                return processed;
            }

            // subview - static bubble
            if ($.inArray(path[0], s.static_bubbles) !== -1) {
                processed.subview = 'static bubble';
                processed.bubble = path.shift();
                processed.bubble_tab = path;

                if (processed.bubble == 'popular') {
                    processed.bubble = '';
                    processed.bubble_tab = '';
                    processed.subview = 'photo';
                    processed.list = 'popular';
                    // console.warn('path', path);
                    if (['today', 'week', 'month', 'nearby'].indexOf(path[0]) > -1) {
                        processed.bubble_tab = path.shift();
                    }
                    // console.warn('popular', processed);

                    if (path.length) {
                        // code
                        processed.code = path[0] !== path[0].toLowerCase() && path.shift() || null;
                    }

                    if (path.length) {
                        if (path[0] == 'thumbs') {
                            processed.subview = path.shift();
                        }
                        if (path[0] == 'gallery') {
                            processed.subview = path.shift();
                        }
                        if (path[0] == 'video') {
                            processed.subview = path.shift();
                        }
                    }
                }

                return processed;
            }

            // subview - gallery, thumbs
            processed.subview = $.inArray(path[path.length - 1], ['gallery', 'thumbs', 'video']) !== -1 && path.pop() || 'photo';
            if (!path.length) {
                return processed;
            }

            // subview - people - user_search
            // if(path[0] === 'user_search'){
            //     processed.list = 'user search';
            //     processed.subview = 'people';
            //     processed.user_search = path.length && path.shift() || null;
            // }
            // if(!path.length){ return processed; }
            if (path[0] === 'cities') {
                processed.subview = path.shift();
                if (s.cities.top_cities[path[0]]) {
                    processed.city = path.shift();
                }
            }
            if (!path.length) {
                return processed;
            }

            // list - search
            if (path[0] === 'search') {
                processed.list = path.shift();
                processed.search = path.length && path.shift() || null;
            }
            if (path[0] === 'following') {
                processed.list = path.shift();
            }
            if (!path.length) {
                return processed;
            }

            // CODE
            processed.code = /^[A-Z\d]+$/.test(path[path.length - 1]) && path.pop() || null;
            if (!path.length) {
                return processed;
            }

            // filter map
            //         processed.filter_map = path[path.length-1] == 'map' && path.pop() && true || false;
            // if(processed.filter_map){
            //     if(processed.subview == 'thumbs'){
            //         processed.subview = null;
            //     }
            // }
            // subview - people - following/followers
            if (processed.list != 'search') {
                processed.list = $.inArray(path[path.length - 1], ['following', 'followers']) !== -1 && path.pop() || null;
            }
            if (processed.list) {
                // processed.subview = !processed.filter_map && 'people' || processed.subview;
                processed.username = path.length && path.shift() || null;
            }
            // /map is an exception
            // if (!processed.code && processed.filter_map){
            //     processed.subview = null;
            // }
            if (!path.length) {
                return processed;
            }

            // list
            processed.list = $.inArray(path[path.length - 1], ['feed', 'favorites', 'favourites']) !== -1 && path.pop() || processed.list;
            processed.list = processed.list == 'favourites' && 'favorites' || processed.list
            // remove u from properly spelt favourites
            if (!path.length) {
                return processed;
            }

            // user or stream
            if (!isNaN(parseInt(path[0]))) {
                processed.stream = parseInt(path.shift());
                processed.list = 'stream';
            } else {
                if (processed.list !== 'favorites') {
                    processed.list = 'user';
                }
                processed.username = path.pop();
            }
            if (!path.length) {
                return processed;
            }

            log(path);

            return processed;
        },
        /**
     * get search api parameters for current view
     * @argument extras {object} This object is extended with params and returned
     * @returns {object} parameters to get image list for current view from search api
     */
        'get_list_params': function(extras) {
            var data = extras || {};
            switch (s.view.list) {
            case 'search':
                data['keywords'] = s.search.query;
                break;
            case 'user':
                data['username'] = s.user.username;
                break;
            case 'stream':
                data['stream'] = s.dashboard.current_stream;
                break;
            case 'favorites':
                data['favorited_by'] = s.user.username;
                break;
            case 'feed':
                data['group'] = 'following';
                data['not_by'] = '.';
                break;
            case 'following':
                data['group'] = 'following';
                break;
            case 'popular':
                data['sort'] = 'favorite_count';
                // console.warn('popular');
                break;
            default:
                if (s.map.map && s.map.map.getBounds()) {
                    data['area'] = s.map.map.getBounds().toUrlValue(5);
                }
                if (s.map.date) {
                    data['date'] = s.map.date;
                }
            }
            return data;
        }
    }


    s.people = {
        'init': function() {
            $('#people-tabs').tabs({
                show :function(event,ui) {
                    $('.tabbody.scroll-me:visible').jScrollPane({forceNoScrollH:true});
                }
            });
            if(s.people.query){
                // console.warn('search for ', s.people.query);
                $('[href="#people-search-tab"]').click();
                $('#people-search-tab ul').empty().append('<li>Loading people…</li>');
                $('#people-search-username').val(s.people.query);
                s.search.users(s.people.query,s.people.add_search_results);
                s.people.query = null;
            }
            if(s.auth.username){
                s.people.get_followers();
                s.people.get_following();
            }
        },
        'add_search_results': function(users) {
            $('#people-search-tab .tabbody ul').empty();
            $("#person-tmpl")
            .tmpl(users)
            .appendTo('#people-search-tab .tabbody ul');
            $('#people-search-tab .tabbody.scroll-me').jScrollPane({forceNoScrollH:true});
        },
        'get_following': function() {
            var url = '/api/user/following/';
            $.ajax({
                url: url,
                success: function(following) {
                    if (following.success) {
                        $(following.response.groups).each(function(i, group) {
                            if (group.group_name == 'following') {
                                $('#following-tab .tabbody ul').empty();
                                $("#person-tmpl")
                                .tmpl(group.users)
                                .appendTo('#following-tab .tabbody ul');
                            }
                            if(!group.users.length){
                                $('#following-tab .tabbody ul').append('<li>You\'re not following anyone yet.</li>');
                            }
                            if(i == following.response.groups.length -1){
                                $('#following-tab .tabbody.scroll-me').jScrollPane({forceNoScrollH:true});
                            }
                        });
                    }
                }
            });

        },
        'get_followers': function() {
            var url = '/api/user/followers/';
            $.ajax({
                url: url,
                success: function(followers) {
                    if (followers.success) {
                        $('#followers-tab .tabbody ul').empty();
                        $("#person-tmpl")
                        .tmpl(followers.response.followers)
                        .appendTo('#followers-tab .tabbody ul');
                        if(!followers.response.followers.length){
                            $('#followers-tab .tabbody ul').append('<li>You don\'t have any followers yet.</li>');
                        }
                        $('#followers-tab .tabbody.scroll-me').jScrollPane({forceNoScrollH:true});
                    }
                }
            });
        }
    }

    s.favorites = {
        'username': null,
        'get_photos': function(options) {

            var o = {
                parent: snapr_web.favorites,
                // put new photos into s.map
                container: s.image.dom.photostream,
                // if in map view, with
                template: snapr_web.photostream_template,
                // function, takes photo and returns dom representation
                method: 'replace',
                // 'replace' all, 'append' or 'prepend'
                no_results: function(r) {
                    if (r.options.method == 'replace') alert('You have no images yet.');
                }
                // callback when there are no results
            }
            $.extend(o, options);

            $.ajax({
                url: s.api_url + '/favorites/',
                success: function(results) {
                    $.extend(o, {
                        photos: results.response.photos
                    })
                    got_photos(o);
                }
            });
        },
        'show': function(username) {
            s.view.list = 'favs';
            s.favorites.username = username;
            snapr_web.favorites.get_photos({
                success: function(r) {
                    s.image.show_bubble(r.photos[0].id, false);
                }
            });
        }
    }
    s.dashboard = {
        'loaded':false,
        'current_stream': null,
        'streams': {},
        'scrollPane_settings': s.scrollPane_settings,
        // defaults
        'elements': {
            'container': new cached_jQ('#dashboard'),
            'side_menu': new cached_jQ('#dashboard-sidemenu'),
            'bubble_container': new cached_jQ('#dashboard-bubble')
        },
        'init': function() {
            s.dashboard.spinner();

            var window_height = $(window).height(),
            magic = window_height * .6,
            basepane = (window_height - 120 - magic) * .75,

            css = '.dashboard-photostream.selected .dashboard-streambody{width:' + magic + 'px} ';
            css += '.dashboard-photostream.selected .imagedisplay{height:' + magic + 'px} ';

            if ($("#dash-css").length) {
                $("#dash-css").text(css);
            } else {
                css = '<style id="dash-css">' + css + '</style>';
                $(css).appendTo($('head'));
            }

            // non-webkit browser detection
            if(navigator.userAgent.toLowerCase().indexOf('webkit') < 0){
                $('body').addClass('non-webkit');
            }
            $('#topbartoggleviewbt').click(function() {
                if (s.view.view == 'dash') {
                    // window.location.hash = '#/'
                    var params = s.view.default_params();
                    params.view = 'map';
                    // console.warn('go to map',params);
                    s.view.go(params);
                } else {
                    // window.location.hash = '#/dash/'

                    var params = s.view.default_params();
                    params.view = 'dash';
                    // console.warn('go to dash',params);
                    s.view.go(params);
                }
            });

            // live: back to map buttons
            // <x class="x-goto-map" data-photo="CODE">MAP</x>
            $('.x-goto-map').live('click',
            function(e) {
                var params = {};
                var stream = $(this).closest('li');
                var code = $(this).data('photo');
                var stream_type = stream.data('stream-type');
                var query_string = stream.data('query');
                var location = $(this).data('location').split(',');
                var aspect = $(this).data('aspect');
                params.code = code;
                params.bubble = '';
                params.view = 'map';
                params.subview = 'photo';
                if(s.view.view == 'dash'){
                    params.map = {zoom:15};
                }else{
                    if(s.map.zoom < 5){
                        params.map = {zoom:15}
                    }
                }

                $('#photo-bubble').show();
                $('#ajax-img-overlay').show();
                $('#imagedisplay').css('width', aspect * 384 + 'px');

                $('#imagebubblebase').empty();
                $('#imageextracon').empty();

                s.image.arrow.hide();

                console.log('x-goto-map click');
                s.map.pan_point_to_arrow(new google.maps.LatLng(location[1],location[0]));

                params.stream = null;

                switch (stream_type) {
                case 'person':
                    params.list = 'users';
                    params.username = query_string.split('=')[1];
                    break;
                case 'search':
                    if(query_string == 'group=following&not_by=.'){
                        params.list = null;
                    }else if (query_string == 'sort=favorite_count') {
                        params.list = 'popular';
                    } else {
                        params.list = 'search';
                        params.search = query_string.split('=')[1];
                    }
                }
                s.view.go(params);
            });

            // live: thumbs change large image
            // <x class="x-thumb" data-photo="CODE">click to show</x>
            // <x class="x-thumb-wrapper photo-CODE (current)"></x>
            $('.x-thumb', s.dashboard.elements.container.get()[0]).live('click',
            function() {
                var $this = $(this),
                photo_id = $this.data('photo'),
                stream = $this.closest('.x-stream'),
                wrapper = $this.closest('.x-thumb-wrapper'),
                prev = wrapper.prev('.x-thumb-wrapper'),
                scroll_api = $this.closest('.scroll-pane').data('jsp');
                scroll_api.reinitialise();
                // scroll_api.scrollToElement(prev.length && prev || wrapper, true);
                var scroll_area = stream.find('.scroll-pane');
                scroll_area.data('paused', true);
                scroll_area.jScrollPane(snapr.dashboard.scrollPane_settings);
                scroll_area.data('paused', false);
                scroll_area.data('jsp').scrollToElement(prev.length && prev || wrapper, true, true);
                s.dashboard.load_photo(stream, photo_id);
            });

            // live: delete stream
            // <x class="x-stream">
            //    <x class="x-delete-stream" data-id="id">delete</x>
            // </x>
            $('.x-delete-stream', s.dashboard.elements.container.get()[0]).live('click',
            function() {
                if (!confirm('Remove this stream from your dashboard?')) {
                    return false;
                }

                var $this = $(this),
                stream_id = $this.data('stream'),
                stream = $this.closest('.dashboard-photostream');

                if (stream.is('.selected')) {
                    var select_next = function() {
                        var scroll_api = $('#dashboard-scroll').data('jsp');
                        s.dashboard.select($('.x-stream').not('.selected').first(), true);
                        scroll_api.reinitialise();
                        var x = $('#dashboard-sidemenu').position().left - 100;
                        scroll_api.scrollToX(x, true);
                    }
                } else {
                    var select_next = function() {
                        var scroll_api = $('#dashboard-scroll').data('jsp');
                        var x = $('#dashboard-sidemenu').position().left - 100;
                        scroll_api.scrollToX(x, true);
                    }
                }

                if (stream_id > 0) {
                    stream.addClass('deleting');
                    $.ajax({
                        url: s.api_url + '/user/dashboard/delete/',
                        data: {
                            'stream_id': stream_id
                        },
                        type: 'POST',
                        success: function(response) {
                            if (response.success) {
                                s.dashboard.animated_hide(stream, true, select_next);
                            } else {
                                alert(response.error.message);
                            }
                        }
                    });
                    return false;
                } else {
                    s.dashboard.animated_hide(stream, true, select_next);
                    return false;
                }

            });

            $('.x-download-button').live('click',function(){
               var url = $(this).data('url');
               if($.browser.msie){
                   window.open(url,"Download");
               }else{
                   window.location.href = url;
               }
            });


            $('.x-remove-dash-bubble').live('click',
            function() {
                var element = $(this).closest('.dash-inline-content');
                var scroll_api = $('#dashboard-scroll').data('jsp');
                if(element.is('.selected')){
                    var x = $('#dashboard-sidemenu').position().left;
                }else{
                    var x = element.position().left;
                }
                scroll_api && scroll_api.scrollToX(x - 100);
                s.dashboard.animated_hide(element, false,function(){
                    s.view.go({
                        'bubble': ''
                    });
                });
            });

            $('.imagelocation.dash-only').live('click',
            function() {
                var params = {};
                var stream = $(this).closest('li');
                var code = $(this).data('photo');
                var stream_type = stream.data('stream-type');
                var query_string = stream.data('query');

                params.code = code;
                params.bubble = '';
                params.view = 'map';
                params.subview = 'photo';
                params.stream = null;

                switch (stream_type) {
                case 'person':
                    params.list = 'users';
                    params.username = query_string.split('=')[1];
                    break;
                case 'search':
                    params.list = 'search';
                    params.search = query_string.split('=')[1];
                }
                s.view.go(params);
            });

            $('#photo-bubble #imagelocation').live('click',
            function() {
                var point = $('#imagelocation', $this).data('point');
                console.log('#photo-bubble #imagelocation click');
                s.map.pan_point_to_arrow(new google.maps.LatLng(point.latitude, point.longitude));
            });

            // live: stream headers activate stream
            // <x class="x-photostream-activate">click to activate</x>
            $('.x-photostream-activate', s.dashboard.elements.container.get()[0]).live('click',
            function() {
                s.dashboard.select($(this).closest('.x-stream'), true);
            });

            $('.x-dash-gallery').live('click',
            function() {
                var params = {};
                var stream = $(this).closest('li');
                var code = $(this).data('photo');
                var stream_type = stream.data('stream-type');
                var query_string = stream.data('query');

                params.code = code;
                params.bubble = '';
                params.view = 'dash';
                params.subview = 'gallery';

                s.gallery.query = query_string;

                switch (stream_type) {
                case 'person':
                    params.list = 'users';
                    params.username = query_string.split('=')[1];
                    break;
                case 'search':
                    if(query_string == 'group=following&not_by=.') {
                        params.list = 'feed';
                    }else if (query_string == 'sort=favorite_count') {
                        params.list = 'popular';
                    } else {
                        params.list = 'search';
                        params.search = query_string.split('=')[1];
                    }
                }
                s.view.go(params);
            });

            // live: next and prev
            $('.x-next', s.dashboard.elements.container.get()[0]).live('click',
            function() {
                var stream = $(this).closest('.x-stream'),
                current = stream.find('.current.x-thumb-wrapper'),
                next_id = current.next().data('photo');
                if (next_id) {
                    s.dashboard.load_photo(stream, next_id);
                    stream.find('.moreimagesscrollarea').data('jsp').scrollToX(current.position().left, true, true);
                }
            });
            $('.x-prev', s.dashboard.elements.container.get()[0]).live('click',
            function() {
                var stream = $(this).closest('.x-stream'),
                prev = stream.find('.current.x-thumb-wrapper').prev(),
                prev_id = prev.data('photo');
                if (prev_id) {
                    s.dashboard.load_photo(stream, prev_id);
                    stream.find('.moreimagesscrollarea').data('jsp').scrollToX(prev.prev().position().left, true, true);
                }
            });

            $('.twitter-share-button.twitter-url').live('click',
            function() {
                var link = 'http://capturethefl.ag/' + $(this).data('code');
                var message = $(this).parent().parent().find('textarea').val();
                var twitterlink = 'http://twitter.com/share?url=' + encodeURIComponent(link) + '&counturl=' + encodeURIComponent(link) + '&text=' + encodeURIComponent(message) + '&related=snapr';
                if (via = $(this).data('via')) {
                    twitterlink = twitterlink + '&via=' + via;
                }
                window.open(twitterlink, '_blank', 'width=500,height=300');
            });
            $('.twitter-share-button.twitter-api').live('click',
            function() {
                var link = 'http://capturethefl.ag/' + $(this).data('code');
                var message = $(this).parent().parent().find('textarea').val();
                var twitter_user = $(this).data('twitter-username')
                if (via = $(this).data('via')) {
                    message = message + ' via @' + via + ' ';
                }
                $.ajax({
                    url: '/api/linked_services/twitter/share/',
                    type: 'POST',
                    data: {
                        id: $(this).data('code'),
                        message: message
                    },
                    success: function(response) {
                        if (response.success) {
                            $('.social-network-messages p').empty()
                            .append('Success! Posted to <a href="http://twitter.com/' + twitter_user + '">@' + twitter_user + '</a> on Twitter.')
                            .fadeIn('fast');
                            setTimeout("$('.social-network-messages p').fadeOut('slow');", 6000);
                            $('.sharingpalette').addClass('palette-closed');
                        } else {
                            alert('Sorry, we had trouble posting to Twitter.');
                        }

                        // test to see if it is really successful!
                    },
                    error: function() {}
                })
            });

            $('.tumblr-share-button.tumblr-url').live('click',
            function() {
                var link = 'http://capturethefl.ag/' + $(this).data('code');
                var message = $(this).parent().parent().find('textarea').val();
                var imgsrc = 'http://media-server2.snapr.us/gal/' + $(this).data('secret') + '/' + $(this).data('code') + '.jpg';
                var tumblrlink = 'http://www.tumblr.com/share/photo?source=' + encodeURIComponent(imgsrc) + '&clickthru=' + encodeURIComponent(link) + '&caption=' + encodeURIComponent(message);
                window.open(tumblrlink, '_blank', 'width=500,height=440');
            });

            $('.tumblr-share-button.tumblr-api').live('click',
            function() {
                var code = $(this).data('code');
                var link = 'http://capturethefl.ag/' + code;
                var message = $(this).parent().parent().find('textarea').val();
                var tumblr_user = $(this).data('tumblr-username');
                $.ajax({
                    url: '/api/linked_services/tumblr/share/',
                    type: 'POST',
                    data: {
                        id: $(this).data('code'),
                        message: message
                    },
                    success: function(response) {
                        console.warn('fb',response);
                        if (response.success) {
                            $('.social-network-messages p').empty()
                            .append('Success! Posted to <a href="http://' + tumblr_user + '.tumblr.com/">' + tumblr_user + '</a> on tumblr.')
                            .fadeIn('fast');
                            setTimeout("$('.social-network-messages p').fadeOut('slow')", 6000);
                            $('.sharingpalette').addClass('palette-closed');
                        } else {
                            alert('Sorry, we had trouble posting to Tumblr.');
                        }

                        // test to see if it is really successful!
                    },
                    error: function() {}
                })
            });

            $('.facebook-like-button.facebook-api').live('click',function(){
                var code = $(this).data('code');
                var link = 'http://capturethefl.ag/' + $(this).data('code');
                var message = $(this).closest('form').find('textarea').val()
                var data = {
                    id: code
                }
                if(message){
                    data.message = message;
                }
                $.ajax({
                    url: '/api/linked_services/facebook/share/',
                    type: 'POST',
                    data: data,
                    success: function(response){
                        if(response.success){
                            $('.social-network-messages p').empty()
                            .append('Success! Posted to Facebook.')
                            .fadeIn('fast');
                            setTimeout("$('.social-network-messages p').fadeOut('slow')", 6000);
                            $('.sharingpalette').addClass('palette-closed');
                        }else{
                            alert('Sorry, we had trouble posting to Facebook');
                        }
                    },
                    error: function(){}
                })
            });

            $('.facebook-like-button.facebook-sharer').live('click',function(){
                var code = $(this).data('code');
                var url = 'http://www.facebook.com/sharer.php?u=http://capturethefl.ag/' + code;
                window.open(url, '_blank', 'width=500,height=440');
            });

            $('.email-share-button').live('click',
            function() {
                var message = $(this).parent().parent().find('textarea').val().replace('\n', '%0A%0A').replace('\r', '%0A');
                var subject = 'Check out this image I found on Snapr!';
                var href = 'mailto:?subject=' + subject + '&body=' + message;
                $(this).attr('href', href);
            });

            $('.snapr-special-select.clickable').live('click',
            function() {
                var list = $(this).closest('#sh-foursquare').find('.venues-select-panel');
                if (list.is(':visible')) {
                    list.hide();
                } else {
                    list.show();
                    ll = list;
                    list.find('.nearby-venues ul').data('jsp').reinitialise({forceNoScrollH:true});
                }
            })

            $('.dash-widget-this').live('click',
            function() {
                var stream = $(this).closest('.dashboard-photostream');
                var type = stream.data('stream-type');
                var query = stream.data('query').split('=');
                if (type == 'person') {
                    var title = 'Photos by ' + query[1];
                    var query = {
                        'title': title,
                        'username': query[1]
                    }
                } else {
                    var title = 'Search for ' + query[1];
                    var query = {
                        'title': title,
                        'keywords': query[1]
                    }
                }

                var widget_code = '<script src="http://static.sna.pr/live/imagewidget1.js"></script>\n<script>new snaprwidget.Widget(' + JSON.stringify(query) + ');</script>';
                alert('Paste this code into your website to display a widget for this stream: ' + widget_code);
            });

            $('.background-radio-group input').live('change', function(e){
                var node = $(this);
                var setting = node.val();
                $.ajax({
                    url:'/api/user/dashboard/theme/',
                    type:'POST',
                    data:{
                        background_style: snapr.dashboard.backgrounds[setting].style,
                        background_image: snapr.dashboard.backgrounds[setting].url
                    },
                    datatype:'jsonp',
                    success: function(response){
                        // console.warn('response',response);
                        if(response.success){
                            $('#dashboard').css('background','url(\'' + snapr.dashboard.backgrounds[setting].url + '\')');
                            if(snapr.dashboard.backgrounds[setting].style == 'fullscreen'){
                                $('#dashboard').css('-webkit-background-size','cover');
                                $('#dashboard').css('-moz-background-size','cover');
                                $('#dashboard').css('-o-background-size','cover');
                                $('#dashboard').css('background-size','cover');
                                $('#dashboard').css('background-repeat','no-repeat');
                            }
                            if(snapr.dashboard.backgrounds[setting].style == 'tile'){
                                $('#dashboard').css('-webkit-background-size','auto');
                                $('#dashboard').css('-moz-background-size','auto');
                                $('#dashboard').css('-o-background-size','auto');
                                $('#dashboard').css('background-size','auto');
                                $('#dashboard').css('background-repeat','repeat');
                            }

                        }else{
                            alert('Sorry, we had trouble changing the background image.');
                            console.warn('error',response)
                        }
                    },
                    error: function(e){
                        console.warn('dash theme error',e)
                        alert('Sorry, we had trouble changing the background image.');
                    }
                });
            });

        },
        'show': function(callback) {
            $(document.body).addClass('dashboard');
            s.map.elements.map.get().hide();
            s.map.elements.time.get().hide();
            s.map.elements.tabs.get().hide();
            $('#news-feed ul').empty();
            $('#news-feed').hide();
            if(!s.dashboard.loaded){
                $('#dashboard').addClass('loading');
                s.dashboard.elements.container.get().addClass('loading').show();
                s.dashboard.elements.container.get().find('#dashboard-space').remove();
                s.dashboard.load_dash(callback);
            }else{
                s.dashboard.elements.container.get().show();
            }
            s.dashboard.reinit_scroll();
        },
        'spinner': function(){
            $('#dash-loading .spinner').spin({
                lines:12,
                length:15,
                width:9,
                radius:25,
                trail:60,
                speed:0.8
            });
        },
        'reset': function(){
            var show_dash = $('#dashboard:visible').length;
            $('#dashboard').removeAttr("style");
            if(!show_dash){
                $('#dashboard').hide();
            }

            s.dashboard.elements.container.get().empty().html('<div id="dash-loading" class="dash-loading"><div class="spinner"></div><h2>Loading Dash</h2></div>');
            s.dashboard.spinner();
        },
        'hide': function() {
            console.warn('dash.hide')
            $(document.body).removeClass('dashboard');
            s.dashboard.elements.container.get().hide();
            if (s.map.map) {
                console.warn('s.map.map');
                s.map.elements.map.get().show();
                s.map.elements.time.get().show();
                s.map.elements.tabs.get().show();
                s.map.get_images();
            } else {
                console.warn('s.map.init');
                s.map.init();
                s.map.elements.map.get().show();
                s.map.elements.time.get().show();
                s.map.elements.tabs.get().show();
                s.map.get_images();
            }
        },
        'select': function(stream, go_to_dash) {
            if (stream.length) {
                stream.addClass('selected');
                var stream_id = $(stream).attr('id');
                if (stream_id !== 'dashboard-stream-0' && stream_id !== 'dashboard-bubble') {
                    $('#dashboard-bubble:visible').hide();
                    if (go_to_dash) {
                        var params = s.view.default_params();
                        params.view = 'dash'
                        s.view.go(params);
                    }
                }

                s.comments.linkHashtags($('#imagetag', stream));

                s.dashboard.set_comments_height(stream);

                s.lists.make_endless(stream.find('.moreimagesscrollarea'), 'stream', true);
                var scroll_api = $('#dashboard-scroll').data('jsp');
                var siblings = stream.siblings('.selected');
                siblings.removeClass('selected');
                siblings.each(function(i,v){
                    $(v).find('.moreimagesscrollarea').jScrollPane({});
                })

                $('.dashboard-photostream').each(function() {
                    var $this = $(this),
                        commentsscroll = $this.find('#commentsscroll');
                    if(commentsscroll.length){
                        commentsscroll.jScrollPane(snapr.dashboard.scrollPane_settings);
                        var scroll_area = $this.find('.scroll-pane');
                        scroll_area.data('paused', true);
                        scroll_area.jScrollPane(snapr.dashboard.scrollPane_settings);
                        scroll_area.data('paused', false);
                        scroll_area.find('.jspPane').css('left', 0).css('top', 0);
                        scroll_area.data('jsp').scrollToElement(scroll_area.find('.current'));
                    }
                });

                s.dashboard.elements.side_menu.get().insertBefore(stream);
                scroll_api && scroll_api.reinitialise();
                var x = $('#dashboard-sidemenu').position().left - 100;
                scroll_api && scroll_api.scrollToX(x, true);
            }
        },
        'animated_hide': function(stream, remove, callback) {
            var t = new Date().getTime();
            var x = $('#dashboard-sidemenu').position().left - 100;
            // console.warn('x1', x);
            $(stream)
            .css({
                visibility: 'hidden'
            })
            .animate({
                'width': 0,
                'margin-right': 0
            },
            300, false,
            function() {
                $(this).hide().css({
                    'visibility': '',
                    'width': '',
                    'margin-right': ''
                });

                if (remove) {
                    $(this).remove();
                }
                s.dashboard.reinit_scroll();
                if (callback) {
                    callback();
                }
            });
        },
        'reload_stream': function(stream){
            stream.addClass('loading');
            var query = stream.data('query');
            $.ajax({
                url:'/ajax/dashboard/stream/?' + query,
                dataType: 'html',
                success: function(response){
                    stream.find('.dashboard-streambody').replaceWith($(response).find('.dashboard-streambody'));
                    if(stream.find('.image-sidepanel-social').length){
                        stream.find('.image-sidepanel-social').replaceWith($(response).find('.image-sidepanel-social'));
                    }else{
                        stream.append($(response).find('.image-sidepanel-social'));
                    }

                    if($(response).is('.empty-stream')){
                        stream.addClass('empty-stream');
                    }else{
                        stream.removeClass('empty-stream');
                    }

                    s.dashboard.activate(stream);
                    s.dashboard.reinit_scroll();
                    var container = stream;
                    var id = container.find('.snaprbubbleimage').data('photo');
                    var codes = s.dashboard.streams[container.data('query')];
                    s.dashboard.set_next_prev(id,codes,container.data('query'));
                    stream.removeClass('loading');
                }
            });
        },
        'load_photo': function(stream, photo_id) {
            stream.addClass('loading');
            stream.find('.x-thumb-wrapper.current').removeClass('current');
            stream.find('.x-thumb-wrapper.photo-' + photo_id).addClass('current');
            $.ajax({
                url: '/ajax/dashboard/stream/?' + $.param({
                    'photo_id': photo_id
                }),
                dataType: 'html',
                success: function(response) {
                    var $response = $(response);
                    stream.find('.imagedisplay').replaceWith($response.find('.imagedisplay'));
                    stream.find('.snaprbubbleimage').load(function(){
                        stream.removeClass('loading');
                    })
                    stream.find('.image-sidepanel-social').replaceWith($response.find('.image-sidepanel-social'));
                    s.comments.linkHashtags(stream.find('#imagetag'));
                    s.dashboard.activate(stream);
                }
            });
        },
        'load_dash': function(callback){// new
            console.warn('load_dash');
            s.dashboard.spinner();
            // console.warn('spinner',spinner)
            s.dashboard.elements.container.get().load(
            '/ajax/dashboard/?feed=true',
            {},
            function(response) {
                s.dashboard.activate();
                $('#dashboard').removeClass('loading');
                if (callback) {
                    callback();
                }
            }
            );

            if(callback){
                callback();
            }
            s.dashboard.loaded = true;
        },
        'activate': function(streams) {
            s.dashboard.elements.container.get().removeClass('loading');

            (streams || $('.dashboard-photostream')).each(function() {
                var $this = $(this);

                // only activate list items
                if($this.is('li')){
                    // hack to remove nearby queries added by the mobile interface
                    if($this.data('query') && $this.data('query').indexOf('nearby=true') > 0){
                        $this.remove();
                    }else{

                        // hack to change heading for following stream so it can't be removed
                        if($this.data('query') == 'group=following&not_by=.'){
                            $this.addClass('feed');
                        }

                        // sharing tabs
                        $this.find('.tabbedcontent')
                        .tabs()
                        // show the tabs on first click
                        // needs to be on the <a> elements because others don't get click event, perhaps .tabs() has stolen it.
                        .find('.tabs li a').click(function() {
                            $(this).closest('.palette-closed').removeClass('palette-closed');

                            var linked_service = $(this).data('linked-service');
                            switch (linked_service) {
                            case 'foursquare':
                                s.linked_service.foursquare.load_social_tab($this);
                                break;
                            }
                        })
                        .end()
                        .find('.snaprclosex-small').click(function() {
                            $(this).closest('.sharingpalette').addClass('palette-closed');
                        });

                        var scroll_area = $this.find('.scroll-pane');
                        s.lists.make_endless(scroll_area, 'stream');

                        $this.find('.x-pretty-date').prettyDate();

                        var container = $this;
                        var id = container.find('.snaprbubbleimage').data('photo');
                        var query = container.data('query');
                        var codes = s.dashboard.streams[query];
                        var str = s.dashboard.streams;

                        if(query){
                            s.dashboard.set_next_prev(id, codes, query);
                        }
                    }
                }
            });
            $('#dashboard-scroll').jScrollPane(s.dashboard.scrollPane_settings);

            s.dashboard.add_person.activate();
            if (!s.bubble.path) {
                s.bubble.hide();
            }
            if (!streams) {
                s.dashboard.select($('.dashboard-photostream').eq(0));
            }
        },
        'set_comments_height': function(stream) {
            var $commentsscroll = stream.find('#commentsscroll');
            $commentsscroll.height(stream.find('.image-sidepanel-social').height() - 288 - stream.find('#imagetag').height());
            $commentsscroll.jScrollPane(s.dashboard.scrollPane_settings);
        },
        'set_next_prev': function(id, codes, query){
            // console.warn('set_next query',id, codes, query)
            var element = $('[data-query="' + query + '"]');
            if(id && codes && query){
                id = String(id);
                element.removeClass('no-next no-prev');
                var pos = $.inArray(id, codes);
                if(pos == -1){
                    element.addClass('no-next no-prev');
                }else{
                    if(pos == 0){
                        // first
                        element.addClass('no-prev');
                        var list = $('.dashboard-photostream.[data-query="' + query + '"]').find('.x-thumb-wrapper').parent();
                        if(list.length < 1){
                            list = $('#gall-time').data('date');
                        }
                        if(s.view.subview == 'thumbs' || s.view.subview == 'gallery'){
                            s.lists.get_newer(list, 'stream');
                        }
                    }
                    if(pos == codes.length -1){
                        // last
                        element.addClass('no-next');
                    }

                    // if(pos == 1){
                    //     // 2nd
                    //     var list = $('.dashboard-photostream.[data-query="' + query + '"]').find('.x-thumb-wrapper').parent();
                    //     s.lists.get_newer(list, 'stream');
                    //     // console.warn('todo: get newer');
                    // }
                    if(pos > 0 && pos == codes.length-1){
                        // last
                        // load more
                        var list = $('.dashboard-photostream.[data-query="' + query + '"]').find('.x-thumb-wrapper').parent();
                        if(list.length < 1){
                            list = $('#gall-time').data('date');
                        }
                        s.lists.get_older(list, 'stream');
                    }
                }
            }
        },
        'resize': function() {
            s.dashboard.reinit_scroll();
        },
        'css': null,
        'reinit_scroll': function(streams) {
            // console.warn('dash.reinit_scroll');
            s.dashboard.css && s.dashboard.css.remove();

            var height = $('.dashboard-streambody').last().height() - 330;

            var css = '<style>';
            //css += '.dashboard-photostream.selected .moreimagesscrollarea{height:'+basepane+'px} ';
            css += '.dashboard-photostream .moreimagesscrollarea{height:' + height + 'px} ';
            css += '</style>'

            s.dashboard.css = $(css)

            s.dashboard.css.appendTo($('head'));

            var dash_width = 500;
            var items = $('.dashboard-photostream');
            for(i = 0; i < items.length; i++){
                var $this = $(items[i]);
                // console.warn('old_dw',dash_width)
                dash_width = dash_width + $this.outerWidth(true);
                $this.find('#commentsscroll').jScrollPane(s.dashboard.scrollPane_settings);
                var scroll_area = $this.find('.scroll-pane');
                scroll_area.data('paused', true);
                scroll_area.jScrollPane(s.dashboard.scrollPane_settings);
                scroll_area.data('paused', false);
                if(i == items.length -1){
                    // console.warn('last', dash_width);
                    $('#dashboard-scroll').width(dash_width);
                    // console.warn(dash_width)
                    $('#dashboard-scroll').jScrollPane(s.dashboard.scrollPane_settings);
                    $('#dashboard-scroll').data('jsp').reinitialise({forceUpdate:true,newWidth:dash_width});
                }
            }
        },
        'add_stream': function(stream) {
            stream.prependTo(s.dashboard.elements.container.get().find('#dashboard-items'));
            s.dashboard.reinit_scroll();
            s.dashboard.activate(stream);
            s.dashboard.select(stream, false);
            var scroll_area = $(stream).find('.scroll-pane');
            // console.warn('scroll_area', scroll_area);
            s.lists.make_endless(scroll_area, 'stream');
            s.lists.make_endless(scroll_area, 'stream', true);
            //  horizontal
        },
        'add_temp_stream': function(stream) {
            $('#dashboard-stream-0').remove();
            stream = $(stream);
            stream.addClass('temporary-stream');
            var add_stream = function(){
                stream.insertBefore($('#dashboard-sidemenu'));
                s.dashboard.activate(stream);
                s.dashboard.reinit_scroll();
                s.dashboard.select($('#dashboard-stream-0'), false);
            }
            if($('#dashboard-body').length < 1){
                s.dashboard.show(add_stream);
            }else{
                add_stream();
            }
        },
        'add_person': {
            'activate': function() {
                s.dashboard.elements.side_menu.get().find('input.username').autocomplete({
                    appendTo: '#add-person-list',
                    source: '/ajax/username_autocomplete/',
                    minLength: 2,
                    select: function(e, ui) {
                        s.dashboard.add_person.by_username(ui.item.value);
                    }
                });
            },
            'submit': function(e) {
                if ($('#add-person-list .ui-autocomplete li').first().text()) {
                    s.dashboard.add_person.by_username($('#add-person-list .ui-autocomplete li').first().text());
                }
                return false;
            },
            'by_username': function(username, callback) {
                //add user directly
                if (s.auth.username) {
                    // logged in
                    if ($('[data-query="username=' + username + '"]').length < 1) {
                        // user is not already on the dashboard
                        scroll_api = $('#dashboard-scroll').data('jsp');
                        scroll_api.scrollToX(10, true);

                        $.ajax({
                            'url': '/ajax/dashboard/stream/',
                            'type': 'POST',
                            'data': {
                                'username': username
                            },
                            'dataType': 'HTML',
                            'success': function(response) {
                                var stream = $(response).filter('.dashboard-photostream');
                                s.dashboard.add_stream(stream);
                                s.dashboard.add_person.hide();
                                s.dashboard.elements.side_menu.get().removeClass('loading');
                                $('#add-person input.username').val('');
                                if (callback) {
                                    callback();
                                }
                            }
                        });
                        return false;
                    } else {
                        // user already exists on the dashboard
                        alert(username + ' is already on the dashboard.');
                        $('#add-person input.username').val('');
                        return false;
                    }
                } else {
                    // not logged in
                    // s.auth.redirect.destination.url = '#/dash/' + ui.item.value + '/';
                    s.auth.redirect.destination.message = 'Please log in or sign up to add user "' + username + '" to your dashboard';
                    s.bubble.load({
                        url: '/sc/login/'
                    });
                }

            },
            'show': function() {
                s.dashboard.elements.side_menu.get().find('#add-person-tab').slideDown(300);
                // TODO - cache
            },
            'hide': function() {
                s.dashboard.elements.side_menu.get().find('#add-person-tab').hide();
            }
        },
        'add_search': {
            'show': function() {
                s.dashboard.elements.side_menu.get().find('#add-search-tab').slideDown(300);
                // TODO - cache
            },
            'hide': function() {
                s.dashboard.elements.side_menu.get().find('#add-search-tab').hide();
            },
            'submit': function(e) {
                s.dashboard.add_search.by_term(s.dashboard.elements.side_menu.get().find('input.search').val());
                return false;
            },
            'by_term': function(term) {
                if (s.auth.username) {
                    //logged in
                    if ($('[data-query="keywords=' + term + '"]').length < 1) {
                        // user is not already on the dashboard
                        s.dashboard.elements.side_menu.get().addClass('loading');
                        $.ajax({
                            'url': '/ajax/dashboard/stream/',
                            'type': 'POST',
                            'data': {
                                'keywords': term
                            },
                            'dataType': 'HTML',
                            'success': function(response) {
                                var $response = $(response);
                                var stream = $(response).filter('.dashboard-photostream');
                                s.dashboard.add_stream(stream);
                                s.dashboard.elements.side_menu.get().removeClass('loading');
                                $('#add-search input.search').val('');
                            }
                        });
                        return false;
                        // prevent default
                    } else {
                        // user already exists on the dashboard
                        alert(term + ' search is already on the dashboard.');
                        return false;
                    }

                } else {
                    //not logged in
                    s.auth.redirect.destination.message = 'Please log in or sign up to add a search to your dashboard';
                    s.bubble.load({
                        url: '/sc/login/'
                    });
                }
            }
        }
    };
    s.gallery = {
        'codes': [],
        // populated when thumbs or bubble are loaded or gallery opened from dash stream, used for next/prev
        'query': null,
        // used to get more images
        'elements': {
            'overlay': new cached_jQ('#gallery-layer'),
            'gallery': new cached_jQ('#snaprgallery'),
            'image': new cached_jQ('#gallery-image'),
            'time': new cached_jQ('#gall-time'),
            'shade': new cached_jQ('#gallery-shade'),
            'topbar': new cached_jQ('#gallery-topbar')
        },
        'init': function() {
            s.gallery.elements.overlay.get().click(function(e) {
                // only hide gallery if the overlay is clicked, not topbar or anything like that
                if (!e || e.target == s.gallery.elements.gallery.get()[0]) {
                    s.gallery.remove_from_url();
                }
            });
            $('.gall-close-view-bt').live('click',
            function() {
                if (s.view.view == 'dash') {
                    var params = s.view.default_params();
                    params.view = 'dash';
                    s.view.go(params);
                } else {
                    s.view.go({
                        'subview': 'photo'
                    });
                }
            })
        },
        /**
     * @argument code {string} Photo code
     * @argument stream_id {int} optional, dash stream for next/prev buttons
     */
        'add_to_url': function(code, stream_id) {
            var params = {
                'code': code,
                'subview': 'gallery'
            };
            if (stream_id) {
                params['stream'] = stream_id;
            }
            snapr.view.go(params);
        },
        'remove_from_url': function() {
            if (s.view.view === 'dash') {
                window.location.hash = '#/dash/'
            } else {
                window.location.hash = window.location.hash.replace('/gallery', '');
            }
        },
        'show': function(photo_id) {
            s.gallery.elements.gallery.get().find('.snaprbubbleimage').hide();
            s.gallery.elements.overlay.get().show();
            $window = $(window);
            s.gallery.elements.gallery.get().load(
            // 184 used to be be subtracted
            '/ajax/gallery/?width=' + ($window.width()) + '&height=' + ($window.height()) + '&photo_id=' + photo_id,
            {},
            function(response) {
                s.gallery.activate();
            }
            );

            // remove thumbs view if it was showing
            s.gallery.elements.overlay.get().removeClass('gallery-thumbs-view');
        },
        'hide': function(e) {
            s.gallery.elements.overlay.get().fadeOut();
        },
        'activate': function() {
            s.gallery.elements.time.get().prettyDate();
            s.gallery.elements.shade.get().mouseout(function(e) {
                if ($(e.relatedTarget).is('.hover')) {
                    s.gallery.show_details();
                }
            });

            s.gallery.elements.image.get().load(function() {
                s.gallery.elements.image.get().show();
            });

            s.comments.linkHashtags(s.gallery.elements.gallery.get().find('#gall-imagetag'));

            if (s.view.view === 'dash') {
                s.gallery.codes = s.dashboard.streams[s.dashboard.current_stream] || s.gallery.codes;
            }

            if(s.gallery.elements.gallery.get().is('none-older') || s.gallery.elements.gallery.get().is('none-newer')){
                // don't remove classes
            }else{
                s.gallery.elements.gallery.get().removeClass('no-next no-prev');
                // show/hide next/prev if photo in list
                var pos = $.inArray(s.image.code, s.gallery.codes);

                if(pos == -1){
                    s.gallery.elements.gallery.get().addClass('no-next no-prev');
                }else{
                    if(pos == 0){
                        // first
                        s.gallery.elements.gallery.get().addClass('no-prev');
                    }
                    if(pos > 0 && pos == s.gallery.codes.length-1){
                        console.warn('gallery activate last')
                        s.gallery.elements.gallery.get().addClass('no-next');

                        if(s.view.view == 'dash'){
                            var list = $('[data-query="' + s.gallery.query + '"] .x-thumb-wrapper').parent();
                            if(s.gallery.elements.gallery.get().is('none-older')){
                                // don't get older
                            }else{
                                if(list.length < 1){
                                    list = $('#gall-time').data('date');
                                    s.lists.get_older(list,'list');
                                }else{
                                    s.lists.get_older(list,'stream');
                                }
                            }
                        }else{
                            var list = $('#imagedetails .x-thumb-wrapper').parent().removeClass('loading-bottom');
                            // list.removeClass('loading-bottom');
                            if(s.gallery.elements.gallery.get().is('none-older')){
                                // don't get older
                            }else{
                                if(list.length < 1){
                                    list = $('#gall-time').data('date');
                                }
                                s.lists.get_older(list,'list');
                            }
                        }
                    }
                }
            }




            // switch ($.inArray(s.image.code, s.gallery.codes)) {
            // case - 1:
            //     s.gallery.elements.gallery.get().addClass('no-next no-prev');
            //     break;
            // case 0:
            //     s.gallery.elements.gallery.get().addClass('no-prev');
            //     s.gallery.elements.gallery.get().removeClass('no-next');
            //     break;
            // case s.gallery.codes.length - 1:
            //     // end
            //     s.gallery.elements.gallery.get().addClass('no-next');
            //     s.gallery.elements.gallery.get().removeClass('no-prev');
            //     break;
            // default:
            //     s.gallery.elements.gallery.get().removeClass('no-next no-prev');
            // }

            // if images is loaded, trigger load event, the browser can't be trusted to do this if it's cached
            if (s.gallery.elements.image.get()[0].complete) {
                s.gallery.elements.image.get().load();
            }
        },
        'hide_details': function(e) {
            s.gallery.elements.topbar.get().fadeOut();
            s.gallery.elements.shade.get().fadeIn(function() {
                s.gallery.elements.overlay.get().addClass('hide-details');
            });
        },
        'show_details': function() {
            s.gallery.elements.topbar.get().fadeIn();
            s.gallery.elements.shade.get().fadeOut(function() {
                s.gallery.elements.overlay.get().removeClass('hide-details');
            });
        },
        'toggle_comments': function() {

            },
        'next': function() {
            var pos = $.inArray(s.image.code, s.gallery.codes);
            if (pos !== -1 && pos !== s.gallery.codes.length - 1 && s.gallery.elements.gallery.get().not('.none-older')) {
                // if in list and not at end
                $('.photo-' + s.image.code).removeClass('current');

                var next = s.gallery.codes[pos + 1];
                $('.photo-' + next).addClass('current');

                s.view.go({
                    'code': next
                });
                return true;
            } else {
                return false;
            }
        },
        'prev': function() {
            var pos = $.inArray(s.image.code, s.gallery.codes);
            if (pos !== -1 && pos !== 0  && s.gallery.elements.gallery.get().not('.none-newer')) {
                // if in list and not first
                $('.photo-' + s.image.code).removeClass('current');

                var prev = s.gallery.codes[pos - 1];
                $('.photo-' + prev).addClass('current');
                s.view.go({
                    'code': prev
                });
                return true;
            }else{
                return false;
            }
        }
    };
    s.cities = {
        'city': null,
        // TODO: this data has been moved to django view.. not needed here
        'top_cities': {
            'new-york': {
                'name': 'New York',
                'area': '40.3429,-74.9073,41.0509,-73.052'
            },
            'london': {
                   'name': 'London',
                   'area': '51.4609,-0.267,51.5335,-0.0351'
               },
            'san-francisco': {
                'name': 'San Francisco',
                'area': '37.6654,-122.6695,37.8499,-122.2057'
            },
            'paris': {
                   'name': 'Paris',
                   'area': '48.6706,1.91574,49.03479,2.77679'
            },
             'la': {
                 'name': 'L.A.',
                 'area': '33.17046,-118.93445,34.75812,-116.91571'
            },
             'tokyo': {
                 'name': 'Tokyo',
                 'area': '35.3201,138.5036,36.0784,140.359'
            },
            'berlin': {
              'name': 'Berlin',
              'area': '52.43404,13.24902,52.57218,13.53398'
            },
            'sydney': {
                'name': 'Sydney',
                'area': '-34.11398,150.85876,-33.67054,151.54472'
            },
            'melbourne': {
                   'name': 'Melbourne',
                   'area': '-38.76968,143.9288,-37.27467,145.96676'
             },
            'auckland': {
                'name': 'Auckland',
                'area': '-37.11972,174.36127,-36.69255,175.04723'
            }
        },
        'elements': {
            'container': new cached_jQ('#cities-layer'),
            'scroll': new cached_jQ('#cities-scroll')
        },
        'init': function() {
            $('#cities-title a').live('click',
            function() {
                s.view.go({
                    subview: 'cities',
                    city: null
                })
            })
        },
        'hide': function(e) {
            s.cities.elements.container.get().fadeOut();
        },
        'show': function(city) {
            if (city) {
                if (s.cities.top_cities[city]) {
                    console.warn('show_city', city, s.cities.top_cities[city]);
                    s.cities.load_city(city);
                } else {
                    console.warn('show_city', city, 'not in top cities');
                }
            } else {
                //console.warn('load city list');
                s.cities.load_all_cities();
            }
            s.cities.elements.container.get().show();
            // .empty().append($("#cities-template").tmpl({})).show();
        },
        'load_all_cities': function() {
            s.cities.elements.scroll.get().empty();
            $.ajax({
                url: '/ajax/cities/',
                data: {},
                dataType: 'html',
                success: function(response) {
                    s.cities.elements.scroll.get().append(response);
                    s.cities.reinit_scroll();
                }
            });
        },
        'load_city': function(city) {

            var city_scroll = s.cities.elements.scroll
            city_scroll.get().empty();
            $.ajax({
                url: '/ajax/city/',
                data: {
                    'slug': city
                },
                dataType: 'html',
                success: function(response) {
                    (city_scroll.get()).append(response);
                    s.cities.reinit_scroll();
                }
            });
        },
        'reinit_scroll': function(settings) {
            //console.log('reinit_scroll');
            if (!settings) {
                settings = s.scrollPane_settings;
            }
            s.cities.elements.scroll.get().data('jsp',null).jScrollPane(settings);
        }
    };
    s.thumbs = {
        'elements': {
            'container': new cached_jQ('#thumbs-layer'),
            'scroll': new cached_jQ('#thumbs-scroll')
        },
        'activate': function() {
            s.thumbs.reinit_scroll();
            s.lists.make_endless(s.thumbs.elements.scroll.get(), 'thumbs')
            s.thumbs.elements.scroll.get().find('.x-pretty-date').prettyDate();
            s.thumbs.elements.scroll.get().find('.user-plus-description').each(function(i, com) {
                s.comments.linkHashtags(com);
            })
            s.thumbs.elements.scroll.get().find('.thumbs-block').each(function(i,cap){
               var date = prettyDate($(cap).data('date'),true);
               var location = $(cap).find('.thumbs-location').text();
               var location_array = location.split(', ');
               $(cap).find('.thumbs-timestamp').text(date);
               // console.warn('thumbb',location_array)
               if(date && location && (date.length + location.length) > 35 && location_array.length > 1){
                   $(cap).find('.thumbs-location').text(location_array[location_array.length -1]);
               }
            });

        },
        /**
     * @argument code {string} Photo code
     */
        'add_to_url': function(code) {
            s.view.go({
                'subview': 'thumbs'
            });
        },
        'remove_from_url': function() {
            if (s.view.list === 'search') {
                s.view.go({
                    'subview': null,
                    'list': null,
                    'search': null
                });
            } else {
                if (s.view.view == 'dash') {
                    window.location.hash = '#/dash/';
                } else {
                    // window.location.hash = window.location.hash.replace('/thumbs', '');
                    s.view.go({
                        'subview': 'photo'
                    });

                }
            }
        },
        'show': function(date) {
            //console.warn('s.thumbs.show')
            s.thumbs.elements.container.get().empty().show();

            var data = {};
            if (date) {
                data = {
                    'date': date
                }
            }
            data = s.view.get_list_params(data);
            data = s.map.get_filter_params(data);
            data.n = s.map.max_images || 20;

            if (s.search.query) {
                data.keywords = s.search.query;
            }
            if(data.sort == 'favorite_count'){
                delete data.date;
            }

            $.ajax({
                url: '/ajax/thumbs/',
                data: data,
                dataType: 'html',
                success: function(response) {
                    $(response).appendTo(s.thumbs.elements.container.get());
                    s.thumbs.activate();
                }
            });
        },
        'hide': function() {
            s.thumbs.elements.container.get().hide();
        },
        'resize': function() {
            log('snapr.thumbs.resize');
            s.thumbs.reinit_scroll();
        },
        'reinit_scroll': function() {
            s.lists.reinit_scroll(s.thumbs.elements.scroll.get());
        }
    }
    s.groups = {
        'init': function() {

            // live: follow buttons
            // <x class="x-follow-button user-username (loading)" data-username="username" data-following="true" data-count="3">Follow</x>
            // <x class="x-followers-count user-username">3</x>
            // <x class="x-following-status user-username (following)">Following</x>
            $('.x-follow-button').live('click',
            function() {
                var $this = $(this),
                username = $this.data('username'),
                count = $this.data('count'),
                following = $this.data('following');

                // Can't follow self
                if (username == s.auth.username) {
                    return false;
                }

                $this.addClass('loading');
                $.ajax({
                    url: s.api_url + '/user/' + (following ? 'unfollow/': 'follow/'),
                    type: 'POST',
                    dataType: 'json',
                    data: {
                        username: username
                    },
                    success: function(response) {
                        if (response.success) {
                            count += following ? -1: 1;
                            $('.x-follow-button.x-user-' + username).data('following', !following).data('count', count);
                            $('.x-following-status.x-user-' + username).toggleClass('following', !following).toggleClass('follow', following);
                            $('.x-followers-count.x-user-' + username).text(count);
                            if(following && $this.closest('.tabbody').parent().is('#following-tab')){
                                $this.parent().fadeOut();
                            }
                            if($this.closest('.tabbedcontent').is('#people-tabs')){
                                // refresh followers and following lists if we're on the people bubble
                                s.people.get_followers();
                                s.people.get_following();
                            }
                            if($('.dashboard-photostream.feed')){
                                s.dashboard.reload_stream($('.dashboard-photostream.feed'));
                            }
                        } else {
                            if (response.error.code == 16) {
                                s.auth.redirect.destination.message = response.error.message;
                                s.auth.redirect.destination.url = window.location.hash;
                                window.location.hash = '#/login';
                                s.view.change();
                            }else if(response.error.code == 43){
                                alert('Sorry, we couldn\'t find this user');
                            }
                        }
                        $this.removeClass('loading');
                    }
                });
            })
        },
        'del_toggle_following': function() {
            var $this = $(this),
            username = $this.data('username'),
            count = $this.data('count'),
            following = $this.data('following'),
            callback = function() {
                count += following ? -1: 1;
                $this.data('following', !following).data('count', count);
                $this.toggleClass('following', !following);
                $('.followers.user-' + username).text(count).data('count', count);
            };
            ($this.data('following') ? s.groups.unfollow: s.groups.follow)(username, callback);
        },
        'del_follow': function(username, callback) {
            $.ajax({
                url: s.api_url + '/user/follow/',
                type: 'POST',
                dataType: 'json',
                data: {
                    username: username
                },
                success: function(response) {
                    if (response.success) {
                        callback();
                    } else {
                        alert(response.error.message);
                    }
                }
            });
        },
        'del_unfollow': function(username, callback) {
            $.ajax({
                url: s.api_url + '/user/unfollow/',
                type: 'POST',
                dataType: 'json',
                data: {
                    username: username
                },
                success: function(response) {
                    if (response.success) {
                        callback();
                    } else {
                        alert(response.error.message);
                    }
                }
            });
        }
    };
    s.comments = {
        'scrollPane_settings': s.scrollPane_settings,
        // defaults
        'init': function() {

            // live: comment inputs
            // <x class="x-comment-input x-photo-CODE" data-photo="CODE" data-count="3">Type comment here</x>
            // <x class="x-comment-count x-photo-CODE">3</x>
            // <x class="x-comment-list x-photo-CODE">...</x>
            $('.x-comment-input').live('keypress',
            function(e) {
                //var key = (window.event) ?  event.keyCode : e.keyCode;
                if (e.keyCode == 13) {
                    var $this = $(this),
                    comment = $this.val(),
                    photo_id = $this.data('photo'),
                    count = $this.data('count');
                    if (comment != '') {
                        $this.addClass('loading');
                        $.ajax({
                            url: '/ajax/comment/',
                            type: 'POST',
                            dataType: 'html',
                            data: {
                                photo_id: photo_id,
                                comment: comment
                            },
                            success: function(response) {
                                $this.val('');
                                $('.x-comment-list.x-photo-' + photo_id).prepend(s.comments.linkHashtags($(response)))
                                .closest('.jspScrollable').jScrollPane(s.comments.scrollPane_settings);
                                $('.x-comment-count.x-photo-' + photo_id).data('count', count++);
                                $this.removeClass('loading');
                                $('.x-comment-list.x-photo-' + photo_id).closest('#commentsscroll').data('jsp').reinitialise();
                            }
                        });
                    }
                    return false;
                }
            });

            $('.remove-comment').live('click',
            function() {
                var comment_id = $(this).parent().data('comment-id');
                var element = $(this).parent();
                if (confirm('Are you sure you want to delete this comment?')) {
                    $.ajax({
                        url: '/api/comment/delete/',
                        type: 'POST',
                        data: {
                            'id': comment_id
                        },
                        success: function(response) {
                            var parent = element.parent();
                            if (response.success) {
                                element.remove();
                            }
                            parent.closest('#commentsscroll').data('jsp').reinitialise();
                        }
                    });
                }
            });
        },
        'linkHashtags': function(comment) {
            if($('p', comment).html()){
                var hashcomment = $('p', comment).html()
                .replace(/[#]+[A-Za-z0-9-_]+/g,
                function(t) {
                    var keyword = t.replace('#', '');
                    return '<span class="hash-search clickable" data-keywords="' + keyword + '">' + t + '</span>';
                });
                $('p', comment).empty().append(hashcomment);
            }
            return comment;
        }
    };
    s.image = {
        'id': '',
        'elements': {
            'container': new cached_jQ('#photo-bubble'),
            'list': new cached_jQ('#moreimagesscrollarea')
        },
        'init': function() {
            // handler binding for clicking thumbs in "more images"
            $('.x-thumb', $('#photo-bubble')[0]).live('click',
            function() {
                var li = $(this).parent(),
                id = li.data('photo');
                if(id != s.image.id){
                    $('#ajax-img-overlay').show();
                    $('#moreimagesscrollarea').find('.current').removeClass('current');
                    li.addClass('current');
                    var curr = snapr.view.get_current_params();
                    if (curr.list == 'area') {
                        // console.warn('don\'t scroll');
                        s.map.scroll = false;
                    }
                    s.view.go({
                        'code': id
                    });
                    s.image.more.scroll_api.scrollToElement(li.prev('li')[0] || li, true, true);
                }
            });

            $('.x-edit-tag').live('click',function(e){
                if(e.target != this && !$(e.target).is('.x-existing-tag')){
                    return true
                }
                var parent = $(this).closest('#imagetag');
                parent.addClass('edit');
                parent.find('textarea').val(parent.find('.x-existing-tag').text());
                parent.find('.x-existing-tag').hide();
                parent.find('.x-edit-textarea').show();
                var $this = parent.closest('.dashboard-photostream');
                if($this.length){
                    s.dashboard.set_comments_height($this);
                }else{
                    s.image.comments.set_height();
                    parent.closest("#image-social-details").find('#commentsscroll').jScrollPane(s.scrollPane_settings);
                }

            });
            $('.x-save-tag').live('click',function(){
                var new_tag = $(this).closest('#imagetag').find('.x-edit-textarea').val();
                var id = $(this).data('photo');
                var parent = $(this).closest('#imagetag');
                var existing_tag = parent.find('.x-existing-tag');
                var data = {
                    id:id,
                    description:new_tag
                }
                $.ajax({
                    url:'/api/photo/',
                    data:data,
                    type:'POST',
                    dataType:'json',
                    success:function(response){
                        // console.warn('success ',response);
                        if(response.success){
                            existing_tag.text(response.response.photo.description);
                            parent.find('.x-edit-textarea').hide();
                            parent.find('.x-existing-tag').show();
                            parent.removeClass('edit');
                            var $this = parent.closest('.dashboard-photostream');
                            if($this.length){
                                s.dashboard.set_comments_height($this);
                            }else{
                                $('not dash',$this)
                                s.image.comments.set_height();
                            }
                            parent.closest("#image-social-details").find('#commentsscroll').jScrollPane(s.scrollPane_settings);
                        }else{
                            alert(response.error.message);
                        }
                    },
                    error:function(error){
                        console.warn('success ',error);
                    }
                })
            });
            $('.x-cancel-edit-tag').live('click',function(e){
                var parent = $(this).closest('#imagetag');
                parent.removeClass('edit');
                parent.find('.x-existing-tag').show();
                parent.find('.x-edit-textarea').hide();
                // .val(parent.find('.x-existing-tag').text());
                var $this = parent.closest('.dashboard-photostream');
                if($this.length){
                    s.dashboard.set_comments_height($this);
                }else{
                    s.image.comments.set_height();
                    parent.closest("#image-social-details").find('#commentsscroll').jScrollPane(s.scrollPane_settings);
                }
            });

        },
        /**
    * @argument code {string} Photo code
    */
        'add_to_url': function(code) {
            snapr.view.go({
                'code': code,
                'subview': 'photo'
            });
        },
        'show': function(id, move_map,zoom) {
            s.image.id = id;
            this.show_bubble(s.image.id, move_map,zoom);
        },
        /**
    * Displays photo without setting image id and therefore page url and title
    * @argument id {string}  The ID for the image to show. eg 'MPL'
    * @argument move_map {bool}  Should the map be panned to place the photo's location. defaults to True
    */
        'show_bubble': function(id, move_map) {

            // calling this directly rather than s.image.show allows showing image without changing the url, eg. at /#/user/
            // console.warn('show_bubble')
            if (id) {
                var idata = {
                    'photo_id': id
                }
            } else {
                var idata = {};
            }
            var godata = s.view.get_list_params(idata);

            if (s.view.view == 'map') {
                s.image.elements.container.get().load(
                '/ajax/photo_bubble/?' + $.param(idata),
                null,
                function(){s.image.activate(move_map)}
                );
            } else {
                //dash view
                if (godata.username) {
                    //temporary user stream - not technically a bubble
                    if ($('[data-query="username=' + godata.username + '"]').length) {
                        s.dashboard.select($('[data-query="username=' + godata.username + '"]'), false);
                    } else {
                        var url = '/ajax/dashboard/stream/?';
                        $.ajax({
                            url: url,
                            type: 'GET',
                            data: godata,
                            dataType: 'html',
                            success: function(stream) {
                                s.dashboard.add_temp_stream(stream)
                            },
                            error: function(e) {
                                console.warn('error', e);
                            }
                        });
                    }
                } else if (godata.photo_id) {
                    var url = '/ajax/dashboard/stream/?';
                    $.ajax({
                        url: url,
                        type: 'GET',
                        data: godata,
                        dataType: 'html',
                        success: function(stream) {
                            s.dashboard.add_temp_stream(stream)
                        },
                        error: function(e) {
                            console.warn('error', e);
                        }
                    });
                } else {
                    var url = '/ajax/dashboard/stream/?';
                    $.ajax({
                        url: url,
                        type: 'GET',
                        data: idata,
                        dataType: 'html',
                        success: function(stream) {
                            s.dashboard.add_temp_stream(stream)
                        },
                        error: function(e) {
                            console.warn('error', e);
                        }
                    });
                }
            }
        },
        /**
    * Loads a photo into an already open bubble, doesn't change 'more images'
    * @argument id {string}  The ID for the image to show. eg 'MPL'
    */
        'load': function(id) {
            s.image.id = id.id || id;
            // allow object or string
            $.ajax({
                url: '/ajax/photo_bubble/?' + $.param({
                    'photo_id': s.image.id,
                    'n': 0
                }),
                dataType: 'html',
                success: function(response) {
                    var $response = $(response);
                    $('#imagedisplayarea').replaceWith($response.find('#imagedisplayarea'));
                    $('#image-social-details').replaceWith($response.find('#image-social-details'));
                    $('#imagelocation').replaceWith($response.find('#imagelocation'));
                    $('#moreimages .socialpallettetab').replaceWith($response.find('#moreimages .socialpallettetab'));
                    if($response.find('#imagebubble.mine').length){
                        $('#imagebubble').addClass('mine');
                    }else{
                        $('#imagebubble').removeClass('mine');
                    }
                    s.map.time.set($('#imagedisplay').data('date'));
                    s.image.activate();
                }
            });
        },
        'activate': function() {
            $this = s.image.elements.container.get();
            // spinner
            var loader = $('#ajax-img-overlay').show(),
            img = $this.find('.snaprbubbleimage'),
            id = img.data('photo');
            // don't try to activate if the bubble content's not there (no search results)
            if (!img.length) {
                $this.show();
                return;
            }
            img.load(function() {
                loader.hide()
            });
            $this.show();

            // sharing
            $('#sharingpalettetabs')
            .tabs({
                show: s.image.comments.set_height
            })
            // show the tabs on first click
            // has to be on the a elements because others don't get click event, perhaps tabs has stolen it.
            .find('.tabs li a').click(function() {
                $(this).closest('.palette-closed').removeClass('palette-closed');
                s.image.comments.set_height();

                var linked_service = $(this).data('linked-service');
                switch (linked_service) {
                case 'foursquare':
                    s.linked_service.foursquare.load_social_tab($this);
                    break;
                }
            })
            .end()
            .find('.snaprclosex-small').click(function() {
                $(this).closest('.sharingpalette').addClass('palette-closed');
                s.image.comments.set_height();
            });

            s.comments.linkHashtags($('#imagetag', $this));
            s.map.time.set($('#imagedisplay', $this).data('date'));

            // pan map to align bubble's arrow
            var point = $('#imagelocation', $this).data('point');
            console.log('pan_to align from image.activate', new google.maps.LatLng(point.latitude, point.longitude))
            s.map.pan_point_to_arrow(new google.maps.LatLng(point.latitude, point.longitude));

            // activate "more images" panel
            s.image.more.activate();

            // scroll to current image if more images panel is visible
            if (s.image.elements.list.get().is(':visible')) {
                if (s.image.elements.list.get().find('.current').length) {
                    s.image.more.scroll_api.scrollToElement(s.image.elements.list.get().find('.current'));
                }
            }
            // if images is loaded, trigger load event, the browser can't be trusted to do this if it's cached
            if (img[0].complete) {
                img.load();
            }
            s.image.set_next_prev(id,s.gallery.codes);
        },
        'set_next_prev': function(id, codes){
            // console.warn('set_next',id,codes)
            var id = String(id) || String($('.snaprbubbleimage').data('photo'));
            var codes = codes.length && codes || s.gallery.codes;
            var pos = $.inArray(id, codes);
            s.image.elements.container.get().removeClass('no-next no-prev');
            if(pos == -1){
                s.image.elements.container.get().addClass('no-next no-prev');
            }else{
                if(pos == 0){
                    // first
                    s.image.elements.container.get().addClass('no-prev');
                }
                if(pos == codes.length -1){
                    // last
                    s.image.elements.container.get().addClass('no-next');
                }
            }
        },
        'resize': function(e) {
            s.image.arrow.hide();
        },
        'next': function() {
            $('#ajax-img-overlay').show();
            //don't scroll map to next image if we're looking at nearby images
            if (s.view.list == 'area') {
                s.map.scroll = false;
            }
            var current = $('#moreimagesscrollarea').find('.thumb-image-block.current');
            if (current.length) {
                var next = current.next('.thumb-image-block') || $('#moreimagesscrollarea').find('.thumb-image-block').first();
                current.removeClass('current');
            } else {
                var next = $('#moreimagesscrollarea').find('.thumb-image-block').first();
            }

            next.addClass('current');
            s.view.go({
                'code': next.data('photo')
            });
            var scroll_api = $('#moreimagesscrollarea').data('jsp');
            scroll_api.reinitialise({forceNoScrollH:true});
            scroll_api.scrollToElement(next.prev() || next, true, true);
            return true;
            //     s.image.more.get_older(s.image.next);
            //     return false;
        },
        'prev': function() {
            $('#ajax-img-overlay').show();
            //don't scroll map to next image if we're looking at nearby images
            if (s.view.list == 'area') {
                s.map.scroll = false;
            }
            var current = $('#moreimagesscrollarea').find('.thumb-image-block.current');
            if (current.length) {
                var prev = current.prev('.thumb-image-block') || $('#moreimagesscrollarea').find('.thumb-image-block').last();
                current.removeClass('current');
            } else {
                var prev = $('#moreimagesscrollarea').find('.thumb-image-block').last();
            }

            prev.addClass('current');
            s.view.go({
                'code': prev.data('photo')
            });
            var scroll_api = $('#moreimagesscrollarea').data('jsp');
            scroll_api.reinitialise({forceNoScrollH:true});
            scroll_api.scrollToElement(prev.prev() || prev, true, true);
            return true;
            //     s.image.more.get_older(s.image.next);
            //     return false;
        },
        'hide': function() {
            $('#photo-bubble').fadeOut().removeClass('openbubble');
        },
        'arrow': {
            'show': function() {
                $('#bubblebasepin img').animate({
                    'bottom': 34
                });
            },
            'hide': function() {
                $('#bubblebasepin img').animate({
                    'bottom': 43
                });
            }
        },
        'comments': {
            'set_height': function() {
                var $commentsscroll = $('#commentsscroll');
                $commentsscroll.height($('#imageextracon').offset().top - $commentsscroll.offset().top + 430);
                $commentsscroll.jScrollPane(s.scrollPane_settings);
            }
        },
        'del_set_privacy': function(id) {
            if (s.image.photo.status == 'public') {
                status = 'private';
            }
            if (s.image.photo.status == 'private') {
                status = 'public';
            }
            if (s.image.photo.status == 'moderated') {
                status = 'moderated';
            }
            // byrowan
            $.ajax({
                url: s.api_url + '/photo/change_status/',
                type: 'POST',
                data: {
                    id: id,
                    status: status
                },
                success: function(r) {
                    s.image.photo.status = status;
                    s.image.dom.tools.options.toggleClass('toolsbt-on', (s.image.photo.status != 'public'));
                    // byrowan
                    $.each(s.map.photos,
                    function(i, v) {
                        if (v.id == id)
                        v.status = status;
                    });
                    $.each(s.search.results,
                    function(i, v) {
                        if (v.id == id)
                        v.status = status;
                    });
                    $.each(s.user.photos,
                    function(i, v) {
                        if (v.id == id)
                        v.status = status;
                    });
                }
            });
        },
        'more': {
            'show': function() {
                s.image.elements.container.get().addClass('image-list-view');
                s.image.more.reinit_scroll();
                s.image.more.scroll_api = $('#moreimagesscrollarea').data('jsp');
                // scroll to current thumb
                if (s.image.elements.list.get().find('.current').length) {
                    if(s.image.elements.list.get().find('.current').prev().length){
                        s.image.more.scroll_api.scrollToElement(s.image.elements.list.get().find('.current').prev(), true, true);
                    }else{
                        s.image.more.scroll_api.scrollToElement(s.image.elements.list.get().find('.current'), true, true);
                    }
                }
                s.image.set_next_prev(s.image.code,s.gallery.codes);
            },
            'hide': function() {
                s.image.elements.container.get().removeClass('image-list-view');
            },
            'activate': function() {
                s.lists.make_endless(s.image.elements.list.get(), 'list');
                s.image.elements.list.get().find('.x-pretty-date').prettyDate();
                s.image.more.reinit_scroll();
            },
            'reinit_scroll': function() {
                s.lists.reinit_scroll(s.image.elements.list.get(),{forceNoScrollH:true});
            }
        },
        'details': {
            'show': function() {
                s.image.elements.container.get().addClass('social-details');
                $('#commentsscroll').jScrollPane(s.scrollPane_settings);
                s.image.comments.set_height();
            },
            'hide': function() {
                s.image.elements.container.get().removeClass('social-details');
            }
        },
        'set_foursquare_venue': function(photo_id, venue_id, callback) {
            $.ajax({
                'url': '/api/photo/',
                'type': 'POST',
                'data': {
                    'id': photo_id,
                    'foursquare_venue_id': venue_id
                },
                'dataType': 'json',
                'success': function(response) {
                    if (callback) {
                        callback(response);
                    } else {
                        console.warn('set_foursquare_venue', response);
                    }
                },
                'error': function(e) {
                    console.warn('error', e)
                }
            })
        }
    };

    s.lists = {
        // endless scrolling
        'types': {
            'list': {
                // "more images" list
                'url': '/ajax/more_photos/',
                'data': function(data, list) {
                    return s.view.get_list_params(data)
                }
            },
            'stream': {
                // dash stream
                'url': '/ajax/more_photos/',
                'data': function(data, list) {
                    data = data || {};
                    var query = list.closest('.x-stream').data('query');
                    var query_array = query.split('&');

                    $.each(query_array,
                    function(k, v) {
                        v_split = v.split('=');
                        data[v_split[0]] = v_split[1];
                        // query += '&' + k + '=' + v
                    });
                    return data;
                }
            },
            'thumbs': {
                // thumbs view
                'url': '/ajax/thumbs/more/',
                'data': function(data, list) {
                    data = s.map.get_filter_params(data);
                    return s.view.get_list_params(data)
                }
            },
            'nearby': {
                'url': '/ajax/more_photos/',
                'data': function(data) {
                    return s.map.get_filter_params(data);
                }
            }
        },
        /**
     * Makes scrolable element of .x-thumb-wrapper elements load more at top or bottom on scroll
     * @argument scroll_area {jQuery} the element
     * @argument type {string} the type of thumb to load (list or thumbs)
     * @argument horizontal {bool} (optional)
     */
        'make_endless': function(scroll_area, type, horizontal) {
            var was_mid = false,
            // keep track if the scrol was ever in the middle, so you can't trigger without leaving top/bottom first
            list = scroll_area.find('.x-thumb-wrapper').parent(),
            event = horizontal ? 'jsp-scroll-x': 'jsp-scroll-y';

            scroll_area.data('paused', false);
            // stops the top action triggering during reinit.
            scroll_area.bind(event,
            function(event, position, top, bottom) {
                if ($(this).data('paused')) {
                    return;
                }
                if (!top && !bottom) {
                    was_mid = true;
                }
                if (top && was_mid) {
                    was_mid = false;
                    s.lists.get_newer(list, type);
                } else if (bottom && was_mid) {
                    was_mid = false;
                    s.lists.get_older(list, type);
                }
            });
        },
        /**
    * Gets and inserts older thumbs
    * @argument list {jQuery} The element containing the thumbs
    * @argument type {string} the type of thumb to load (list or thumbs)
    */
        'get_older': function(list, type) {
            // console.warn('get_older',list, type)
            // console.warn('typeof',typeof list);
            // don't get while a request is in action, or we'll get and append the same ones twice
            if(typeof list == 'string'){
                if (s.gallery.elements.gallery.get().is('.none-older')) {
                    return false;
                }
                var newdate = Date.parse(list).toSnaprString();
                var data = s.lists.types[type].data({'max_date': newdate});
            }else{
                if (list.is('.loading-bottom') || s.gallery.elements.gallery.get().is('.none-older')) {
                    return false;
                }
                var data = {};
                if(s.map.filter && s.map.filter.type){
                    $.extend(data, s.map.filter);
                    delete data.type;
                }
                if(type == 'stream'){
                    var container = list.closest('.dashboard-photostream');
                    var id = container.find('.snaprbubbleimage').data('photo');
                    var codes = s.dashboard.streams[container.data('query')];
                }else if (type == 'thumbs'){
                    var container = [];
                    var id = null;
                    var codes = s.gallery.codes;
                }else{
                    // (type == 'list')
                    var container = list.closest('#imagebubble');
                    var id = container.find('.snaprbubbleimage').data('photo');
                    var codes = s.gallery.codes;
                }

                var scroll_area = list.closest('.jspScrollable');

                list.addClass('loading-bottom');
                // console.warn('scroll_area',scroll_area)
                if(scroll_area){
                    s.lists.reinit_scroll(scroll_area);
                }
                // to show loader elements
                var oldest = list.find('.x-thumb-wrapper').last();
                var newdate = Date.parse(oldest.data('date')).add(-1).seconds();
                newdate = newdate.toSnaprString();
                $.extend(data, s.lists.types[type].data({'max_date': newdate},list));
            }
            delete data.date;

            $.ajax({
                url: s.lists.types[type].url,
                dataType: 'html',
                data: data,
                success: function(response) {
                    list.removeClass && list.removeClass('loading-bottom');
                    var elements = $(response);
                    if (elements.length) {
                        elements.insertBefore(list.find('.x-loading-older'));
                        list.removeClass && list.removeClass('none-older');
                        elements.find('.x-pretty-date').prettyDate();
                        $('a', response).each(function(i, v) {
                            var code = $(v).data('photo');
                            if (code) {
                                codes.push(code);
                            }
                        });

                        // s.lists.make_endless(scroll_area, 'stream', true);
                        s.lists.reinit_scroll(scroll_area);
                    } else {
                        if(typeof list == 'object'){
                            list.addClass('none-older');
                        }
                        if(s.view.subview == 'gallery'){
                            $('#snaprgallery').addClass('none-older');
                        }
                    }
                    if(type == 'stream'){
                        s.dashboard.set_next_prev(id,codes,container.data('query'));
                    }else{
                        s.image.set_next_prev(id,codes);
                    }
                    if(s.view.subview == 'gallery'){
                        s.gallery.activate();
                    }
                }
            });
        },
        'get_nearby': function(list, data) {
            $('.thumb-image-block', list).remove();
            list.addClass('loading-bottom');
            var scroll_area = list.closest('.jspScrollable');

            $.ajax({
                url: s.lists.types['nearby'].url,
                data: data,
                dataType: 'html',
                success: function(response) {
                    list.removeClass('loading-bottom');
                    var elements = $(response);
                    res = response;
                    el = elements;
                    s.gallery.codes = [];
                    $('a', response).each(function(i, v) {
                        var code = $(v).data('photo');
                        if (code) {
                            s.gallery.codes.push(code);
                        }
                    })
                    // console.warn('gall', s.gallery.codes)
                    elements.find('[data-photo="' + s.image.code + '"]').parent().addClass('current');
                    //ddd
                    if (elements.length) {
                        elements.insertBefore(list.find('.x-loading-older'));
                        list.removeClass('none-older');
                        elements.find('.x-pretty-date').prettyDate();
                    } else {
                        list.addClass('none-older');
                    }
                    s.lists.reinit_scroll(scroll_area);
                    s.image.set_next_prev(s.image.code, s.gallery.codes);
                }
            });
        },
        /**
    * Gets and inserts newer thumbs
    * @argument list {jQuery} The element containing the thumbs
    * @argument type {string} the type of thumb to load (list or thumbs)
    */
        'get_newer': function(list, type) {
            // don't get while a request is in action, or we'll get and append the same ones twice
            if (list.is('.loading-top')) {
                return false;
            }
            if(typeof list == 'String'){
                var newdate = list.toSnaprString();
                data = s.lists.types[type].data({'min_date': newdate});
            }else{


                var data = {};
                if(s.map.filter && s.map.filter.type){
                    $.extend(data, s.map.filter);
                    delete data.type;
                }
                if(type == 'stream'){
                    var container = list.closest('.dashboard-photostream');
                    var id = container.find('.snaprbubbleimage').data('photo');
                    var codes = s.dashboard.streams[container.data('query')];
                }else if (type == 'thumbs'){
                    var container = [];
                    var id = null;
                    var codes = s.gallery.codes;
                }else{
                    // (type == 'list')
                    var container = list.closest('#imagebubble');
                    var id = container.find('.snaprbubbleimage').data('photo');
                    var codes = s.gallery.codes;
                }

                var scroll_area = list.closest('.jspScrollable');

                list.addClass('loading-top');
                s.lists.reinit_scroll(scroll_area);
                // to show loader elements
                var newest = list.find('.x-thumb-wrapper').first();
                var newdate = Date.parse(newest.data('date')).add(1).seconds();
                newdate = newdate.toSnaprString();
                $.extend(data, s.lists.types[type].data({'min_date': newdate},list));
            }

            delete data.date;

            log(type, s.lists.types[type]);
            $.ajax({
                url: s.lists.types[type].url,
                dataType: 'html',
                data: data,
                success: function(response) {
                    list.removeClass('loading-top');
                    var elements = $(response);
                    if (elements.length) {
                        elements.insertAfter(list.find('.x-loading-newer'));
                        elements.find('.x-pretty-date').prettyDate();
                    }

                    $('a', response).each(function(i, v) {
                        var code = $(v).data('photo');
                        if (code) {
                            codes.unshift(code);
                        }
                    });

                    s.lists.reinit_scroll(scroll_area);
                    if(type == 'stream'){
                        s.dashboard.set_next_prev(id,codes,container.data('query'));
                    }else{
                        s.image.set_next_prev(id,codes);
                    }
                    if(s.view.subview == 'gallery'){
                        s.gallery.activate();
                    }
                }
            });
        },
        'reinit_scroll': function(scroll_area, settings) {
            if (!settings) {
                settings = s.scrollPane_settings;
            }
            scroll_area.data('paused', true);
            scroll_area.jScrollPane(settings);
            scroll_area.data('paused', false);
        },
    },
    s.list = {
        // deprecate
        'dom': {
            'bubble': null,
            // #snaprlistlayer
            'title': null,
            // #snaprlistlayer h1
            'list': null,
            // #snaprlistlayer ul
            'loading': null,
            // #snaprlistlayer ul li#listviewscrollarea-loading.template
            'image_template': null,
            // #snaprlistlayer ul li.photo.template
            'user_template': null,
            // #snaprlistlayer ul li.user.template
            'no_results': null,
            // #snaprlistlayer ul li.no-results.template
            'no_more_results': null,
            // #snaprlistlayer ul li.no-more-results.template
            'top_button': null
            // #topbartoggleviewbt
        },
        'init': function() {
            s.list.dom.bubble = $('#snaprlistlayer');
            s.list.dom.title = s.list.dom.bubble.find('h1');
            s.list.dom.list = s.list.dom.bubble.find('ul');
            s.list.dom.loading = s.list.dom.list.find('#listviewscrollarea-loading').removeClass('template').show().remove();
            s.list.dom.image_template = s.list.dom.list.find('.photo').removeClass('template').show().remove();
            s.list.dom.user_template = s.list.dom.list.find('.user').removeClass('template').show().remove();
            //s.list.dom.no_more_results = s.list.dom.list.find('.nomoreresults').removeClass('template').show().remove();
            s.list.dom.no_results = s.list.dom.list.find('.noresults').removeClass('template').show().remove();
            s.list.dom.top_button = $('#topbartoggleviewbt');

            s.list.dom.bubble.find('#listviewscroll').scroll(function() {
                if (s.view.view != 'user search') {
                    if ($(this).scrollBottom() < 30) {
                        s.list.get_older_photos();
                    }
                    //if( $(this).scrollTop() < 5 ){
                    //    s.list.get_newer_photos();
                    //}
                }
            });
        },
        'show': function() {

            s.list.dom.list.find('li').remove();
            s.list.insert_items(s.search.results, s.list.insert_users);
            s.list.dom.title.text('Search results for username "' + s.search.query + '"');


            // close other open bubbles
            //s.image.hide();
            $('.openbubble').not('#snaprlistlayer').fadeOut().removeClass('openbubble');
            //show bubble
            s.list.dom.bubble.fadeIn().addClass('openbubble');
            s.list.dom.top_button.addClass('listview');
            // reset scroll to top
            s.list.dom.list.parent().scrollTop(0);
            // must happen after show
        },
        'hide': function(finished) {
            if (s.list.dom.top_button) {
                s.list.dom.top_button.removeClass('listview');
            }
            if (s.list.dom.bubble) {
                s.list.dom.bubble.fadeOut().removeClass('openbubble');
            }
            if (finished !== false) {
                s.search.complete();
            }
            if (s.image.id) {
                s.image.show(s.image.id);
            }
        },
        'photos': function() {
            return s.user.photos;
        },
        'get_older_photos': function() {
            if (!s.list.getting_older_photos) {
                if (!$('.end:visible', s.list.dom.list).length) {
                    s.list.getting_older_photos = true;
                    s.list.dom.loading.hide().appendTo(s.list.dom.list).slideDown(200);
                    s.search.user(s.search.query, {
                        method: 'append'
                    });
                }
            }
        },
        'get_newer_photos': function() {
            if (!s.list.getting_newer_photos) {
                s.list.getting_newer_photos = true;
                s.list.dom.loading.hide().appendTo(s.list.dom.list).slideDown(200);
                s.search.user(s.search.query, {
                    method: 'prepend'
                });

            }
        },
        'insert_items': function(items, callback) {
            if (items.length) {
                callback(items);
            } else {
                s.list.dom.no_results.appendTo(s.list.dom.list);
            }
        },
        'insert_older_items': function(items, callback) {
            if (items.length) {
                callback(items);
            } else {
                s.list.dom.no_more_results.appendTo(s.list.dom.list);
            }
            s.list.dom.loading.remove();
            s.list.getting_older_photos = false;
        },
        'insert_newer_items': function(items, callback) {
            if (items.length) {
                callback(items);
            }
            s.list.dom.loading.remove();
            s.list.getting_newer_photos = false;
        },
        'insert_images': function(images) {
            $.each(images,
            function(i, image) {
                var li = s.list.dom.image_template.clone().appendTo(s.list.dom.list);
                li.find('.listtagtext a').text(image.description);
                li.find('.listtitle a').text(image.username);
                li.find('.listdetails').text(prettyDate(image.date));
                li.find('.listdetailsmore').text(image.location.location);
                li.find('.listviewthumb').attr('src', 'http://media-server2.s.us/thm/' + image.secret + '/' + image.id + '.jpg');
                li.click(function() {
                    s.list.hide(false);
                    s.image.show(image.id);
                });
            });
        },
        'insert_users': function(users) {

            $.each(users,
            function(i, user) {
                var li = s.list.dom.user_template.clone().appendTo(s.list.dom.list);
                li.find('.listtitle a').text(user.username);
                if (user.last_photo) {
                    li.find('.listtagtext a').text(user.last_photo.description);
                    li.find('.listdetails').text(prettyDate(user.last_photo.date));
                    li.find('.listdetailsmore').text(user.last_photo.location.location);
                    li.find('.listviewthumb').attr('src', '/avatars/' + user.username);
                }
                li.click(function() {
                    s.list.hide();
                    s.user.show(user.username);
                });
            });
        }
    };
    s.user = {
        'init': function() {
            s.user.load_templates();
            // <x class="x-user" data-username="username">username</x>
            $('.x-user').live('click',
            function() {
                if($('#photo-bubble:visible').length){
                    $('#ajax-img-overlay').show();
                }
                log('username clickd');
                s.view.go({
                    'username': $(this).data('username').toLowerCase(),
                    'subview': 'photo',
                    'code': null,
                    'search': null,
                    'list': 'user',
                    'bubble': null
                });
            });
            $('.icon-mysnaps').live('click',
            function() {
                log('username clickd');
                s.view.go({
                    'username': s.auth.username,
                    'subview': 'photo',
                    'code': null,
                    'search': null,
                    'list': 'user',
                    'bubble': null
                });
            });

            $('.x-show-profile').live('click',
            function() {
                var username = $(this).data('username');
                var container = $(this).closest('#imageextracon').find('.user-profile-popup-container');
                var profile = container.find('.user-profile');

                if (container.is(':visible')) {
                    container.hide();
                    $('.hover-hide-profile').removeClass('hover-hide-profile').addClass('hover-show-profile');
                } else {
                    $.ajax({
                        url: '/api/users/details/',
                        dataType: 'jsonp',
                        data: {
                            'username': username
                        },
                        success: function(response) {
                            if (response.success) {
                                profile.replaceWith($("#user-profile-template").tmpl(response.response));
                                container.show()
                                $('.hover-show-profile').removeClass('hover-show-profile').addClass('hover-hide-profile');
                            } else {
                                alert(response.error.message);
                            }
                        }
                    });
                }
            });
            $('.x-show-profile').live({
                mouseenter: function() {
                    if ($(this).closest('#imageextracon').find('.user-profile-popup-container').is(':visible')) {
                        $(this).closest('div').addClass('hover-hide-profile');
                    } else {
                        $(this).closest('div').addClass('hover-show-profile');
                    }
                },
                mouseleave: function() {
                    if ($(this).closest('#imageextracon').find('.user-profile-popup-container').is(':visible')) {
                        $(this).closest('div').removeClass('hover-hide-profile');
                    } else {
                        $(this).closest('div').removeClass('hover-show-profile');
                    }
                }
            });
            $('#imageauthor .x-user').live({
                mouseenter: function() {
                    $(this).closest('div').addClass('hover-show-photostream');
                },
                mouseleave: function() {
                    $(this).closest('div').removeClass('hover-show-photostream');
                }
            });

            $('.x-hide-profile').live('click',
            function() {
                var container = $(this).closest('#imageextracon').find('.user-profile-popup-container');
                container.hide();
            });

            $('.x-dash-add').live('click',
            function() {
                if (!$(this).hasClass('added')) {
                    var username = $(this).data('username');
                    var button = $(this);
                    if (confirm("Would you like to add " + username + " to your dashboard?")) {
                        $('#x-dash-add').addClass('loading');
                        s.dashboard.add_person.by_username(username,
                        function() {
                            button.removeClass('not-added').addClass('added');
                        });
                    }
                }
            });

        },
        // David, can we remove this?
        'load_templates': function(callback) {
            if (callback) {
                callback();
            }
        },
        'username': '',
        'photos': [],
        'get_photos': function(options) {

            // set container to photostream if image bubble is open, else list
            /*if(s.image.id){
                    var container = s.image.dom.photostream;
                    var template = snapr_web.photostream_template;
            }else{
                    var container = s.list.dom.list;
                    var template = snapr_web.list_view.template;
            }*/

            var o = {
                parent: s.user,
                // put new photos into s.map
                //container: container,    // if in map view, with
                //template: template,    // function, takes photo and returns dom representation
                method: 'replace',
                // 'replace' all, 'append' or 'prepend'
                no_results: function(r) {
                    if (r.options.method == 'replace') {
                        if (s.user.username == s.auth.username) {
                            $('#tab-my-account').click();
                        } else {
                            alert(s.user.username + ' has no images yet.');
                        }
                    }
                }
                // callback when there are no results
            }
            $.extend(o, options);

            var data = {
                sort: 'date_utc',
                username: s.user.username,
                n: s.map.max_images
            }

            switch (o.method) {
            case 'append':
                $.extend(data, {
                    max_date: s.user.photos[s.user.photos.length - 1].date
                })
                break;
            case 'prepend':
                $.extend(data, {
                    min_date: s.user.photos[0].date
                })
                break;
            }

            $.ajax({
                url: s.api_url + '/search/',
                data: data,
                success: function(data) {
                    $.extend(o, {
                        photos: data.response.photos || []
                    })
                    got_photos(o);
                }
            });
        },
        'show': function(username) {
            s.thumbs.hide();
            s.gallery.hide();
            s.user.username = username;
            s.image.id = null;
            s.view.list = 'user';
            s.search.type = null;
            s.search.query = null;
            s.image.show_bubble();
        }
    };
    s.search = {
        'dom': {},
        'init': function() {
            s.search.geocoder = new google.maps.Geocoder();
            $('#topbarsearchfield').smartFocus('quick search...');
        },
        'type': null,
        'query': null,
        'submit': function(q) {
            var q = q || $('#topbarsearchfield').val();

            // call right function for the selected search type
            switch ($('#topbarsearchtype').val().toLowerCase()) {
            case 'search locations':
                s.search.locations(q);
                break;
            case 'search tags':
                s.search.tags(q);
                break;
            case 'search users':
                s.people.query = q;
                var params = s.view.default_params();
                if (s.view.view == 'dash') {
                    params.view = 'dash';
                }
                params.user_search = q;

                var curr = s.view.get_current_params();

                if (curr.bubble == 'people') {
                    //dpwolf do in page search
                    $('[href="#people-search-tab"]').click();
                    $('#people-search-tab ul').empty().append('<li>Loading people…</li>');
                    $('#people-search-username').val(params.user_search);
                    s.search.users(params.user_search, s.people.add_search_results);

                } else {
                    s.view.go(params);
                }


                // s.search.users(q, s.list.show);
            }
            return false;
        },
        'geocoder': null,
        'locations': function(q) {

            s.search.geocoder.geocode({
                'address': q
            },
            function(results, status) {
                if (status == google.maps.GeocoderStatus.OK) {
                    //if there is more than one result, show list
                    if (results.length > 1) {

                        // set #/?st and q
                        s.image.hide();
                        s.search.type = 'location';
                        s.search.query = q;


                        var bubble = $('#topbarsearchclarification');
                        var list = bubble.find('ul');

                        // remove old results
                        list.find('li').remove();

                        // put new results in
                        $.each(results,
                        function(i, result) {
                            var li = $('<li><a class="clickable">' + result.formatted_address + '</a></li>')
                            li.click(function() {
                                s.map.map.fitBounds(result.geometry.viewport);
                                s.search.complete();
                                $('#topbarsearchclarification').slideUp(100).removeClass('openbubble');
                            });
                            list.append(li);
                        });

                        //show bubble
                        $('.openbubble').fadeOut();
                        bubble.slideDown(100).addClass('openbubble');
                        // if there is just one result
                    } else {
                        // center the map there
                        var params = s.view.default_params();
                        delete params.filter_map;
                        s.view.go(params)
                        s.map.map.fitBounds(results[0].geometry.bounds);
                    }
                } else {
                    if (status == 'ZERO_RESULTS') {
                        var bubble = $('#topbarsearchclarification');
                        var list = bubble.find('ul');
                        // remove old results
                        list.find('li').remove();
                        var li = $('<li><a>Sorry, your search returned no results.</a></li>')
                        list.append(li);
                        //show bubble
                        $('.openbubble').hide();
                        s.image.hide();
                        bubble.slideDown(100).addClass('openbubble');
                    }
                }
            });
        },
        'users': function(q, callback, more) {
            $('#people-search-tab ul').empty().append('<li>Loading people…</li>');
            // set #/?st and q
            $.ajax({
                url: s.api_url + '/users/search/',
                data: {
                    username: q,
                    n: 20
                },
                success: function(data) {
                    s.search.results = more ? $.merge(s.search.results, data.response.users) : data.response.users;
                    if (data.response.users.length) {
                        callback(data.response.users);
                    } else {
                        $('#people-search-tab ul').empty().append('<li><p>Sorry, your search for ' + q + ' returned no results.</p></li>')
                    }
                }
            });
            return false;
        },
        'tags': function(q, options) {

            s.view.go({
                'subview': 'thumbs',
                'list': 'search',
                'search': q,
                'stream': null,
                'code': null,
                'username': null,
                'bubble': null,
                'bubble_tab': null
            });
            // set #/?st and q
            s.search.type = 'tag';
            //s.view.view = 'thumbs';
            //s.view.list = 'search';
            s.search.query = q;


            //s.thumbs.show();
        },
        'favs': function(user, options) {
            // set #/?st and q
            s.search.type = 'fav';
            s.view.list = 'fav';
            s.search.query = user;


            // set container to photostream if image bubble is open, else list
            /*if(s.image.id){
                    var container = s.image.dom.photostream;
                    var template = snapr_web.photostream_template;
            }else{
                    var container = s.list.dom.list;
                    var template = snapr_web.list_view.template;
            }*/

            var o = {
                parent: s.search,
                // put new photos into s.search
                //container: container,    // if in map view, with
                //template: template,    // function, takes photo and returns dom representation
                method: 'replace',
                // 'replace' all, 'append' or 'prepend'
                no_results: function(r) {
                    if (r.options.method == 'replace') alert(s.search.query + ' has no favorites yet.');
                }
                // callback when there are no results
            }
            $.extend(o, options);

            var data = {
                sort: 'date_utc',
                favorited_by: user,
                n: s.map.max_images
            }

            switch (o.method) {
            case 'append':
                $.extend(data, {
                    max_date: s.search.photos[s.search.photos.length - 1].date
                })
                break;
            case 'prepend':
                $.extend(data, {
                    min_date: s.search.photos[0].date
                })
                break;
            }

            $.ajax({
                url: s.api_url + '/search/',
                data: data,
                success: function(data) {
                    $.extend(o, {
                        photos: data.response.photos
                    })
                    got_photos(o);
                }
            });

        },
        'results': [],
        'complete': function() {
            $('#topbarsearchfield').val('quick search...');
            s.search.type = null;
            s.view.view = 'map';
            s.view.list = 'area';
            s.search.query = null;

        }
    };
    s.map = {
        'elements': {
            'map': new cached_jQ('#map-layer'),
            'max_images': new cached_jQ('#mapimages'),
            'style': new cached_jQ('#mapstyle'),
            'time': new cached_jQ('#time'),
            'tabs': new cached_jQ('#snaprtabslayer')
        },
        'filter': null,
        // user, following, search
        'loaded': false,
        'map': null,
        'scroll': true,
        'location': '8.4938,138.8092',
        'date': null,
        'zoom': 2,
        // 'zooming': false,
        'max_images': 30,
        'overlays': [],
        'projection_helper': new google.maps.OverlayView(),
        'init': function() {
            if (window.location.hash.indexOf('/dash') < 0) {
                $('#snaprtimelayer').show();
                $('#snaprtabs').show();
                var world = new google.maps.LatLngBounds(new google.maps.LatLng(70, -0), new google.maps.LatLng( - 60, 0))
                $('#snaprtopbarleft a:first').click(function(e) {
                    e.preventDefault();
                    var params = s.view.default_params();
                    delete params.filter_map;
                    s.view.go(params);
                    s.map.map.fitBounds(world);
                });

                // get initial values from cookie
                var location_cookie = $.cookie('location');
                if (location_cookie && location_cookie.indexOf('NaN') < 0) {
                    location_cookie = location_cookie.split(':')
                    s.map.location = location_cookie[0]
                    s.map.zoom = parseInt(location_cookie[1])
                }

                var time_bubble = $('#snaprtimelayer'),
                people_menu = time_bubble.find('.x-map-people-options').hide(),
                people_menu_timer;

                time_bubble.find('.x-thumbs-bt').click(function() {
                    s.view.go({
                        'subview': 'thumbs',
                        'code': null,
                        'username': null,
                        'bubble': '',
                        'map':{
                            'location': s.map.location,
                            'date':s.map.date,
                            'zoom':s.map.zoom
                        }
                    });
                });

                people_menu.mouseleave(function() {
                    people_menu_timer && clearTimeout(people_menu_timer);
                    people_menu_timer = setTimeout(function() {
                        people_menu.hide();
                    },
                    2000);
                });

                time_bubble.find('.x-map-people-select').click(function() {
                    people_menu.show();
                    people_menu_timer && clearTimeout(people_menu_timer);
                    people_menu_timer = setTimeout(function() {
                        people_menu.hide();
                    },
                    4000);
                });

                if ($('body').hasClass('loggedin')) {
                    time_bubble.find('.x-map-people-options .me').text(s.auth.username);
                }

                time_bubble.find('.x-map-people-options .me').click(function() {
                    s.view.go({
                        'subview': null,
                        'filter_map': {
                            'type': 'user',
                            'username': s.auth.username
                        }
                    });
                    time_bubble.find('.x-map-people-select').val(s.auth.username);
                    people_menu.hide();
                });
                time_bubble.find('.x-map-people-options .everyone').click(function() {
                    s.view.go({
                        'subview': null,
                        'filter_map': false
                    });
                    time_bubble.find('.x-map-people-select').val('Everyone');
                    people_menu.hide();
                });
                time_bubble.find('.x-map-people-options .following').click(function() {
                    s.view.go({
                        'subview': null,
                        'filter_map': {
                            'type': 'group',
                            'group': 'following'
                        }
                    });
                    time_bubble.find('.x-map-people-select').val('Following');
                    people_menu.hide();
                });

                $('.photo_bubble_close').live('click',
                function() {
                    var params = s.view.default_params();
                    delete params.filter_map;
                    s.view.go(params);
                });

                // get max images cookie
                s.map.max_images = $.cookie('max_images') || 20;
                s.map.elements.max_images.get().val(s.map.max_images);

                //get map type cookie
                map_style = $.cookie('map_style') || 'ROADMAP';
                s.map.elements.style.get().val(map_style);

                var location = this.location.split(',');
                var latlng = new google.maps.LatLng(location[0], location[1]);
                var snaprOptions = {
                    zoom: this.zoom,
                    center: latlng,
                    mapTypeId: google.maps.MapTypeId[map_style],
                    mapTypeControl: false,
                    streetViewControl: false
                };

                s.map.map = new google.maps.Map(s.map.elements.map.get()[0], snaprOptions);
                // google maps api v3 does not have an easy way to convert px to WGS
                s.map.projection_helper.setMap(s.map.map);
                s.map.projection_helper.draw = function() {
                    if (!this.ready) {
                        this.ready = true;
                        google.maps.event.trigger(this, 'ready');
                    }
                };

                // event:idle = map has finished drawing after loading or changing pan or zoom
                google.maps.event.addListener(s.map.map, 'idle',
                function() {
                    // update fragment
                    if (s.map.location && s.map.location !== s.map.map.getCenter().toUrlValue(8) && s.map.scroll) {
                        // console.warn('map moved');
                        var params = s.view.get_current_params();
                        //dpp
                        params.map.location = s.map.map.getCenter().toUrlValue(8);
                        s.map.location = s.map.map.getCenter().toUrlValue(8);
                        params.map.zoom = s.map.map.getZoom();
                        params.map.date = s.map.date;
                        s.map.zoom = s.map.map.getZoom();
                        s.view.go(params);
                    } else {
                        // if there is no map location already set, zoom to the world
                        if(!s.map.location){
                            var world = new google.maps.LatLngBounds(new google.maps.LatLng(70, -0), new google.maps.LatLng( - 60, 0));
                            s.map.map.fitBounds(world);
                        }
                        // if zoom has changed
                        if (s.map.zoom != s.map.map.getZoom()) {
                            console.warn('zoom has changed');
                            var params = s.view.get_current_params();
                            params.map.zoom = s.map.map.getZoom();
                            s.map.zoom = s.map.map.getZoom();

                            // if an images is showing, keep it's pin right
                            // also check to see that #imagelocation has loaded
                            if (s.image.code && $('#imagelocation').length) {
                                var point = $('#imagelocation').data('point');
                                // check that lat and lon are not 0
                                if(point.latitude || point.longitude){
                                    // console.warn('pan_point keep it right',point)
                                    s.map.pan_point_to_arrow(new google.maps.LatLng(point.latitude, point.longitude), false);
                                }
                            }
                            s.view.go(params);
                        }
                    }
                    if (s.map.location.indexOf('NaN' == -1)) {
                        //dpwolf temp just to make sure we don't save a dodgy location as a cookie
                        $.cookie('location', s.map.location + ':' + s.map.zoom, {
                            expires: 365
                        });
                        // get thumbs for new location and call update functoin to place them.
                        s.map.loaded = true;
                        s.map.get_images();
                    }
                    // s.map.scroll = true;
                });

                // break bubble pin arrow on map drag
                google.maps.event.addListener(s.map.map, 'dragstart', s.image.arrow.hide);

                s.map.time.set(s.map.date);
                s.map.elements.time.get().find('.map-clock').click(function() {
                    s.map.time.set();
                    s.map.date = null;
                    s.view.go({
                        'subview': null
                    });
                    s.map.get_images();
                });
                s.map.elements.time.get().find('.timedisplay').click(function() {
                    $(this).toggleClass('absolute-date-mode');
                });

                // Map type control
                s.map.elements.style.get().change(function() {
                    s.map.map.setMapTypeId(google.maps.MapTypeId[$(this).val()]);
                    $.cookie('map_style', $(this).val(), {
                        expires: 365
                    });
                });

                // Max images control
                s.map.elements.max_images.get().change(function() {
                    s.map.max_images = $(this).val();
                    s.map.get_images();
                    $.cookie('max_images', s.map.max_images, {
                        expires: 365
                    });
                });
            }

        },
        'show': function(photo_id) {
            s.view.go({
                'subview': null,
                'code': photo_id
            });
        },
        'photos': [],
        'time': {
            'set': function(time) {
                if (time) {
                    s.map.elements.time.get().find('.relative-date').text(prettyDate(time));
                    s.map.elements.time.get().find('.absolute-date').text(prettyDate(time, false));
                    s.map.date = time;
                } else {
                    s.map.elements.time.get().find('.relative-date').text('Now');
                    s.map.elements.time.get().find('.absolute-date').text('Now');
                };
            }
        },
        //get photos in map area
        'get_images': function() {
            // only get images if we're in map view and the map is loaded so we can get bounds
            if (s.view.view == 'map' && s.map.map.getBounds()) {
                clearTimeout(s.map.timeout);

                var data = {
                    n: s.map.max_images,
                    area: s.map.map.getBounds().toUrlValue(5)
                }

                if (s.map.date) {
                    $.extend(data, {
                        date: s.map.date
                    })
                } else {
                    $.extend(data, {
                        sort: 'date_utc'
                    })
                }

                if (s.map.filter) {
                    $.extend(data, s.map.get_filter_params())
                }

                $('#snaprmapalert').fadeOut(200);

                s.map.xhr && s.map.xhr.abort();
                s.map.xhr = $.ajax({
                    url: s.api_url + '/thumbs/',
                    data: data,
                    success: function(results) {
                        if (!s.map.date) {
                            s.map.timeout = setTimeout(function() {
                                s.map.get_images();
                            },
                            5 * 60 * 1000);
                        }
                        if (!results || !results.response.photos.length) {
                            $('#snaprmapalert').fadeIn(200);
                            s.map.update_photos([]);
                        }else{
                            s.map.update_photos(results.response.photos);
                        }
                    }
                });

                if (s.view.subview == 'photo' && s.view.list == 'area') {
                    // console.warn('s.lists.get_nearby');
                    var list = $('#moreimagesscroll ul');
                    s.lists.get_nearby(list, data);
                    //dddd
                }
            }
        },
        'get_filter_params': function(extras) {
            var data = extras || {};
            switch (s.map.filter.type) {
            case 'user':
                data['username'] = s.map.filter.username;
                break;
            case 'group':
                data['group'] = s.map.filter.group;
                break;
            }
            return data;
        },

        'last_photos': null,
        // find photos that are new and those that need to be removed
        'update_photos': function(new_photos) {
            if(JSON.stringify(new_photos) != s.map.last_photos){
                console.log('new map photos', new_photos);
                s.map.last_photos = JSON.stringify(new_photos);
                s.map.remove_photos();
                s.map.photos = new_photos;
                s.map.place_photos();
            }else{
                console.log('not updating map photos, they havent changed');
            }
        },
        // put s.map.photos on map
        'place_photos': function() {
            sorted = s.map.photos.slice();
            sorted.sort(function(a, b) {
                return parseFloat(b.location.latitude) - parseFloat(a.location.latitude)
            });
            $.each(sorted,
            function(i, snap) {
                s.map.overlays[i] = new map_thumb(snap, s.map.map);
            });
        },

        // clear photos from map
        'remove_photos': function() {
            $.each(s.map.overlays,
            function(i, overlay) {
                overlay.setMap(null);
            });
        },
        'go_to': function(latitude, longitude, zoom) {
            if (latitude && longitude) {
                var point = new google.maps.LatLng(latitude, longitude)
                if (!zoom) {
                    s.map.map.panTo(point);
                    return;
                } else {
                    s.map.map.setCenter(point);
                }
            }
            if (zoom) {
                s.map.map.setZoom(zoom);
            }

        },
        'pan_point_to_arrow': function(point, zoom) {
            console.log('pan_point_to_arrow', point, zoom)
            if (s.map.scroll) {
                // console.warn('pan_point_to_arrow',point,zoom);
                var projection = s.map.projection_helper.getProjection();
                // if projection helper is not ready, try again soon
                if (!projection) {
                    setTimeout(function() {
                        console.log('activated from timeout');
                        s.map.pan_point_to_arrow(point);
                    },
                    100);
                    return false;
                }
                //zoom map if need be
                if (s.map.map.getZoom() < 5 && (zoom !== false)) {
                    s.map.map.setZoom(5);
                }

                // Move map
                var offsetx = $(window).width() / 2 - $('.basepinarrow').offset().left - 7;
                var offsety = $('#imagebubble').offset().top - $(window).height() / 2 + 492;

                var pxlocation = projection.fromLatLngToContainerPixel(point);
                s.map.map.panTo(projection.fromContainerPixelToLatLng(new google.maps.Point(pxlocation.x + offsetx, pxlocation.y - offsety)));
                // show bubble pin arrow
                s.image.arrow.show();
            } else {
                s.image.arrow.hide();
            }
            s.map.scroll = true;
        }
    }
    s.auth = {
        'dom': {
            'button': null,
            // #topbarloginbt a
            'message': null
            // #topbarloginms
        },
        'init': function() {
            s.auth.dom.message = $('#topbarloginms')
            .click(function() {
                // if logged in
                if (s.auth.username) {
                    //show photostream
                    var params = s.view.default_params();
                    params.view = s.view.view;
                    params.subview = 'photo';
                    params.username = s.auth.username;
                    s.view.go(params);
                } else {
                    // register
                    if($('#topbarloginpanel').is(':visible')){
                        s.auth.dom.button.click();
                    }
                    s.view.go({
                        bubble: 'join-snapr'
                    });
                }

            });
            s.auth.dom.button = $('#topbarloginbt a')
            .click(function() {
                // if logged in
                if (s.auth.username) {
                    //log out
                    s.auth.logout();
                } else {
                    $('#topbarloginpanel').toggle()
                    .find('input.username').last().focus();
                    $(this).parent().toggleClass('loginclicked');
                    if ($.browser.msie) $('#map-layer').hide().show();
                    // remind ie that the map exists
                }
            });
            $('.join-button').click(function() {
                s.bubble.load({
                    url: '/sc/join-snapr/'
                });
            });
            $('.login-button').live('click',function() {
                if($('#topbarloginpanel').is(':hidden')){
                    s.auth.dom.button.click();
                }
            });
        },
        'username': null,
        'login': function(top) {
            if(top){
                var form = $('.snaprloginform').first();
            }else{
                var form = $('.snaprloginform').last();
            }

            var username = form.find('input.username').val();
            var password = form.find('input.password').val();
            var remember = Boolean(form.find('#rememberme:checked').length);
            var hash = window.location.hash;
            var subview = s.view.subview;
            $.ajax({
                url: '/vweb/login/',
                type: 'POST',
                data: {
                    id: username,
                    pw: password
                },
                success: function(data) {
                    if (data.success) {
                        // success
                        // close login window
                        $('#topbarloginpanel').hide();

                        //clear username & passsword fields
                        $('input.username').val('')
                        $('input.password').val('')
                        s.image.hide();

                        $('#modal').dialog('close');
                        $('#modal').empty();

                        s.auth.loggedin(username);
                        if (s.view.view == 'dash') {
                            s.dashboard.reset();
                            s.dashboard.loaded = false;
                            s.dashboard.show();
                            s.view.go({bubble:''});
                        } else {
                            if (!s.auth.redirect.destination.url && !subview) {
                                s.auth.redirect.destination.url = '#/dash/';
                            } else {
                                window.location.hash = '#/';
                                s.view.change();
                                if(!s.auth.redirect.destination.url){
                                    s.auth.redirect.destination.url = hash;
                                }
                            }
                        }
                        s.auth.redirect.go();
                    } else {
                        alert(data.error.message);
                    }
                }
            });
            return false;
        },
        'loggedin': function(username) {
            // change header text to "welcome username"
            $('#topbarloginms').html('<p>Welcome <a>' + username + '</a></p>');
            // change button to logout
            $('#topbarloginbt a').parent().removeClass('loginclicked');
            s.auth.username = username;
            $('.x-map-people-options .me').text(s.auth.username);
            $('body').addClass('loggedin');
            s.map.get_images();
        },
        'logout': function() {
            $.ajax({
                url: '/vweb/logout/',
                type: 'POST',
                success: function(data) {
                    if (data.success) {
                        // success
                        $('#topbarloginms').html('<p><a class="clickable" href="#/join-snapr/">Create an Account</a></p>');
                        $('body').removeClass('loggedin');
                        s.auth.username = null;
                        $('.x-map-people-options .me').text('Just Me');
                        s.dashboard.loaded = false;
                        $('#news-feed ul').empty();
                        $('#news-feed').hide();
                        if (s.view.view == 'dash') {
                            // console.warn('logout');
                            s.view.go(s.view.default_params());
                        } else {
                            if(s.view.subview == 'photo'){
                                var hash = window.location.hash;
                                window.location.hash = '#/'
                                s.view.change();
                                window.location.hash = hash;
                                s.view.change();
                            }else{
                                window.location.hash = '#/'
                                s.view.change();
                            }
                        }
                        s.dashboard.reset();
                    } else {
                        alert(data.error.message);
                    }
                }
            });
        },
        'redirect': {
            'destination': {},
            'go': function() {
                if (url = s.auth.redirect.destination.url) {
                    s.auth.redirect.destination = {};
                    if (window.location.hash !== url) {
                        window.location.hash = url;
                        s.auth.redirect.destination.url = false;
                        s.view.change();
                    } else {
                        //manually refresh url
                        url = window.location.hash;
                        window.location.hash = '';
                        window.location.hash = url;
                        s.auth.redirect.destination.url = false;
                        s.view.change();
                    }
                }
            },
        },

        'twitter_register': function() {
            $('#modal')
            .find('.step1').hide()
            .end()
            .find('.step2').show();
        },
        'twitter_authenticate': function() {
            $.ajax({
                url: '/vweb/twitter_auth_url/',
                success: function(data) {
                    if (data.success) {
                        $('<p id="waiting-twitter">Waiting for Twitter authentication.<br/><br/> If the Twitter window does not open it may have been stopped by a popup blocker.<br/><br/><a href="' + data.response.url + '" target="_blank">Click here to authenticate Twitter in a new window</a>.</p>').dialog({
                            dialogClass: 'white',
                            show: 'scale',
                            width: 300,
                            resizable: false,
                            modal: true,
                            buttons: {
                                "Cancel": function() {
                                    $(this).dialog("close");
                                }
                            }
                        })
                        var winl = (screen.width - 900) / 2;
                        var wint = (screen.height - 380) / 2;
                        if (winl < 0) winl = 0;
                        if (wint < 0) wint = 0;
                        window.open(
                        data.response.url,
                        'twitter_auth',
                        'status = 0, resizable = 0, dependent = 1, height = 380, width = 900, top = ' + wint + ', left = ' + winl);
                    } else {
                        alert(data.error.message);
                    }
                }
            });
            return false;
        },
        'twitter_return': function(username) {
            $('#waiting-twitter').dialog("close");
            $('#modal .step2').tabs('select', 1);
            $('#createaccountsnapr #importtwitterprofile').attr('checked', true).data('done', true);

            $('#username').val(username);

            var v = function() {
                var b = $('#createaccountsnapr').validate().element('#username');
                if (b === false) {
                    $('#twitter-username-error').show();
                } else if (b === true) {
                    $('#twitter-success').show();
                } else {
                    setTimeout(v, 200);
                }
            };
            v();
        },
        'snapr_register': function() {
            $('#modal')
            .find('.step1').hide()
            .end()
            .find('.step2').show()
            .tabs('select', 1);
        },
        'submit_register': function(form, re_challenge, re_response) {
            username = $(form).find('#username').val();
            if ($(form).find('#importtwitterprofile').data('done')) {
                url = '/vweb/twitter_signup/';
            } else {
                url = s.api_url + '/user/signup/';
            }
            $.ajax({
                url: url,
                type: 'POST',
                data: {
                    username: username,
                    password: $(form).find('#snaprpassword').val(),
                    email: $(form).find('#snapremail').val(),
                    import_twitter: $(form).find('#importtwitterprofile').is(':checked'),
                    re_challenge: re_challenge,
                    re_response: re_response
                },
                success: function(data) {
                    if (data.success) {
                        $('#modal input').val('')
                        s.auth.loggedin(username);
                        $('#modal h1').text('Success!');
                        $('#sign-in-up-forms').hide();
                        $('#sign-in-up-success').show()
                            .find('.username').text(username);

                        $('#modal #show-my-profile').click(function() {
                            $('#tab-my-account').click();
                        });
                    } else {
                        alert(data.error.message);
                    }
                }
            });
            return false;
        },
        'forgot_password': function(form) {
            form = $(form);
            $.ajax({
                url: '/vweb/forgot_password/',
                type: 'POST',
                data: {
                    email: form.find('input[name=email]').val()
                },
                success: function(data) {
                    if (data.success) {
                        form.hide()
                        .next('.success').show();
                    } else {
                        alert(data.error.message);
                    }
                }
            });
            return false;
        },
        'my_account': {
            'save_account': function(form) {
                $(form).parent().find('.save-success').remove();
                $.ajax({
                    url: s.api_url + '/user/settings/',
                    type: 'POST',
                    data: {
                        email: $('#accemail', form).val(),
                        name: $('#accyourname', form).val(),
                        findableByEmail: $('#importtwitterprofile', form).is(':checked')
                    },
                    success: function(data) {
                        if (data.success) {
                            $(form).find('.validate-success').removeClass('validate-success')
                            .end().parent().append('<p class="save-success">Thanks, Your Settings have been Saved</p>');
                            setTimeout(function() {
                                $(form).next('.save-success').fadeOut(1000);
                            },
                            5000);
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
                return false;
            },
            'save_password': function(form) {
                $(form).parent().find('.save-success').remove();
                $.ajax({
                    url: s.api_url + '/user/settings/',
                    type: 'POST',
                    data: {
                        password: $('#pwnewpassword', form).val()
                    },
                    success: function(data) {
                        if (data.success) {
                            $(form).find('.validate-success').removeClass('validate-success')
                            .end().parent().append('<p class="save-success">Thanks, Your Settings have been Saved</p>');
                            setTimeout(function() {
                                $(form).next('.save-success').fadeOut(1000);
                            },
                            5000);
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
                return false;
            },
            'save_notifications': function(form) {
                $(form).parent().find('.save-success').remove();
                $.ajax({
                    url: s.api_url + '/user/settings/',
                    type: 'POST',
                    data: {
                        notifyWhenAddedToGroup: $('#ntfollow', form).is(':checked'),
                        notifyWhenImageFavorited: $('#ntfav', form).is(':checked'),
                        notifyWhenCommentAdded: $('#ntcomment', form).is(':checked'),
                        notifyAfterComment: $('#ntafter_comment', form).is(':checked'),
                        notifyWhenImageModerated: $('#ntmoderation', form).is(':checked'),
                        subscribeToEmailNewsletter: $('#ntnewsupdates', form).is(':checked')
                    },
                    success: function(data) {
                        if (data.success) {
                            $(form).find('.validate-success').removeClass('validate-success')
                            .end().parent().append('<p class="save-success">Thanks, Your Settings have been Saved</p>');
                            setTimeout(function() {
                                $(form).next('.save-success').fadeOut(1000);
                            },
                            5000);
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
                return false;
            },
            'save_profile': function(form) {
                $(form).parent().find('.save-success').remove();
                $.ajax({
                    url: s.api_url + '/user/settings/',
                    type: 'POST',
                    data: {
                        name: $('#pflyourname', form).val(),
                        location: $('#pfllocation', form).val(),
                        website: $('#pflwebsite', form).val(),
                        bio: $('#pflbio', form).val(),
                        icon: $('input[name=icon]:checked', form).val()
                    },
                    success: function(data) {
                        if (data.success) {
                            $(form).find('.validate-success').removeClass('validate-success')
                            .end().parent().append('<p class="save-success">Thanks, Your Settings have been Saved</p>');
                            setTimeout(function() {
                                $(form).next('.save-success').fadeOut(1000);
                            },
                            5000);
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
                return false;
            }
        },
        'twitter': {
            'unlink': function(username) {
                $('#twitter-' + username).find('.service-bt-unlink a').text('Unlinking...');
                $.ajax({
                    url: s.api_url + '/linked_services/twitter/delete/',
                    type: 'POST',
                    data: {
                        username: username
                    },
                    success: function(data) {
                        if (data.success) {
                            $('#link-twitter').removeClass('linked').addClass('not-linked')
                            .find('.service-details a').text('').attr('href', '');
                            $('#link-twitter .service-settings').hide();
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'import_profile': function(username) {
                $('#twitter-' + username).find('.service-bt-two a').text('Importing...');
                $.ajax({
                    url: s.api_url + '/linked_services/twitter/import_profile/',
                    type: 'POST',
                    data: {
                        username: username
                    },
                    success: function(data) {
                        if (data.success) {
                            s.bubble.load({
                                url: '/sc/my-account/',
                                success: function() {
                                    $('#modal .tabbedcontent').tabs('select', 3);
                                }
                            });
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'show_settings': function() {
                $('#link-twitter .service-settings').slideToggle();
            },
            'link': function() {
                s.auth.twitter_authenticate();
                s.auth.twitter_return = function(username) {
                    $('#waiting-twitter').dialog("close");
                    $.ajax({
                        url: s.api_url + '/linked_services/twitter/',
                        type: 'POST',
                        success: function(data) {
                            if (data.success) {
                                s.bubble.load({
                                    url: '/sc/my-account/',
                                    success: function() {
                                        $('#modal .tabbedcontent').tabs('select', 4);
                                    }
                                });
                            } else {
                                alert(data.error.message);
                            }
                        }
                    });
                };
            },
            'save': function() {

                // The element containging settings chekcboxes etc
                var settings = $('#link-twitter'),

                // settings to pass to the api
                data = {
                    show_name: settings.find('#showacc-twtr').is(':checked'),
                    allow_tweets: settings.find('#allowuse-twtr').is(':checked')
                };
                $.ajax({
                    url: s.api_url + '/linked_services/twitter/',
                    type: 'POST',
                    data: data,
                    success: function(data) {
                        if (data.success) {
                            s.linked_service.twitter.show_settings();
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            }
        },
        'facebook': {
            'link': function() {
                $('<p id="waiting-facebook">Waiting for Facebook authentication.<br/><br/> If the Facebook window does not open it may have been stopped by a popup blocker.<br/><br/><a href="https://graph.facebook.com/oauth/authorize?scope=user_photos,offline_access,publish_stream&client_id=156852557659134&redirect_uri='+s.base_url+'vweb/facebook_return/" target="_blank">Click here to authenticate Facebook in a new tab</a>.</p>').dialog({
                    dialogClass: 'white',
                    show: 'scale',
                    width: 300,
                    resizable: false,
                    modal: true,
                    buttons: {
                        "Cancel": function() {
                            $(this).dialog("close");
                        }
                    }
                })
                var winl = (screen.width - 500) / 2;
                var wint = (screen.height - 450) / 2;
                if (winl < 0) winl = 0;
                if (wint < 0) wint = 0;
                window.open(
                'https://graph.facebook.com/oauth/authorize?scope=user_photos,offline_access,publish_stream&client_id=156852557659134&redirect_uri='+s.base_url+'vweb/facebook_return/&display=popup',
                'facebook_auth',
                'status = 0, resizable = 0, dependent = 1, height = 450, width = 500, top = ' + wint + ', left = ' + winl);
            },
            'authenticated': function(username, url) {
                $('#waiting-facebook').dialog("close");
                $('#link-facebook').addClass('linked').removeClass('not-linked')
                .find('.service-details a').text(username).attr('href', url.replace(':443', '')).end()
                .find('.service-settings').slideDown();
            },
            'show_settings': function() {
                $('#link-facebook .service-settings').slideToggle();
            },
            'save': function() {

                // The element containging settings chekcboxes etc
                var settings = $('#link-facebook'),

                // settings to pass to the api
                data = {
                    show_profile_link: settings.find('#showacc-fb').is(':checked'),
                    allow_newsfeed_posts: settings.find('#allowuse-fb').is(':checked'),
                    allow_gallery_posts: settings.find('#allowgal-fb').is(':checked'),
                    gallery_name: settings.find('#gallname-fb').val()
                };
                $.ajax({
                    url: s.api_url + '/linked_services/facebook/',
                    type: 'POST',
                    data: data,
                    success: function(data) {
                        if (data.success) {
                            s.linked_service.facebook.show_settings();
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'unlink': function() {
                $('#link-facebook').find('.service-bt-unlink a').text('Unlinking...');
                $.ajax({
                    url: s.api_url + '/linked_services/facebook/delete/',
                    type: 'POST',
                    success: function(data) {
                        if (data.success) {
                            $('#link-facebook').removeClass('linked').addClass('not-linked')
                            .find('.service-details a').text('').attr('href', '').end()
                            .find('.service-settings').hide().end()
                            .find('.service-bt-unlink a').text('Unlink');
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            }
        },
        'tumblr': {
            'unlink': function(username) {
                $('#link-tumblr').find('.service-bt-unlink a').text('Unlinking...');
                $.ajax({
                    url: s.api_url + '/linked_services/tumblr/delete/',
                    type: 'POST',
                    data: {
                        username: username
                    },
                    success: function(data) {
                        if (data.success) {
                            $('#link-tumblr').removeClass('linked').addClass('not-linked')
                            .find('.service-details a').text('').attr('href', '').end()
                            .find('.service-settings').hide().end()
                            .find('.service-bt-unlink a').text('Unlink');
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'show_settings': function() {
                $('#link-tumblr .service-settings#tumblr-settings').slideToggle();
            },
            'show_link_div': function() {
                $('#link-tumblr .service-settings#tumblr-link').slideToggle();
            },
            'link': function() {
                var data = {
                    username: $('#username-tmb').val(),
                    password: $('#pw-tmb').val()
                };
                $.ajax({
                    url: s.api_url + '/linked_services/tumblr/',
                    type: 'POST',
                    data: data,
                    success: function(data) {
                        if (data.success) {
                            s.linked_service.tumblr.authenticated(data.response.username, data.response.url)
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'authenticated': function(username, url) {
                $('#link-tumblr .service-settings#tumblr-link').hide();
                $('#link-tumblr').addClass('linked').removeClass('not-linked')
                .find('.service-details a').text(username).attr('href', url).end()
                .find('.service-settings#tumblr-settings').slideDown();
            },
            'save': function() {

                // The element containging settings chekcboxes etc
                var settings = $('#link-tumblr'),

                // settings to pass to the api
                data = {
                    show_name: settings.find('#showacc-tmb').is(':checked'),
                    allow_posts: settings.find('#allowuse-tmb').is(':checked')
                };
                $.ajax({
                    url: s.api_url + '/linked_services/tumblr/',
                    type: 'POST',
                    data: data,
                    success: function(data) {
                        if (data.success) {
                            s.linked_service.tumblr.show_settings();
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            }
        },
        'foursquare': {
            'unlink': function(username) {
                $('#link-foursquare').find('.service-bt-unlink a').text('Unlinking...');
                $.ajax({
                    url: s.api_url + '/linked_services/foursquare/delete/',
                    type: 'POST',
                    data: {
                        username: username
                    },
                    success: function(data) {
                        if (data.success) {
                            $('#link-foursquare').removeClass('linked').addClass('not-linked')
                            .find('.service-details a').text('').attr('href', '').end()
                            .find('.service-settings').hide().end()
                            .find('.service-bt-unlink a').text('Unlink');
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'show_settings': function() {
                $('#link-foursquare .service-settings').slideToggle();
            },
            'link': function() {

                $('<p id="waiting-foursquare">Waiting for Foursquare authentication.<br/><br/> If the Foursquare window does not open it may have been stopped by a popup blocker.<br/><br/><a href="' + s.foursquare_url + '" target="_blank">Click here to authenticate Foursquare in a new window</a>.</p>').dialog({
                    dialogClass: 'white',
                    show: 'scale',
                    width: 300,
                    resizable: false,
                    modal: true,
                    buttons: {
                        "Cancel": function() {
                            $(this).dialog("close");
                        }
                    }
                })
                var winl = (screen.width - 900) / 2;
                var wint = (screen.height - 650) / 2;
                if (winl < 0) winl = 0;
                if (wint < 0) wint = 0;
                window.open(
                s.foursquare_url,
                'foursquare_auth',
                'status = 0, resizable = 0, dependent = 1, height = 650, width = 900, top = ' + wint + ', left = ' + winl);
            },
            'authenticated': function(username, id) {
                $('#waiting-foursquare').dialog("close");
                $('#link-foursquare').addClass('linked').removeClass('not-linked')
                .find('.service-details a').text(username).attr('href', 'http://foursquare.com/user/-' + id).end()
                .find('.service-settings').slideDown();
            },
            'save': function() {

                // The element containging settings chekcboxes etc
                var settings = $('#link-foursquare'),

                // settings to pass to the api
                data = {
                    show_username: settings.find('#showacc-fsq').is(':checked'),
                    allow_checkin: settings.find('#allowuse-fsq').is(':checked')
                };
                $.ajax({
                    url: s.api_url + '/linked_services/foursquare/',
                    type: 'POST',
                    data: data,
                    success: function(data) {
                        if (data.success) {
                            s.linked_service.foursquare.show_settings();
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            }
        }
    };
    s.linked_service = {
        'init': function() {
            $('.foursquare-venue-li').live('click',
            function() {
                var list = $(this).closest('#sh-foursquare').find('.venues-select-panel');
                var venue_name = $(this).data('foursquare-venue-name');
                var venue_id = $(this).data('foursquare-venue-id');
                var selected_venue = $(this).closest('.tabbody').find('.foursquare-venue-name');
                selected_venue.empty().text(venue_name).data('foursquare-venue-id', venue_id);
                $(list).hide();
            });
            $('.add-to-foursquare-todo a').live('click',
            function() {
                var list = $(this).closest('#sh-foursquare').find('.venues-select-panel');
                var selected_venue = $(this).closest('.tabbody').find('.foursquare-venue-name');
                var venue_id = selected_venue.data('foursquare-venue-id', venue_id);
                s.linked_service.foursquare.todo_popup(venue_id);
            });
            $('.set-as-foursquare-venue a').live('click',
            function() {
                var photo_id = $(this).data('code');
                var selected_venue = $(this).closest('.tabbody').find('.foursquare-venue-name');
                var venue_id = selected_venue.data('foursquare-venue-id', venue_id);
                s.image.set_foursquare_venue(photo_id, venue_id,
                function(response) {
                    console.warn('set_foursquare_venue', response);
                });
            });

        },
        'twitter': {
            'unlink': function(username) {
                $('#twitter-' + username).find('.service-bt-unlink a').text('Unlinking...');
                $.ajax({
                    url: s.api_url + '/linked_services/twitter/delete/',
                    type: 'POST',
                    data: {
                        username: username
                    },
                    success: function(data) {
                        if (data.success) {
                            $('#link-twitter').removeClass('linked').addClass('not-linked')
                            .find('.service-details a').text('').attr('href', '');
                            $('#link-twitter .service-settings').hide();
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'import_profile': function(username) {
                $('#twitter-' + username).find('.service-bt-two a').text('Importing...');
                $.ajax({
                    url: s.api_url + '/linked_services/twitter/import_profile/',
                    type: 'POST',
                    data: {
                        username: username
                    },
                    success: function(data) {
                        if (data.success) {
                            s.bubble.load({
                                url: '/sc/my-account/',
                                success: function() {
                                    $('#modal .tabbedcontent').tabs('select', 3);
                                }
                            });
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'show_settings': function() {
                $('#link-twitter .service-settings').slideToggle();
            },
            'link': function() {
                s.auth.twitter_authenticate();
                s.auth.twitter_return = function(username) {
                    $('#waiting-twitter').dialog("close");
                    $.ajax({
                        url: s.api_url + '/linked_services/twitter/',
                        type: 'POST',
                        success: function(data) {
                            if (data.success) {
                                s.bubble.load({
                                    url: '/sc/my-account/',
                                    success: function() {
                                        $('#modal .tabbedcontent').tabs('select', 4);
                                    }
                                });
                            } else {
                                alert(data.error.message);
                            }
                        }
                    });
                };
            },
            'save': function() {

                // The element containging settings chekcboxes etc
                var settings = $('#link-twitter'),

                // settings to pass to the api
                data = {
                    show_name: settings.find('#showacc-twtr').is(':checked'),
                    allow_tweets: settings.find('#allowuse-twtr').is(':checked')
                };
                $.ajax({
                    url: s.api_url + '/linked_services/twitter/',
                    type: 'POST',
                    data: data,
                    success: function(data) {
                        if (data.success) {
                            s.linked_service.twitter.show_settings();
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            }
        },
        'facebook': {
            'link': function() {
                $('<p id="waiting-facebook">Waiting for Facebook authentication.<br/><br/> If the Facebook window does not open it may have been stopped by a popup blocker.<br/><br/><a href="https://graph.facebook.com/oauth/authorize?scope=user_photos,offline_access,publish_stream&client_id=156852557659134&redirect_uri='+s.base_url+'vweb/facebook_return/" target="_blank">Click here to authenticate Facebook in a new tab</a>.</p>').dialog({
                    dialogClass: 'white',
                    show: 'scale',
                    width: 300,
                    resizable: false,
                    modal: true,
                    buttons: {
                        "Cancel": function() {
                            $(this).dialog("close");
                        }
                    }
                })
                var winl = (screen.width - 500) / 2;
                var wint = (screen.height - 450) / 2;
                if (winl < 0) winl = 0;
                if (wint < 0) wint = 0;
                window.open(
                'https://graph.facebook.com/oauth/authorize?scope=user_photos,offline_access,publish_stream&client_id=156852557659134&redirect_uri='+s.base_url+'vweb/facebook_return/&display=popup',
                'facebook_auth',
                'status = 0, resizable = 0, dependent = 1, height = 450, width = 500, top = ' + wint + ', left = ' + winl);
            },
            'authenticated': function(username, url) {
                $('#waiting-facebook').dialog("close");
                $('#link-facebook').addClass('linked').removeClass('not-linked')
                .find('.service-details a').text(username).attr('href', url.replace(':443', '')).end()
                .find('.service-settings').slideDown();
            },
            'show_settings': function() {
                $('#link-facebook .service-settings').slideToggle();
            },
            'save': function() {

                // The element containging settings chekcboxes etc
                var settings = $('#link-facebook'),

                // settings to pass to the api
                data = {
                    show_profile_link: settings.find('#showacc-fb').is(':checked'),
                    allow_newsfeed_posts: settings.find('#allowuse-fb').is(':checked'),
                    allow_gallery_posts: settings.find('#allowgal-fb').is(':checked'),
                    gallery_name: settings.find('#gallname-fb').val()
                };
                $.ajax({
                    url: s.api_url + '/linked_services/facebook/',
                    type: 'POST',
                    data: data,
                    success: function(data) {
                        if (data.success) {
                            s.linked_service.facebook.show_settings();
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'unlink': function() {
                $('#link-facebook').find('.service-bt-unlink a').text('Unlinking...');
                $.ajax({
                    url: s.api_url + '/linked_services/facebook/delete/',
                    type: 'POST',
                    success: function(data) {
                        if (data.success) {
                            $('#link-facebook').removeClass('linked').addClass('not-linked')
                            .find('.service-details a').text('').attr('href', '').end()
                            .find('.service-settings').hide().end()
                            .find('.service-bt-unlink a').text('Unlink');
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            }
        },
        'tumblr': {
            'unlink': function(username) {
                $('#link-tumblr').find('.service-bt-unlink a').text('Unlinking...');
                $.ajax({
                    url: s.api_url + '/linked_services/tumblr/delete/',
                    type: 'POST',
                    data: {
                        username: username
                    },
                    success: function(data) {
                        if (data.success) {
                            $('#link-tumblr').removeClass('linked').addClass('not-linked')
                            .find('.service-details a').text('').attr('href', '').end()
                            .find('.service-settings').hide().end()
                            .find('.service-bt-unlink a').text('Unlink');
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'show_settings': function() {
                $('#link-tumblr .service-settings#tumblr-settings').slideToggle();
            },
            'show_link_div': function() {
                $('#link-tumblr .service-settings#tumblr-link').slideToggle();
            },
            'link': function() {
                var data = {
                    username: $('#username-tmb').val(),
                    password: $('#pw-tmb').val()
                };
                $.ajax({
                    url: s.api_url + '/linked_services/tumblr/',
                    type: 'POST',
                    data: data,
                    success: function(data) {
                        if (data.success) {
                            s.linked_service.tumblr.authenticated(data.response.username, data.response.url)
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'authenticated': function(username, url) {
                $('#link-tumblr .service-settings#tumblr-link').hide();
                $('#link-tumblr').addClass('linked').removeClass('not-linked')
                .find('.service-details a').text(username).attr('href', url).end()
                .find('.service-settings#tumblr-settings').slideDown();
            },
            'save': function() {

                // The element containging settings chekcboxes etc
                var settings = $('#link-tumblr'),

                // settings to pass to the api
                data = {
                    show_name: settings.find('#showacc-tmb').is(':checked'),
                    allow_posts: settings.find('#allowuse-tmb').is(':checked')
                };
                $.ajax({
                    url: s.api_url + '/linked_services/tumblr/',
                    type: 'POST',
                    data: data,
                    success: function(data) {
                        if (data.success) {
                            s.linked_service.tumblr.show_settings();
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            }
        },
        'foursquare': {
            'unlink': function(username) {
                $('#link-foursquare').find('.service-bt-unlink a').text('Unlinking...');
                $.ajax({
                    url: s.api_url + '/linked_services/foursquare/delete/',
                    type: 'POST',
                    data: {
                        username: username
                    },
                    success: function(data) {
                        if (data.success) {
                            $('#link-foursquare').removeClass('linked').addClass('not-linked')
                            .find('.service-details a').text('').attr('href', '').end()
                            .find('.service-settings').hide().end()
                            .find('.service-bt-unlink a').text('Unlink');
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'show_settings': function() {
                $('#link-foursquare .service-settings').slideToggle();
            },
            'link': function() {
                $.ajax({
                    url: '/vweb/foursquare_auth_url/',
                    success: function(data) {
                        if (data.success) {
                            $('<p id="waiting-foursquare">Waiting for Foursquare authentication.<br/><br/> If the Foursquare window does not open it may have been stopped by a popup blocker.<br/><br/><a href="' + data.response.url + '" target="_blank">Click here to authenticate Foursquare in a new window</a>.</p>').dialog({
                                dialogClass: 'white',
                                show: 'scale',
                                width: 300,
                                resizable: false,
                                modal: true,
                                buttons: {
                                    "Cancel": function() {
                                        $(this).dialog("close");
                                    }
                                }
                            })
                            var winl = (screen.width - 900) / 2;
                            var wint = (screen.height - 650) / 2;
                            if (winl < 0) winl = 0;
                            if (wint < 0) wint = 0;
                            window.open(
                            data.response.url,
                            'twitter_auth',
                            'status = 0, resizable = 0, dependent = 1, height = 650, width = 900, top = ' + wint + ', left = ' + winl);
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'authenticated': function(username, id) {
                $('#waiting-foursquare').dialog("close");
                $('#link-foursquare').addClass('linked').removeClass('not-linked')
                .find('.service-details a').text(username).attr('href', 'http://foursquare.com/user/-' + id).end()
                .find('.service-settings').slideDown();
            },
            'save': function() {

                // The element containging settings chekcboxes etc
                var settings = $('#link-foursquare'),

                // settings to pass to the api
                data = {
                    show_username: settings.find('#showacc-fsq').is(':checked'),
                    allow_checkin: settings.find('#allowuse-fsq').is(':checked')
                };
                $.ajax({
                    url: s.api_url + '/linked_services/foursquare/',
                    type: 'POST',
                    data: data,
                    success: function(data) {
                        if (data.success) {
                            s.linked_service.foursquare.show_settings();
                        } else {
                            alert(data.error.message);
                        }
                    }
                });
            },
            'get_nearby_venues': function(ll, callback, fail) {
                if (ll) {
                    $.ajax({
                        'url': '/api/linked_services/foursquare/venues/',
                        'data': {
                            'll': ll,
                            'intent': 'checkin'
                        },
                        'dataType': 'json',
                        'success': function(result) {
                            // console.warn(result);
                            if (result.success && result.response.foursquare_response.response.groups) {
                                callback(result.response.foursquare_response.response.groups[0].items)
                            } else {
                                if (fail) {
                                    fail();
                                }
                            }
                        }
                    });
                }
            },
            'todo_popup': function(venue_id) {
                window.open('http://foursquare.com/remote_todo?vid=' + venue_id + '&color=light',
                '_blank',
                'toolbar=0,status=0,height=380,width=650,scrollbars=no,resizable=no');
            },
            'load_social_tab': function(tab) {
                var $this = $(tab);
                var sss = $this.find('.snapr-special-select');
                var ll = sss.data('ll');
                var nearby_venues = $this.find('.nearby-venues ul');
                nearby_venues.empty();
                nearby_venues.jScrollPane();

                sss.addClass('loading');
                if(!sss.find('.foursquare-venue-name').text()){
                    sss.find('.foursquare-venue-name').text('Getting Venues...');
                }else{
                    if($this.find('.owner-set').length < 1){
                        return false;
                    }
                }
                var nearby_venues_api = nearby_venues.data('jsp');
                var success = function(response) {
                    $(response).each(function(i, v) {
                        if (i == 0) {
                            sss.find('.foursquare-venue-name').empty().text(v.name).data('foursquare-venue-id', v.id);
                            sss.removeClass('loading');
                        }
                        if(v.categories.length){
                            var icon_src = v.categories[0].icon;
                        }else{
                            var icon_src = 'http://foursquare.com/img/categories/none.png';
                        }
                        var icon = '<img src="' + icon_src + '" alt="icon" class="foursquare-icon"/>';
                        var li = '<li class="foursquare-venue-li clickable" data-foursquare-venue-name="' + this.name + '" data-foursquare-venue-id="' + this.id + '">' + icon + '<span class="fs-venuename">' + this.name + '</span></li>';
                        nearby_venues_api.getContentPane().append(li);
                    });
                    todo_button.show();
                    set_button.show();
                }
                var fail = function(response) {
                    sss.find('.foursquare-venue-name').empty().text('Hmm.. No joy.');
                    sss.removeClass('loading');
                }
                s.linked_service.foursquare.get_nearby_venues(ll, success, fail);
            }
        }
    };
    s.bubble = {
        // Modals that are NOT THE IMAGE BUBBLE.
        'path': '',
        'elements': {
            'modal': new cached_jQ('#modal'),
            'loader': $('<img src="//static.sna.pr/live/gfx/loading.gif" style="margin-top: 130px; margin-bottom: 150px; margin-left: 284px; margin-right: 0;"/>')
        },
        'content': null,
        // content area dom element
        'show': function() {
            if (s.bubble.path) {
                load_bubble = function(){
                    s.bubble.load({
                        url: '/sc/' + s.bubble.path + '/'
                    });
                    load_bubble = null;
                }
                if(s.view.view == 'dash'){
                    if($('#dashboard-sidemenu').length){
                        load_bubble();
                    }else{
                        s.dashboard.show(load_bubble);
                    }
                }else{
                    load_bubble();
                }
            }
        },
        'load': function(o) {
            if (s.view.view === 'map') {
                s.bubble.elements.modal.get()
                .empty().append(s.bubble.elements.loader.get())
                .dialog({
                    dialogClass: 'white',
                    title: o.title,
                    show: 'scale',
                    width: 640,
                    close: function(e, ui) {
                        s.view.go({
                            'bubble': null,
                            'bubble_tab': null
                        });
                    },
                    resizable: false
                })
                .load(o.url,
                function() {
                    $(this).find(".tabbedcontent").tabs({
                        show :function(event,ui) {
                            $('.tabbody.scroll-me:visible').jScrollPane({forceNoScrollH:true});
                        }
                    });
                    if (o.success)
                    o.success();
                });
            } else {
                var dashbubble = function() {
                    s.dashboard.elements.bubble_container.get()
                    .insertBefore($('#dashboard-sidemenu'))
                    .empty()
                    .addClass('selected')
                    .show()
                    .append(s.bubble.elements.loader.get())
                    .load(o.url,
                    function() {
                        $(this).find(".tabbedcontent").tabs({
                            show :function(event,ui) {
                                $('.tabbody.scroll-me:visible').jScrollPane({forceNoScrollH:true});
                            }
                        });
                        if (o.success)
                        o.success();
                    });
                    s.dashboard.select($('#dashboard-bubble'), false);
                }
                if ($('#dashboard-bubble').length) {
                    dashbubble();
                } else {
                    //only gets fired on initial page load when going direct to a /dash/bubble hash address
                    // console.warn('s.dashboard.show(dashbubble);');
                    s.dashboard.show(dashbubble);
                }
            }
        },
        'hide': function() {
            if ($('#dashboard-bubble.selected')) {
                s.dashboard.select($('#dashboard-bubble.selected').next(), false);
            } else {
                s.bubble.elements.modal.get()
                .dialog('option', 'close',
                function() {})
                // don't want to fire close event function
                .dialog('close');
                // s.bubble.remove_from_url();
            }
        }
        // 'remove_from_url': function(){
        //     if(s.view.view === 'dash'){
        //         window.location.hash = '#/dash/'
        //     }else{
        //         window.location.hash = window.location.hash.replace(s.bubble.path, '').replace('//','');
        //     }
        // },
    };
    s.init = function() {
        if (loaded) return false;
        loaded = true;

        s.dashboard.init();
        s.gallery.init();
        s.video.init();
        s.cities.init();
        s.comments.init();
        s.groups.init();
        s.user.init();
        s.search.init();
        s.image.init();
        s.auth.init();
        s.map.init();
        s.news.init();
        // s.people.init();
        s.linked_service.init();
        // live: favorite buttons
        // <x class="x-favorite-button x-photo-CODE (loading)" data-photo="CODE" data-favorite="true" data-count="3">favorite</x>
        // <x class="x-favorite-count x-photo-CODE">3</x>
        // <x class="x-favorite-status x-photo-CODE (favorite)">favorited</x>
        $('.x-favorite-button').live('click',
        function() {
            var $this = $(this),
            photo = $this.data('photo'),
            count = $this.data('count'),
            favorite = $this.data('favorite'),
            reactions = $this.closest('#imageextracon').find('.x-comment-list');

            $this.addClass('loading');
            $.ajax({
                url: s.api_url + '/favorite/' + (favorite ? 'remove/': ''),
                type: 'POST',
                dataType: 'json',
                data: {
                    id: photo
                },
                success: function(response) {
                    if (response.success) {
                        count += favorite ? -1: 1;
                        $('.x-favorite-button.x-photo-' + photo).data('favorite', !favorite).data('count', count);
                        $this.data('favorite', !favorite).data('count', count).toggleClass('favorite', !favorite);
                        $('.x-favorite-status.x-photo-' + photo).toggleClass('favorite', !favorite);
                        if(reactions.length){
                            if(favorite){
                                reactions.find('.fav-mine').remove();
                            }else{
                                reactions.prepend('<li class="fav-mine">' +
                                    '<a class="clickable x-user" data-username="' + snapr.auth.username.toLowerCase() + '">' +
                                    snapr.auth.username + '</a> <p class="like-text">likes this</p>' +
                                    '</li>');
                            }
                        }

                        if(count){
                            if($this.is('.fc-mini')){
                                var times = '';
                            }else{
                                var times = '&times; ';
                            }
                            $('.x-favorite-count.x-photo-' + photo).html(times + count).show();
                        }else{
                            $('.x-favorite-count.x-photo-' + photo).html('').show();
                        }
                    } else {
                        if (response.error.code == 16) {
                            s.auth.redirect.destination.message = response.error.message;
                            s.auth.redirect.destination.url = window.location.hash;
                            window.location.hash = '#/login';
                        }
                        //dpwolf-todo
                    }
                    $this.removeClass('loading');
                }
            });
        })

        // live: flag buttons
        // <x class="x-flag-button x-photo-CODE (loading)" data-photo="CODE" data-flagged="true">flag</x>
        // <x class="x-flag-status x-photo-CODE (flagged)">flagged</x>
        $('.x-flag-button').live('click',
        function() {
            var $this = $(this),
            photo = $this.data('photo'),
            flagged = $this.data('flagged');
            // can't unflag
            if (flagged) {
                return;
            }

            $this.addClass('loading');
            $.ajax({
                url: s.api_url + '/report/',
                type: 'POST',
                dataType: 'json',
                data: {
                    id: photo
                },
                success: function(response) {
                    if (response.success) {
                        $('.x-flag-button.x-photo-' + photo).data('flagged', !flagged);
                        $('.x-flag-status.x-photo-' + photo).toggleClass('flagged', !flagged);
                    } else {
                        alert(response.error.message);
                    }
                    $this.removeClass('loading');
                }
            });
        })
        // live: favorite buttons
        // <x class="x-privacy-button x-photo-CODE (loading)" data-photo="CODE" data-private="true">Set Privacy</x>
        // <x class="x-privacy-status x-photo-CODE (private)">Private</x>
        $('.x-privacy-button').live('click',
        function() {
            var $this = $(this),
            photo = $this.data('photo'),
            is_private = $this.data('private');

            $this.addClass('loading');
            $.ajax({
                url: s.api_url + '/photo/change_status/',
                type: 'POST',
                data: {
                    id: photo,
                    status: is_private && 'public' || 'private'
                },
                success: function(response) {
                    if (response.success) {
                        $('.x-privacy-button.x-photo-' + photo)
                        .data('private', !is_private)
                        .toggleClass('private', !is_private);
                    } else {
                        alert(response.error.message);
                    }
                    $this.removeClass('loading');
                }
            });
        });
        $('.x-delete-button').live('click',
        function() {
            var id = $(this).data('photo');
            if (confirm('Are you sure you want to delete this image? You may prefer to set it to private so that it can only be seen by you.')) {
                $.ajax({
                    url: s.api_url + '/delete/',
                    type: 'POST',
                    data: {
                        id: id
                    },
                    success: function(r) {
                        if (s.view.subview == 'gallery') {
                            var next = s.gallery.next();
                        } else {
                            var next = s.image.next();
                        }

                        if (!next) {
                            var params = s.view.default_params();
                            params.view = s.view.view;
                            s.view.go(params);
                        }
                        if (s.view.view == 'map') {
                            s.map.get_images();
                        }
                        $('.photo-' + id).remove();
                        s.gallery.codes.splice([s.gallery.codes.indexOf(id)], 1);
                        //s.cache.remove(id);
                    }
                });
            }

        })

        $('#commentmessage').live('click',
        function() {
            s.auth.redirect.destination.url = window.location.hash;
            s.auth.redirect.destination.message = 'Please log in to post a comment.';
            window.location.hash = '#/login';
        })

        $('.hash-search').live('click',
        function() {
            var keywords = $(this).data('keywords');
            s.search.tags(keywords);
        })

        // Fragment (hash) change
        $(window)
        .hashchange(s.view.change)
        // bind
        .trigger('hashchange', [true])
        // trigger to go to initial URL
        .resize(function() {
            switch (s.view.subview) {
            case 'thumbs':
                s.thumbs.resize();
                break;
            case 'photo':
                s.image.resize();
            }
            switch (s.view.view) {
            case 'dash':
                s.dashboard.resize();
            }
        });


        //twttr.anywhere(function (T) {
        //  T.hovercards();
        //});

        // Bottom Tabs
        $('#tab-my-snaps').click(function() {
            s.user.show(s.auth.username, {
                no_results: function() {
                    s.bubble.load({
                        url: '/sc/no-snaps/'
                    });
                }
            });
        });
        $('#advanced-search-button').click(function() {
            s.bubble.load({
                url: '/sc/search/'
            });
        });
        $('#forgot_password').click(function() {
            s.bubble.load({
                url: '/sc/forgot-password/'
            });
        });

        //clicking location in bubble goes there
        // $('#imagelocation').click(function() {
        //     var point = $(this).data('point');
        //     s.map.pan_point_to_arrow(new google.maps.LatLng(point.latitude, point.longitude));
        // });

        // Show the large image and hide the loader when it's loaded
        $('#photo-bubble #imagedisplay img').load(function() {
            s.image.dom.loader.hide();
            $(this).show();
        });

        // Make the loader show and hide with any ajax requests
        $(document).ajaxStart(function() {
            $('#snaprtopbarleft .loadinggfx').fadeIn(200);
        }).ajaxStop(function() {
            $('#snaprtopbarleft .loadinggfx').fadeOut(200);
        });

        // Search
        $('#topbarsearchtype').click(function(e) {
            $(this).blur();
            $('#topbarsearchtypelist').slideDown(100);
        });
        var search_type_timer;
        $('#topbarsearchtypelist')
        .mouseleave(function() {
            search_type_timer = setTimeout(function() {
                $('#topbarsearchtypelist').slideUp(100);
            },
            2000);
        }).mouseenter(function() {
            clearTimeout(search_type_timer)
        });
        $('#topbarsearchtypelist a').click(function(e) {
            $('#topbarsearchtype').val($(this).text());
            $('#topbarsearchtypelist').slideUp(100);
        });

        $('.map-settings-bt a').click(function() {
            if ($('#snaprtabs .tabsicons').hasClass('open')) {
                $('#snaprtabs .tabsicons').removeClass('open');
                $('#settings-menu').hide();
            } else {
                $('#snaprtabs .tabsicons').addClass('open');
                $('#settings-menu').show();
            }
        })

        $('.tabsicons .clickable').mouseenter(function() {
            $('#snaprtabs .tabsicons').removeClass('open');
            $('#settings-menu, #favourites-menu').hide();
        });

        $('#snaprtabs ul li.tabsicons a .icon').mouseenter(function(){
           $(this).parent().addClass('open-tip');
        });
        $('#snaprtabs ul li.tabsicons a .icon').mouseleave(function(){
           $(this).parent().removeClass('open-tip');
        });

    };

    s.news = {
        'init': function() {
            s.news.response = {};
            $('.icon-news').live('click',function(){
                if($('#news-feed').is(':visible')){
                    $('#news-feed').hide();
                }else{
                    $(this).removeClass('open-tip');
                    $('#news-feed ul').empty().append('<li class="loading">Loading News...</li>');
                    $('#news-feed').show();
                    s.news.get_news();
                }
            });
            $('.x-thumb', $('#news-feed')).live('click',
            function() {
                var $this = $(this),
                photo_id = $this.data('photo');
                s.view.go({
                    'code': photo_id,
                    'subview': 'photo',
                    'bubble': null,
                    'username': null,
                    'list': 'area',
                    'search': null
                });
            });
        },
        'get_news': function(date) {
            var news_data = {
                n: 10
            };
            if (date) {
                news_data.max_date = date;
            }
            $.ajax({
                url: '/api/user/news/',
                data: news_data,
                success: s.news.display_news,
                error: function(e) {
                    console.warn('error', e)
                }
            })
        },
        'display_news': function(news) {
            if (news.success) {
                console.warn('news',news.response.news);
                nn = news
                $('#news-feed ul').empty();
                if(news.response.news.length > 0){
                    $("#news-item-tmpl")
                    .tmpl(news.response.news)
                    .appendTo('#news-feed ul');
                    $('#news-feed ul').jScrollPane({forceNoScrollH:true});
                }else{
                    $('#news-feed ul').append('<li>No news, check back later.</li>');
                }
            }
        }
    };

    s.video = {
        'init': function(callback) {
            $('.video-close-view-bt').live('click',
            function() {
                sublimevideo.stop && sublimevideo.stop();
                sublimevideo.stop && sublimevideo.unprepare();
                $('video source').each(function() {
                    $(this).remove();
                })
                $('#sublime-video').remove();
                if (s.view.view == 'dash') {
                    if (window.history.length > 2) {
                        window.history.back();
                    } else {
                        var params = s.view.default_params();
                        params.view = 'dash';
                        s.view.go(params);
                    }
                } else {
                    if (window.history.length > 2) {
                        window.history.back();
                    } else {
                        s.view.go({
                            'subview': 'photo'
                        });
                    }
                }
            })
        },
        'elements': {
            'overlay': new cached_jQ('#video-layer'),
            'videocontainer': new cached_jQ('#snaprvideo'),
            'video': new cached_jQ('#video'),
            'time': new cached_jQ('#vid-time'),
            'shade': new cached_jQ('#video-shade'),
            'topbar': new cached_jQ('#video-topbar')
        },
        'hide_details': function(e) {
            s.video.elements.topbar.get().fadeOut();
            s.video.elements.shade.get().fadeIn(function() {
                s.video.elements.overlay.get().addClass('hide-details');
            });
        },
        'show_details': function() {
            s.video.elements.topbar.get().fadeIn();
            s.video.elements.shade.get().fadeOut(function() {
                s.video.elements.overlay.get().removeClass('hide-details');
            });
        },
        'activate': function() {
            // console.warn('video activate');
            // kind of a hack but it seems to work
            if(jilion.sublime.video.init){
                jilion.sublime.video.init();
            }else{
                sublimevideo.load();
            }

            sublimevideo.ready && sublimevideo.ready(function(){
                // console.warn('sublimevideo.ready');
                sublimevideo.prepareAndPlay("sublime-video");
            });

            s.video.elements.time.get().prettyDate();
            s.video.elements.shade.get().mouseout(function(e) {
                if ($(e.relatedTarget).is('.hover')) {
                    s.video.show_details();
                }
            });
            s.video.elements.video.get().load(function() {
                s.video.elements.video.get().show();
            });
            s.comments.linkHashtags(s.video.elements.video.get().find('#video-imagetag'));

            // s.comments.linkHashtags(s.video.elements.videocontainer.get().find('#video-imagetag'));
            //  // if images is loaded, trigger load event, the browser can't be trusted to do this if it's cached
            //  if( s.video.elements.video.get()[0].complete){
            //       s.video.elements.video.get().load();
            //  }
        },
        'show': function(photo_id) {
            // console.warn('show video ' + photo_id);
            s.video.elements.videocontainer.get().find('.snaprbubbleimage').hide();
            s.video.elements.overlay.get().show();
            $window = $(window);
            s.video.elements.videocontainer.get().load(
            // 184 used to be be subtracted
            '/ajax/video/?width=' + ($window.width()) + '&height=' + ($window.height()) + '&photo_id=' + photo_id,
            {},
            function(response) {
                s.video.activate();
            }
            );

            // remove thumbs view if it was showing
            s.video.elements.overlay.get().removeClass('gallery-thumbs-view');
        },
        'hide': function() {
            s.video.elements.overlay.get().fadeOut();
            // console.warn('hide video');
        }
    }

    // IE is the worst
    if ($.browser.msie && !loaded) {
        window.onload = s.init;
    } else {
        $(s.init);
    }

    //
    // Define custom google map overlay for snapr thumbs
    //
    function map_thumb(photo, map) {
        this.photo_ = photo;
        this.map_ = map;
        this.div_ = null;
        this.setMap(map);
    }
    map_thumb.prototype = new google.maps.OverlayView();
    map_thumb.prototype.onAdd = function() {
        var div = $('#snaprthumbtemplate').clone(),
        photo_id = this.photo_.id,
        username = this.photo_.username,
        location = this.photo_.location,
        aspect = this.photo_.width / this.photo_.height;
        date = this.photo_.date;
        // console.warn('th',this.photo_)
        div.show();
        div.find('a').click(function() {
            var params = {
                'code': photo_id,
                'subview': 'photo',
                'bubble': null,
                'username': null,
                'list': 'area',
                'search': null,
                'map': {'date':date}
            }
            if (snapr.user.username !== snapr.auth.username) {
                params.username = null;
            }
            if(s.map.zoom < 5){
                s.map.map.setZoom(5);
                s.map.zoom = 5;
            }


            $('#photo-bubble').show();
            $('#ajax-img-overlay').show();
            $('#imagedisplay').css('width', aspect * 384 + 'px');
            s.image.arrow.hide();

            //console.log('clicked map thumb, panning to arrow');
            //s.map.time.set(date);
            //s.map.pan_point_to_arrow(new google.maps.LatLng(location.latitude,location.longitude));
            s.view.go(params);
        });
        div.find('.snaprmapthumbimage').attr('src', 'http://media-server2.snapr.us/thm/' + this.photo_.secret + '/' + this.photo_.id + '.jpg');

        // Set the overlay's div_ property to this DIV (not jQuery object)
        this.div_ = div[0];

        var panes = this.getPanes();
        panes.floatPane.appendChild(this.div_);
    }
    map_thumb.prototype.draw = function() {
        var overlayProjection = this.getProjection();
        var position = new google.maps.LatLng(this.photo_.location.latitude, this.photo_.location.longitude);
        var px = overlayProjection.fromLatLngToDivPixel(position);

        this.div_.style.left = px.x + 'px';
        this.div_.style.top = px.y + 'px';
    }
    map_thumb.prototype.onRemove = function() {
        this.div_.parentNode.removeChild(this.div_);
        this.div_ = null;
    }
    map_thumb.prototype.hide = function() {
        this.div_ && (this.div_.style.visibility = "hidden");
    }
    map_thumb.prototype.show = function() {
        this.div_ && (this.div_.style.visibility = "visible");
    }
    map_thumb.prototype.toggle = function() {
        if (this.div_) {
            (this.div_.style.visibility == "hidden" && this.show || this.hide)();
        }
    }
    map_thumb.prototype.toggleDOM = function() {
        this.setMap(this.getMap() && null || this.map_);
    }


    //
    // jQ extentions
    //
    jQuery.validator.addMethod("alphanum_",
    function(value, element) {
        return this.optional(element) || /^[0-9a-z_]+$/i.test(value);
    },
    "Letters, numbers and _ only please");

    jQuery.validator.addMethod("snapr_username",
    function(value, element, param) {
        if (this.optional(element))
            return "dependency-mismatch";

        var previous = this.previousValue(element);
        if (!this.settings.messages[element.name])
            this.settings.messages[element.name] = {};
        previous.originalMessage = this.settings.messages[element.name].snapr_username;
        this.settings.messages[element.name].snapr_username = previous.message;

        param = typeof param == "string" && {
            url: param
        } || param;

        if (previous.old !== value) {
            previous.old = value;
            var validator = this;
            this.startRequest(element);
            var data = {};
            data[element.name] = value;
            $.ajax($.extend(true, {
                url: s.api_url + '/user/validate/',
                mode: "abort",
                port: "validate" + element.name,
                dataType: "json",
                data: data,
                success: function(response) {
                    validator.settings.messages[element.name].snapr_username = previous.originalMessage;
                    var valid = response.success;
                    if (valid) {
                        var submitted = validator.formSubmitted;
                        validator.prepareElement(element);
                        validator.formSubmitted = submitted;
                        validator.successList.push(element);
                        validator.showErrors();
                    } else {
                        var errors = {};
                        errors[element.name] = response.error.message;
                        validator.showErrors(errors);
                    }
                    previous.valid = valid;
                    validator.stopRequest(element, valid);
                }
            },
            param));
            return "pending";
        } else if (this.pending[element.name]) {
            return "pending";
        }
        return previous.valid;
    });


    jQuery.fn.smartFocus = function(text) {
        $(this).val(text).focus(function() {
            if ($(this).val() == text) {
                $(this).val('').addClass('focus');
            }
        }).blur(function() {
            if ($(this).val() == '') {
                $(this).val(text).removeClass('focus');
            }
        });
        return this;
    };

    jQuery.fn.scrollBottom = function() {
        var that = jQuery(this);
        return that.children().height() - that.scrollTop() - that.height();
    };

    jQuery.fn.prettyDate = function() {
        return this.each(function() {
            var $this = $(this);
            $this.text(prettyDate($this.data('date')));
        });
    };

    $.fn.spin = function(opts) {
      this.each(function() {
        var $this = $(this),
            spinner = $this.data('spinner');

        if (spinner) spinner.stop();
        if (opts !== false) {
          opts = $.extend({color: $this.css('color')}, opts);
          spinner = new Spinner(opts).spin(this);
          $this.data('spinner', spinner);
        }
      });
      return this;
    };

    //
    // functions
    //
    /**
 * Format date into human readible format relative to now
 *
 * within last hour = "X minutes ago"
 * today more than an hour ago= "about X hours ago"
 * prior to today: 9:27 PM Apr 29th
 * prior to this year: 9:27 PM Apr 29th, 2009
 * @argument time {string}
 * @returns {string} formatted date.
 */
    function prettyDate(time, relative){
        time = (time || "").replace(/-/g,"/").replace(/ \//g," -").replace(/[TZ]/g," ");
        //add 0000 to set to utc for relative times
        if(relative !== false && time.split(' ').length <3){
            time = time + ' -0000';
        }
        var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
        date = new Date(time),now = new Date(),
        diff = (((now).getTime() - date.getTime()) / 1000),
        day_diff = Math.floor(diff / 86400);
        date = new Date(time.replace(/ [\+-]\d{4}$/,'')); //strip TZ
        if(date.getHours() <= 12){
            var hours = date.getHours(),
            ap = 'AM'
        }else{
            var hours = date.getHours() -12,
            ap = 'PM'
        }
        if(relative !== false){
            if ( isNaN(day_diff) || day_diff < 0 )//|| day_diff >= 31 )
                return;
            if (day_diff == 0){
                return(
                    diff < 60 && 'just now' ||
                    diff < 120 && 'a minute ago' ||
                    diff < 3600 && Math.floor( diff / 60 ) + " mins ago" ||
                    diff < 86400 && Math.floor( diff / 3600 ) + " hours ago"
                );
            }
            if (day_diff == 1){
                return 'Yesterday';
            }
            if (day_diff < 7){
                return day_diff + 'days ago';
            }
            if(date.getYear() == now.getYear()){
                return (date.getDate()).ordinal() + ' ' + months[date.getMonth()];
            }else{
                var yr = String(date.getFullYear());
                yr = yr.substring(yr.length - 2,yr.length);
                return (date.getDate()).ordinal() + ' ' + months[date.getMonth()] + ' \'' + yr;
            }
        }
        var full_date = hours+' '+ap+', '+months[date.getMonth()]+' '+date.getDate();
        if (date.getFullYear() == new Date().getFullYear())
            return full_date
        return full_date+', '+date.getFullYear()
    }

    s.prettyDate = prettyDate;

    // converts human readable date string to a string suitable for the python calls
    // function pyDate(date){
    //     alert('pyDate');
    //     var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
    //     var t = Date.parse(date.replace('a.m.','am').replace('p.m.','pm'));
    //     var d = new Date(t);
    //     var s = d.getFullYear() + '-' + months[d.getMonth()] + '-' + d.getDay() + ' ' + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds();
    //     return s;
    //     // fmt=%Y-%m-%d %H:%M:%S
    //
    // }
    Date.prototype.relative = function() {
        var date = this,
        diff = (((new Date()).getTime() - date.getTime()) / 1000),
        day_diff = Math.floor(diff / 86400);
        if (isNaN(day_diff) || day_diff < 0)
        //|| day_diff >= 31 )
        return;
        if (day_diff == 0) {
            return (
            diff < 60 && 'just now' ||
            diff < 120 && 'a minute ago' ||
            diff < 3600 && Math.floor(diff / 60) + " minutes ago" ||
            diff < 7200 && "about 1 hour ago" ||
            diff < 86400 && "about " + Math.floor(diff / 3600) + " hours ago"
            );
        }
        date = new Date(time.replace(/ [\+-]\d{4}$/, ''));
        //strip TZ
        if (date.getHours() <= 12) {
            var hours = date.getHours(),
            ap = 'AM'
        } else {
            var hours = date.getHours() - 12,
            ap = 'PM'

        }

        //if the image was taken yesterday it would show: 8:25 PM Yesterday
        if (day_diff == 1) {
            return hours + ':' + date.getMinutes().zeroFill(2) + ' ' + ap + ' Yesterday';
        }
        //If the image was taken within the last 30 days it would say: 8:25 PM X Days Ago
        if (day_diff < 30) {
            return hours + ':' + date.getMinutes().zeroFill(2) + ' ' + ap + ' ' + day_diff + ' Days Ago';
        }

        var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        full_date = hours + ':' + date.getMinutes().zeroFill(2) + ' ' + ap + ' ' + months[date.getMonth()] + ' ' + date.getDate().ordinal();
        if (date.getFullYear() == new Date().getFullYear())
        return full_date
        return full_date + ', ' + date.getFullYear()
    }
    /**
 * fill number out to width with 0s
 * @argument width {number}
 * @returns {string}
 */
    Number.prototype.zeroFill = function(width) {
        width -= this.toString().length;
        if (width > 0)
        {
            return new Array(width + (/\./.test(this) ? 2: 1)).join('0') + this;
        }
        return this.toString();
    }
    Number.prototype.ordinal = function() {
        if (this != 11 && this != 12 && this != 13) {
            that = String(this)
            switch (that.substr(that.length - 1)) {
            case '1':
                return that + 'st';
            case '2':
                return that + 'nd';
            case '3':
                return that + 'rd';
            }
        }
        return this + 'th';
    }
    String.prototype.trim = function(len, toChar) {
        if (this.length <= len) {
            return this.toString();
        }
        var st = this.substr(0, len);
        if (toChar) {
            st = st.substring(0, s.lastIndexOf(toChar));
        }
        return st + '...'
    }
    Date.prototype.toSnaprString = function() {
        return this.getFullYear() + '-' + ((this.getMonth() + 1).zeroFill(2)) + '-' + this.getDate().zeroFill(2) + ' ' + this.getHours().zeroFill(2) + ':' + this.getMinutes().zeroFill(2) + ':' + this.getSeconds().zeroFill(2);
    };
    // expose
    window.snapr = s;
})(window);

