/***
    Pandora
***/

var pandora = new Ox.App({
    apiURL: '/api/',
    config: '/site.json',
    init: 'hello',
}).launch(function(data) {

    Ox.print('data', data);

	var app = {
			$ui: {
				body: $('body'),
				document: $(document),
				window: $(window)
			},
			config: data.config,
			requests: {},
			templates: {
				info: $('<div>').load('/static/html/itemInfo.html')
			},
			ui: {
				infoRatio: 4 / 3,
				sectionElement: 'buttons',
		        selectedMovies: []
			},
			user: data.user
		};

    if (app.user.group == 'guest') {
        app.user = data.config.user;
        $.browser.safari && Ox.theme('modern');
    }


    // $.each(pandora.afterLaunch, function(i, f) { f(); });
    

	function load() {

	    Ox.Request.requests() && app.$ui.loadingIcon.start();
	    Ox.Event.bind('', 'requestStart', function() {
	        Ox.print('requestStart')
	        app.$ui.loadingIcon.start();
	    });
	    Ox.Event.bind('', 'requestStop', function() {
	        Ox.print('requestStop')
	        app.$ui.loadingIcon.stop();
	    });

		Query.fromString(location.hash.substr(1));

	    URL.parse();
	    window.onpopstate = function() {
	        URL.update();
	    };

		app.$ui.appPanel = ui.appPanel();
		app.$ui.appPanel.display();	    

        app.ui.sectionButtonsWidth = app.$ui.sectionButtons.width() + 8;

        app.$ui.window.resize(function() {
            if (app.user.ui.item == '') {
                app.$ui.list.size();
                resizeGroups(app.$ui.rightPanel.width());
                if (app.user.ui.listView == 'map') {
                    app.$ui.map.triggerResize();
                }
            } else {
                Ox.print('app.$ui.window.resize');
                app.$ui.editor.options({
					height: app.$ui.document.height() -
					        20 - 24 - app.$ui.contentPanel.size(0) - 1 - 16,
                    width: app.$ui.document.width() -
                            app.$ui.mainPanel.size(0) - app.$ui.timelinePanel.size(1) - 2 // fixme: item, not timelinePanel
                });
            }
        });

        pandora.app = app; // remove later

	}

    function login(data) {
        app.user = data.user;
        app.$ui.appPanel.reload();
    }

    function logout(data) {
        app.user = data.user;
        app.$ui.appPanel.reload();
    }

	var ui = {
	    accountCodeInput: function() {
	        return new Ox.Input({
                id: 'code',
                label: 'Code',
                labelWidth: 100,
                validate: function(value, callback) {
                    callback({
                        message: 'Missing Code',
                        valid: value.length > 0
                    });
                },
                width: 368
            });
	    },
	    accountDialog: function(action) {
	        var buttonTitle = {
                    login: 'Login',
                    register: 'Register',
                    reset: 'Reset Password',
                    resetlogin: 'Reset Password and Login'
                },            
	            buttons = {
    	            login: ['register', 'reset'],
    	            register: ['login'],
    	            reset: ['login'],
    	            resetlogin: ['login']
	            },
    	        text = {
                    login: '',
                    register: '',
                    reset: 'To reset your password, please enter your username or e-mail address.',
                    resetlogin: 'To login to your account, please choose a new password, and enter the code that we have e-mailed you.'
                },
                title = {
                    login: 'Login',
                    register: 'Register',
                    reset: 'Reset Password',
                    resetlogin: 'Reset Password'
                };
	        var that = new Ox.Dialog({
    	            buttons: [
    	                $.map(buttons[action], function(v) {
                            return button(v);
                        }),
                        [button('cancel'), button('submit')]
                    ],
                    content: app.$ui.accountForm = ui.accountForm(action),
                    height: 200,
                    id: 'accountDialog',
                    minHeight: 200,
                    minWidth: 400,
                    title: title[action],
                    width: 400
    	        })
    	        .bindEvent({
    	            resize: function(event, data) {
                        var width = data.width - 32;
                        app.$ui.accountForm.items.forEach(function(item) {
                            item.options({width: width})
                        });
                    }
    	        });
    	    function button(type) {
    	        if (type == 'cancel') {
    	            return new Ox.Button({
                        id: 'cancel',
                        title: 'Cancel'
                    }).bindEvent('click', function() {
                        that.close();
                    })
    	        } else if (type == 'submit') {
    	            return new Ox.Button({
                        disabled: true,
                        id: 'submit',
                        title: buttonTitle[action]
                    }).bindEvent('click', function() {
                        app.$ui.accountForm.submit()
                    })
    	        } else {
    	            return new Ox.Button({
    	                id: type,
    	                title: title[type] + '...'
    	            }).bindEvent('click', function() {
        	            action = type;
                        that.options({
                            buttons: [
                                $.map(buttons[type], function(v) {
                                    return button(v);
                                }),
                                [button('cancel'), button('submit')]
                            ],
                            content: app.$ui.accountForm = ui.accountForm(type),
                            title: title[type]
                        });
                    });
    	        }
    	    }
	        return that;
	    },
	    accountEmailInput: function() {
	        return new Ox.Input({
                id: 'email',
                label: 'E-Mail Address',
                labelWidth: 100,
                type: 'email',
                validate: function(value, callback) {
                    callback({
                        message: 'Missing E-Mail Address',
                        valid: value.length > 0
                    });
                },
                width: 368
            });
	    },
	    accountForm: function(action) {
	        app.$ui.codeInput = ui.accountEmailInput();
	        app.$ui.emailInput = ui.accountEmailInput();
	        app.$ui.passwordInput = ui.accountPasswordInput();
	        app.$ui.usernameInput = ui.accountUsernameInput();
	        app.$ui.usernameOrEmailInput = ui.accountUsernameOrEmailInput();
	        var items = {
	                'login': [
	                    app.$ui.usernameInput,
	                    app.$ui.passwordInput
	                ],
	                'register': [
                        app.$ui.usernameInput,
                        app.$ui.passwordInput,
                        app.$ui.emailInput
	                ],
	                'reset': [
	                    app.$ui.usernameOrEmailInput
	                ],
	                'resetlogin': [
	                    app.$ui.usernameInput,
	                    app.$ui.passwordInput,
	                    app.$ui.codeInput
	                ]
	            },
	            that = new Ox.Form({
                    error: 'Unknown username or wrong password',
                    id: 'accountForm',
                    items: $.map(items[action], function(v, i) {
                        return {element: v};
                    }),
                    submit: function(data, callback) {
                        pandora.api.login(data, function(result) {
                            if (!result.data.errors) {
                                app.$ui.accountDialog.close();
                                login(result.data);
                            } else {
                                callback([{id: 'password', message: 'Incorrect Password'}]);
                            }
                        });
                    }
                })
                .bindEvent({
                    submit: function(event, data) {
                
                    },
                    validate: function(event, data) {
                        app.$ui.accountDialog[(data.valid ? 'enable' : 'disable') + 'Button']('submit');
                    }
                });
            that.items = items[action];
            return that;	        
	    },
        accountLogoutDialog: function() {
            var that = new Ox.Dialog({
                    buttons: [
                        new Ox.Button({
                            id: 'cancel',
                            title: 'Cancel'
                        }).bindEvent('click', function() {
                            that.close();
                            app.$ui.mainMenu.getItem('loginlogout').toggleTitle();
                        }),
                        new Ox.Button({
                            id: 'submit',
                            title: 'Logout'
                        }).bindEvent('click', function() {
                            that.close();
                            pandora.api.logout({}, function(result) {
                                logout(result.data);
                            });
                        })
                    ],
                    height: 160,
                    title: 'Logout',
                    width: 300
                })
                .append(
                    new Ox.Element('div')
                        .html('Are you sure you want to logout?')
                )
            return that;
        },
	    accountPasswordInput: function() {
	        return new Ox.Input({
	            autovalidate: /.+/,
                id: 'password',
                label: 'Password',
                labelWidth: 100,
                type: 'password',
                validate: function(value, callback) {
                    callback({
                        message: 'Missing Password',
                        valid: value.length > 0
                    });
                },
                width: 368
            });
	    },
	    accountUsernameInput: function() {
	        return new Ox.Input({
                autovalidate: function(value, blur, callback) {
                    var length = value.length;
                    value = $.map(value.toLowerCase().split(''), function(v, i) {
                        if (new RegExp('[a-z0-9' + ((i == 0 || (i == length - 1 && blur)) ? '' : '\-_') + ']')(v)) {
                            return v
                        } else {
                            return null;
                        }
                    }).join('');
                    $.each(['--', '-_', '_-', '__'], function(i, v) {
                        while (value.indexOf(v) > -1) {
                            value = value.replace(new RegExp(v, 'g'), v[0]);
                        }
                    })
                    callback(value);
                },
                id: 'username',
                label: 'Username',
                labelWidth: 100,
                validate: function(value, callback) {
                    !value.length ? callback({
                        message: 'Missing Username',
                        valid: false
                    }) : pandora.api.findUser({
                        key: 'username',
                        value: value,
                        operator: '='
                    }, function(result) {
                        Ox.print('result', result)
                        var valid = result.data.users.length == 1;
                        callback({
                            message: 'Unknown Username',
                            valid: valid
                        });
                    });
                },
                width: 368
            });
	    },
        accountUsernameOrEmailInput: function() {
            return new Ox.Input({
                id: 'email',
                label: 'Username or E-Mail Address',
                labelWidth: 184,
                validate: function(value, callback) {
                    callback({
                        message: 'Missing Username or E-Mail Address',
                        valid: value.length > 0
                    });
                },
                width: 368
            });
        },
		annotations: function() {
			var that = new Ox.Element(),
				$bins = [];
		    $.each(app.config.layers, function(i, layer) {
		        var $bin = new Ox.CollapsePanel({
		            id: layer.id,
		            size: 16,
		            title: layer.title
		        });
		        $bins.push($bin);
		        $bin.$content.append(
		            $('<div>').css({ height: '20px' }).append(
		                $('<div>').css({ float: 'left', width: '16px', height: '16px', margin: '1px'}).append(
		                    $('<img>').attr({ src: 'static/oxjs/build/png/ox.ui.modern/iconFind.png' }).css({ width: '16px', height: '16px', border: 0, background: 'rgb(64, 64, 64)', WebkitBorderRadius: '2px' })
		                )
		            ).append(
		                $('<div>').css({ float: 'left', width: '122px', height: '14px', margin: '2px' }).html('Foo')
		            ).append(
		                $('<div>').css({ float: 'left', width: '40px', height: '14px', margin: '2px', textAlign: 'right' }).html('23')
		            )
		        );
		    });
		    $.each($bins, function(i, bin) {
		        that.append(bin);
		    });
			return that;
		},
		appPanel: function() {
			var that = new Ox.SplitPanel({
			        elements: [
			            {
			                element: app.$ui.mainMenu = ui.mainMenu(),
			                size: 20
			            },
			            {
			                element: app.$ui.mainPanel = ui.mainPanel()
						}
					],
			        orientation: 'vertical'
			    });
			that.display = function() {
				app.$ui.body.css({opacity: 0});
			    that.appendTo(app.$ui.body);
			    app.$ui.body.animate({opacity: 1}, 1000);
				return that;
			}
			that.reload = function() {
			    $.each($elements, function(i, $element) {
			        $element.remove();
			    });
			    app.$ui.appPanel = ui.appPanel().appendTo(app.$ui.body);
			    return that;
			}
			return that;
		},
		browser: function(mode) {
			if (mode == 'list') {
				app.$ui.groups = ui.groups();
				var that = new Ox.SplitPanel({
	                elements: [
	                    {
	                        element: app.$ui.groups[0],
	                        size: app.ui.groups[0].size
	                    },
	                    {
	                        element: app.$ui.groupsInnerPanel = ui.groupsInnerPanel()
	                    },
	                    {
	                        element: app.$ui.groups[4],
	                        size: app.ui.groups[4].size
	                    },
	                ],
	                id: 'browser',
	                orientation: 'horizontal'
	            })
	            .bindEvent('resize', function(event, data) {
	                Ox.print('resizing groups...')
	                $.each(app.$ui.groups, function(i, list) {
	                    list.size();
	                });
	            });
			} else if (mode == 'item') {
				var that = new Ox.Element('div');
			}
			return that;
		},
		contentPanel: function() {
			var that = new Ox.SplitPanel({
		        elements: app.user.ui.item == '' ? [
		            {
		                collapsible: true,
		                element: app.$ui.browser = ui.browser('list')
			                .bindEvent('resize', function(event, data) {
			                    Ox.print('resizing groups...')
			                    $.each(app.$ui.groups, function(i, list) {
			                        list.size();
			                    });
			                }),
		                resizable: true,
		                resize: [96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256],
		                size: app.user.ui.groupsSize
		            },
		            {
		                element: app.$ui.list = ui.list(app.user.ui.listView)
		            }
		        ] : [
    	            {
    	                collapsible: true,
    	                element: app.$ui.browser = ui.browser('item'),
    	                size: 80
    	            },
    	            {
    	                element: app.$ui.item = ui.item(app.user.ui.item, app.user.ui.itemView)
    	            }
		        ],
		        orientation: 'vertical'
		    });
			return that;
		},
		findElement: function() {
			var that = new Ox.FormElementGroup({
                    elements: [
                        app.$ui.findSelect = new Ox.Select({
                                id: 'select',
                                items: $.map(app.config.findKeys, function(key, i) {
                                    return {
                                        id: key.id,
                                        title: 'Find: ' + key.title
                                    };
                                }),
                                overlap: 'right',
                                width: 112
                            })
                            .bindEvent('change', function(event, data) {
                                var key = data.selected[0].id;
                                app.user.ui.findQuery.conditions[0].key = key
                                app.$ui.mainMenu.checkItem('findMenu_find_' + key);
                                app.$ui.findInput.focus();
                            }),
                        app.$ui.findInput = new Ox.Input({
                            autocomplete: function(value, callback) {
                                var key = 'title';
                                var findKey = Ox.getObjectById(app.config.findKeys, key);
                                Ox.print('autocomplete', key, value);
                                value === '' && Ox.print('Warning: autocomplete function should never be called with empty value');
                                if ('autocomplete' in findKey && findKey.autocomplete) {
                                    pandora.api.find({
                                        keys: [key],
                                        query: {
                                            conditions: [
                                                {
                                                    key: key,
                                                    value: value,
                                                    operator: ''
                                                }
                                            ],
                                            operator: ''
                                        },
                                        sort: [
                                            {
                                                key: key,
                                                operator: ''
                                            }
                                        ],
                                        range: [0, 10]
                                    }, function(result) {
                                        callback($.map(result.data.items, function(v) {
                                            return v.title;
                                        }));
                                    });
                                } else {
                                    callback();                            
                                }
                            },
                            autocompleteSelect: true,
                            autocompleteSelectHighlight: true,
                            autocompleteSelectSubmit: true,
                            clear: true,
                            id: 'input',
                            width: 192
                        })
                        .bindEvent('submit', function(event, data) {
                            var key = app.user.ui.findQuery.conditions[0].key,
                                query;
                            Ox.print('key', key);
                            app.user.ui.findQuery.conditions = [
                                {
                                    key: key == 'all' ? '' : key,
                                    value: data.value,
                                    operator: ''
                                }
                            ];
                            $.each(app.ui.groups, function(i, group) {
                                group.query.conditions = [];
                                app.$ui.groups[i].options({
                                    request: function(data, callback) {
                                        delete data.keys;
                                        return pandora.api.find($.extend(data, {
                                            group: group.id,
                                            query: Query.toObject(group.id)
                                        }), callback);
                                    }
                                });
                            });
                            app.$ui.list.options({
                                request: function(data, callback) {
                                    return pandora.api.find($.extend(data, {
                                        query: query = Query.toObject()
                                    }), callback);
                                }
                            });
                            history.pushState({}, '', '/#' + Query.toString(query));
                        })
                    ],
                    id: 'findElement'
                })
                .css({
                    float: 'right',
                    margin: '4px'
                });
			return that;
		},
		groups: function() {
			var $groups = [],
				panelWidth = app.$ui.document.width() - app.user.ui.listsSize - 1;
		    app.ui.groups = $.map(app.config.groups, function(id, i) {
		        var title = Ox.getObjectById(app.config.sortKeys, id).title,
		            width = getGroupWidth(i, panelWidth);
		        return {
		            id: id,
		            element: $groups[i] = new Ox.TextList({
		                    columns: [
		                        {
		                            align: 'left',
		                            id: 'name',
		                            operator: id == 'year' ? '-' : '+',
		                            title: title,
		                            unique: true,
		                            visible: true,
		                            width: width.column
		                        },
		                        {
		                            align: 'right',
		                            id: 'items',
		                            operator: '-',
		                            title: '#',
		                            visible: true,
		                            width: 40
		                        }
		                    ],
		                    id: 'group_' + id,
		                    request: function(data, callback) {
		                        Ox.print('sending request', data)
		                        delete data.keys;
		                        return pandora.api.find($.extend(data, {
		                            group: id,
		                            query: Query.toObject(id)
		                        }), callback);
		                    },
		                    sort: [
		                        {
		                            key: id == 'year' ? 'name' : 'items',
		                            operator: '-'
		                        }
		                    ]
		                })
		                .bindEvent('select', function(event, data) {
		                    Ox.print('-- select', i)
		                    var group = app.ui.groups[i],
		                        query;
		                    app.ui.groups[i].query.conditions = $.map(data.ids, function(v) {
		                        return {
		                            key: group.id,
		                            value: v,
		                            operator: '='
		                        };
		                    });
		                    query = Query.toObject();
		                    Ox.print('-- data, query', data, query)
		                    app.$ui.list.options({
		                        request: function(data, callback) {
		                            return pandora.api.find($.extend(data, {
		                                query: query
		                            }), callback);
		                        }
		                    });
		                    $.each(app.ui.groups, function(i_, group_) {
		                        if (i_ != i) {
		                            Ox.print('setting groups request', i, i_)
		                            app.$ui.groups[i_].options({
		                                request: function(data, callback) {
		                                    delete data.keys;
		                                    return pandora.api.find($.extend(data, {
		                                        group: group_.id,
		                                        query: Query.toObject(group_.id)
		                                    }), callback);
		                                }
		                            });
		                        }
		                    });
		                    history.pushState({}, '', '/#' + Query.toString(query));
		                }),
		            query: {
		                conditions: [],
		                operator: '|'
		            },
		            size: width.list,
		            title: title
		        };
		    });
		    return $groups;
		},
		groupsInnerPanel: function() {
			var that = new Ox.SplitPanel({
                elements: [
                    {
                        element: app.$ui.groups[1],
                        size: app.ui.groups[1].size
                    },
                    {
                        element: app.$ui.groups[2],
                    },
                    {
                        element: app.$ui.groups[3],
                        size: app.ui.groups[3].size
                    }
                ],
                orientation: 'horizontal'
            });
			return that;
		},
		info: function() {
			var that = new Ox.Element()
		        .append(
		            app.$ui.infoStill = new Ox.Element('img')
		                .css({
		                    position: 'absolute',
		                    left: 0,
		                    top: 0
		                })
		        )
		        .append(
		            app.$ui.infoTimeline = new Ox.Element('img')
		                .css({
		                    position: 'absolute',
		                    left: 0,
		                    bottom: 0,
		                    height: '16px',
		                })
		        );
		    return that;
		},
        item: function(id, view) { // fixme: params are not necessary
            var that,
                elements = [
                ];
            if (view == 'timeline') {
                that = app.$ui.timelinePanel = new Ox.SplitPanel({
					elements: [
						{
							element: new Ox.Element('div'),
						},
						{
							collapsible: true,
							element: app.$ui.annotations = ui.annotations(),
							size: 256
						}
					],
					orientation: 'horizontal'
				});
				getItem();
            }
            function getItem() {
                pandora.api.getItem(id, function(result) {
		            var video = result.data.item.stream,
		                cuts = result.data.item.layers.cuts || {},
		                subtitles = result.data.item.layers.subtitles || [{
		                    'in': 5,
		                    'out': 10,
		                    'text': 'This subtitle is just a test...'
		                }];
		            video.height = 96;
		            video.width = parseInt(video.height * video.aspectRatio / 2) * 2;
		            video.url = video.baseUrl + '/' + video.height + 'p.' + ($.support.video.webm ? 'webm' : 'mp4');
		            app.$ui.timelinePanel.replace(0, app.$ui.editor = new Ox.VideoEditor({
		                cuts: cuts,
		                duration: video.duration,
		                find: '',
		                frameURL: function(position) {
		                    return '/' + id + '/frame/' + video.width.toString() + '/' + position.toString() + '.jpg'
		                },
						height: app.$ui.contentPanel.size(1),
		                id: 'editor',
		                largeTimeline: true,
		                matches: [],
		                points: [0, 0],
		                position: 0,
		                posterFrame: parseInt(video.duration / 2),
		                subtitles: subtitles,
		                videoHeight: video.height,
		                videoId: id,
		                videoWidth: video.width,
		                videoSize: 'small',
		                videoURL: video.url,
		                width: app.$ui.document.width() - app.$ui.mainPanel.size(0) - 1 - 256 - 1
		            }).bindEvent('resize', function(event, data) {
						Ox.print('RESIZE:', data)
						app.$ui.editor.options({
							width: data
						});
					}));
					app.$ui.rightPanel.bindEvent('resize', function(event, data) {
	                    Ox.print('rightPanel resize', data, app.$ui.timelinePanel.size(1))
	                    app.$ui.editor.options({
	                        width: data - app.$ui.timelinePanel.size(1) - 1
	                    });
	                });
		        });
            }
            that.display = function() {
                app.$ui.contentPanel.replaceElements([
                    {
						collapsible: true,
						element: app.$ui.browser = new Ox.Element('div').options({id: 'browser'}),
						resizable: false,
						size: 80
					},
					{
					    element: app.$ui.timelinePanel = new Ox.SplitPanel({
							elements: [
								{
									element: app.$ui.editor = new Ox.Element('div')
								},
								{
									collapsible: true,
									element: app.$ui.annotations = ui.annotations(),
									size: 256
								}
							],
							orientation: 'horizontal'
						})
					}
	            ]);
	            // getItem(); // fixme: can the asynchronicity be moved within the video editor?
            }
            return that;
        },
		item_: function(id, view) {
			var $item;
		    //location.hash = '!' + id;
		    //app.user.ui.mode = 'item';
		    app.$ui.mainMenu.enableItem('openmovie');
		    app.$ui.mainMenu.checkItem('viewMenu_openmovie_' + view);
		    //FIXME: there should be a menu function for this
		    if (view == 'timeline') {
		        pandora.api.getItem(id, function(result) {
		            item_debug = result.data.item;
		            var video = result.data.item.stream,
		                cuts = result.data.item.layers.cuts || {},
		                subtitles = result.data.item.layers.subtitles || [{
		                    'in': 5,
		                    'out': 10,
		                    'text': 'This subtitle is just a test...'
		                }];
		            video.height = 96;
		            video.width = parseInt(video.height * video.aspectRatio / 2) * 2;
		            video.url = video.baseUrl + '/' + video.height + 'p.' + ($.support.video.webm ? 'webm' : 'mp4');
					//app.$ui.contentPanel.size(0, 80);
		            app.$ui.contentPanel.replaceElements([
						{
							collapsible: true,
							element: app.$ui.browser = new Ox.Element('div').options({id: 'browser'}),
							resizable: false,
							size: 80
						},
						{
							element: app.$ui.timelinePanel = new Ox.SplitPanel({
								elements: [
									{
										element: app.$ui.editor = new Ox.VideoEditor({
							                cuts: cuts,
							                duration: video.duration,
							                find: '',
							                frameURL: function(position) {
							                    return '/' + id + '/frame/' + video.width.toString() + '/' + position.toString() + '.jpg'
							                },
											height: app.$ui.contentPanel.size(1),
							                id: 'editor',
							                largeTimeline: true,
							                matches: [],
							                points: [0, 0],
							                position: 0,
							                posterFrame: parseInt(video.duration / 2),
							                subtitles: subtitles,
							                videoHeight: video.height,
							                videoId: id,
							                videoWidth: video.width,
							                videoSize: 'small',
							                videoURL: video.url,
							                width: app.$ui.document.width() - app.$ui.mainPanel.size(0) - 1 - 256 - 1
							            })
										.bindEvent('resize', function(event, data) {
											Ox.print('RESIZE:', data)
											app.$ui.editor.options({
												width: data
											});
										}),
										size: 'auto'
									},
									{
										collapsible: true,
										element: app.$ui.annotations = ui.annotations(),
										size: 256
									}
								],
								orientation: 'horizontal'
							}),
							size: 'auto'
						}
					]);
		            app.$ui.rightPanel
		                .bindEvent('resize', function(event, data) {
		                    Ox.print('rightPanel resize', data, app.$ui.item.size(1))
		                    app.$ui.editor.options({
		                        width: data - app.$ui.item.size(1) - 1
		                    });
		                });
					///*
		            app.$ui.window.resize(function() {
		                app.$ui.editor.options({
							height: app.$ui.document.height() - 20 - 24 - app.$ui.contentPanel.size(0) - 1 - 16,
		                    width: app.$ui.document.width() - app.$ui.mainPanel.size(0) - app.$ui.timelinePanel.size(1) - 2
		                });
		            });
					//*/
		        });
		    } else if (view == 'info') {
		        pandora.api.getItem(id, function(result) {
		            item_debug = result.data.item;
		            var item = result.data.item;
		            var $item = new Ox.Container();
		            $item.append(app.template.info.tmpl(item));
		            app.$ui.rightPanel.replace(1, $item);
		            /*
		            app.$ui.rightPanel
		                .bindEvent('resize', function(event, data) {
		                    app.$ui.editor.options({
		                        width: data - app.$ui.timelinePanel.size(1) - 1
		                    });
		                });
		            */
		        });
		    }
		},
		leftPanel: function() {
			var that = new Ox.SplitPanel({
                    elements: [
						{
							element: app.$ui.sectionbar = ui.sectionbar('buttons'),
							size: 24
						},
                        {
                            element: app.$ui.sections = ui.sections().options({
                                id: 'listsPanel'
                            })
                        },
                        {
                            collapsible: true,
                            element: app.$ui.info = ui.info().options({
                                id: 'infoPanel'
                            }),
                            size: app.user.ui.listsSize / app.ui.infoRatio + 16
                        }
                    ],
                    id: 'leftPanel',
                    orientation: 'vertical'
                })
                .bindEvent('resize', function(event, data) {
                    if (data < app.ui.sectionButtonsWidth && app.$ui.sectionButtons) {
                        app.$ui.sectionButtons.remove();
                        delete app.$ui.sectionButtons;
                        app.$ui.sectionbar.append(app.$ui.sectionSelect = ui.sectionSelect());
                    } else if (data >= app.ui.sectionButtonsWidth && app.$ui.sectionSelect) {
                        app.$ui.sectionSelect.remove();
                        delete app.$ui.sectionSelect;
                        app.$ui.sectionbar.append(app.$ui.sectionButtons = ui.sectionButtons());
                    }
                    Ox.print('resize', data, data / app.ui.infoRatio + 16);
                    app.$ui.leftPanel.size('infoPanel', Math.round(data / app.ui.infoRatio) + 16);
                });
			return that;
		},
		list: function(view) {
			var that, $map,
		        keys = ['director', 'id', 'poster', 'title', 'year'];
		    app.user.ui.mode = 'list';
		    Ox.print('constructList', view);
		    if (view == 'list') {
		        that = new Ox.TextList({
		            columns: $.map(app.config.sortKeys, function(key, i) {
		                return $.extend({
		                    visible: $.inArray(key.id, app.user.ui.columns) > -1,
		                    unique: key.id == 'id'
		                }, key);
		            }),
		            columnsMovable: true,
		            columnsRemovable: true,
		            columnsResizable: true,
		            id: 'list',
		            request: function(data, callback) {
		                Ox.print('data, Query.toObject', data, Query.toObject())
		                pandora.api.find($.extend(data, {
		                    query: Query.toObject()
		                }), callback);
		            },
		            sort: app.user.ui.sort
		        })
		        .bindEvent({
		            resize: function(event, data) {
		                that.size();
		            }
		        });
		    } else if (view == 'icons') {
		        that = new Ox.IconList({
		            id: 'list',
		            item: function(data, sort, size) {
		                var ratio = data.poster.width / data.poster.height;
		                size = size || 128;
		                return {
		                    height: ratio <= 1 ? size : size / ratio,
		                    id: data['id'],
		                    info: data[['title', 'director'].indexOf(sort[0].key) > -1 ? 'year' : sort[0].key],
		                    title: data.title + (data.director ? ' (' + data.director + ')' : ''),
		                    url: data.poster.url.replace(/jpg/, size + '.jpg'),
		                    width: ratio >= 1 ? size : size * ratio
		                };
		            },
		            keys: keys,
		            request: function(data, callback) {
		                Ox.print('data, Query.toObject', data, Query.toObject())
		                pandora.api.find($.extend(data, {
		                    query: Query.toObject()
		                }), callback);
		            },
		            size: 128,
		            sort: app.user.ui.sort,
		            unique: 'id'
		        });
			} else if (view == 'map') {
				that = new Ox.SplitPanel({
					elements: [
						{
							element: new Ox.SplitPanel({
								elements: [
									{
										element: new Ox.Toolbar({
		                                    	orientation: 'horizontal',
		                                    	size: 24
		                                	})
											.append(
		                                        app.$ui.findMapInput = new Ox.Input({
		                                            clear: true,
		                                            id: 'findMapInput',
		                                            placeholder: 'Find on Map',
		                                            width: 192
		                                        })
		                                        .css({
		                                            float: 'right',
		                                            margin: '4px'
		                                        })
												.bindEvent({
													submit: function(event, data) {
														app.$ui.map.find(data.value, function(data) {
															app.$ui.mapStatusbar.html(data.geoname + ' ' + JSON.stringify(data.points))
														});
													}
												})
		                                    ),
										size: 24
									},
									{
										element: app.$ui.map = new Ox.Map({
												places: [
													{
														geoname: 'Beirut, Lebanon',
														name: 'Beirut',
														points: {
															'center': [33.8886284, 35.4954794],
															'northeast': [33.8978909, 35.5114868],
															'southwest': [33.8793659, 35.479472]
														}
													},
													{
														geoname: 'Berlin, Germany',
														name: 'Berlin',
														points: {
															'center': [52.506701, 13.4246065],
															'northeast': [52.675323, 13.760909],
															'southwest': [52.338079, 13.088304]
														}
													},
													{
														geoname: 'Mumbai, Maharashtra, India',
														name: 'Bombay',
														points: {
															'center': [19.07871865, 72.8778187],
															'northeast': [19.2695223, 72.9806562],
															'southwest': [18.887915, 72.7749812]
														}
													}
												]
											})
											.bindEvent({								
												select: function(event, data) {
													app.$ui.mapStatusbar.html(data.geoname + ' ' + JSON.stringify(data.points))
												}
											}),
										id: 'map',
										size: 'auto'
									},
									{
										element: app.$ui.mapStatusbar = new Ox.Toolbar({
			                                    orientation: 'horizontal',
			                                    size: 16
			                                })
											.css({
												fontSize: '9px',
												padding: '2px 4px 0 0',
												textAlign: 'right'
											}),
										size: 16
									}
								],
								orientation: 'vertical'
							}),
						},
						{
							element: new Ox.Element(),
							id: 'place',
							size: 128 + 16 + 12
						}
					],
					orientation: 'horizontal'
				})
				.bindEvent('resize', function() {
				    app.$ui.map.triggerResize();
				});
		    } else {
		        $list = new Ox.Element('<div>')
		            .css({
		                width: '100px',
		                height: '100px',
		                background: 'red'
		            });
		    }

		    ['list', 'icons'].indexOf(view) > -1 && that.bindEvent({
		        closepreview: function(event, data) {
		            app.$ui.previewDialog.close();
		            delete app.$ui.previewDialog;
		        },
		        load: function(event, data) {
		            app.$ui.total.html(ui.status('total', data));
		            data = [];
		            $.each(app.config.totals, function(i, v) {
		                data[v.id] = 0;
		            });
		            app.$ui.selected.html(ui.status('selected', data));
		        },
		        open: function(event, data) {
		            var id = data.ids[0];
		            URL.set(id);
		        },
		        openpreview: function(event, data) {
		            app.requests.preview && pandora.api.cancel(app.requests.preview);
		            app.requests.preview = pandora.api.find({
		                keys: ['director', 'id', 'poster', 'title'],
		                query: {
		                    conditions: $.map(data.ids, function(id, i) {
		                        return {
		                            key: 'id',
		                            value: id,
		                            operator: '='
		                        }
		                    }),
		                    operator: '|'
		                }
		            }, function(result) {
		                var documentHeight = app.$ui.document.height(),
		                    item = result.data.items[0],
		                    title = item.title + (item.director ? ' (' + item.director + ')' : ''),
		                    dialogHeight = documentHeight - 100,
		                    dialogWidth;
		                app.ui.previewRatio = item.poster.width / item.poster.height,
		                dialogWidth = parseInt((dialogHeight - 48) * app.ui.previewRatio);
		                if ('previewDialog' in app.$ui) {
		                    app.$ui.previewDialog.options({
		                        title: title
		                    });
		                    app.$ui.previewImage.animate({
		                        opacity: 0
		                    }, 100, function() {
		                        app.$ui.previewDialog.size(dialogWidth, dialogHeight, function() {
		                            app.$ui.previewImage
		                                .attr({
		                                    src: item.poster.url
		                                })
		                                .one('load', function() {
		                                    app.$ui.previewImage
		                                        .css({
		                                            width: dialogWidth + 'px',
		                                            height: (dialogHeight - 48 - 2) + 'px', // fixme: why -2 ?
		                                            opacity: 0
		                                        })
		                                        .animate({
		                                            opacity: 1
		                                        }, 100);
		                                });
		                        });
		                    });
		                    Ox.print(app.$ui.document.height(), dialogWidth, 'x', dialogHeight, dialogWidth / (dialogHeight - 48), item.poster.width, 'x', item.poster.height, item.poster.width / item.poster.height)
		                } else {
		                    app.$ui.previewImage = $('<img>')
		                        .attr({
		                            src: item.poster.url
		                        })
		                        .css({
		                            position: 'absolute',
		                            width: dialogWidth + 'px',
		                            height: (dialogHeight - 48 - 2) + 'px', // fixme: why -2 ?
		                            left: 0,
		                            top: 0,
		                            right: 0,
		                            bottom: 0,
		                            margin: 'auto',
		                        });
		                    app.$ui.previewDialog = new Ox.Dialog({
		                            buttons: [
		                                {
		                                    title: 'Close',
		                                    click: function() {
		                                        app.$ui.previewDialog.close();
		                                        delete app.$ui.previewDialog;
		                                        app.$ui.list.closePreview();
		                                    }
		                                }
		                            ],
		                            height: dialogHeight,
		                            id: 'previewDialog',
		                            minHeight: app.ui.previewRatio >= 1 ? 128 / app.ui.previewRatio + 48 : 176,
		                            minWidth: app.ui.previewRatio >= 1 ? 128 : 176 * app.ui.previewRatio,
		                            padding: 0,
		                            title: title,
		                            width: dialogWidth
		                        })
		                        .append(app.$ui.previewImage)
		                        .bindEvent('resize', function(event, data) {
		                            var dialogRatio = data.width / (data.height - 48),
		                                height, width;
		                            if (dialogRatio < app.ui.previewRatio) {
		                                width = data.width;
		                                height = width / app.ui.previewRatio;
		                            } else {
		                                height = (data.height - 48 - 2);
		                                width = height * app.ui.previewRatio;
		                            }
		                            app.$ui.previewImage.css({
		                                width: width + 'px',
		                                height: height + 'px', // fixme: why -2 ?
		                            })
		                        })
		                        .open();
		                    //app.$ui.previewImage = $image;
		                    //Ox.print(app.$document.height(), dialogWidth, 'x', dialogHeight, dialogWidth / (dialogHeight - 48), item.poster.width, 'x', item.poster.height, item.poster.width / item.poster.height)
		                }
		            });
		        },
		        select: function(event, data) {
		            var $still, $timeline;
		            app.ui.selectedMovies = data.ids;
		            if (data.ids.length) {
		                app.$ui.mainMenu.enableItem('copy');
		                app.$ui.mainMenu.enableItem('openmovie');
		            } else {
		                app.$ui.mainMenu.disableItem('copy');
		                app.$ui.mainMenu.disableItem('openmovie');            
		            }
		            if (data.ids.length == 1) {
		                $still = $('<img>')
		                    .attr({
		                        src: 'http://0xdb.org/' + data.ids[0] + '/still.jpg'
		                    })
		                    .one('load', function() {
		                        if (data.ids[0] != app.ui.selectedMovies[0]) {
		                            Ox.print('cancel after load...')
		                            return;
		                        }
		                        var image = $still[0],
		                            imageWidth = image.width,
		                            imageHeight = image.height,
		                            width = app.$ui.info.width(),
		                            height = imageHeight * width / imageWidth;
		                        app.ui.infoRatio = width / height;
		                        app.$ui.leftPanel.size('infoPanel', height + 16);
		                        $still.css({
		                                position: 'absolute',
		                                left: 0,
		                                top: 0,
		                                //width: width + 'px',
		                                //height: height + 'px',
		                                width: '100%',
		                                opacity: 0
		                            })
		                            .appendTo(app.$ui.info.$element)
		                            .animate({
		                                opacity: 1
		                            });
		                        app.$ui.infoStill.animate({
		                            opacity: 0
		                        }, 250);
		                        app.$ui.info.animate({
		                            height: (height + 16) + 'px'
		                        }, 250, function() {
		                            app.$ui.infoStill.remove();
		                            app.$ui.infoStill = $still;
		                        });
		                    });
		                /*
		                $timeline = $('<img>')
		                    .attr({
		                        src: 'http://0xdb.org/' + data.ids[0] + '/timeline/timeline.png'
		                    })
		                    .one('load', function() {
		                        $timeline.css({
		                                position: 'absolute',
		                                left: 0,
		                                bottom: '16px',
		                                opacity: 0
		                            })
		                            .appendTo($ui.info.$element)
		                            .animate({
		                                opacity: 1
		                            });
		                        $ui.infoTimeline.animate({
		                            opacity: 0
		                        }, 250, function() {
		                            $ui.infoTimeline.remove();
		                            $ui.infoTimeline = $timeline;
		                        });
		                    });
		                */
		            }
		            pandora.api.find({
		                query: {
		                    conditions: $.map(data.ids, function(id, i) {
		                        return {
		                            key: 'id',
		                            value: id,
		                            operator: '='
		                        }
		                    }),
		                    operator: '|'
		                }
		            }, function(result) {
		                app.$ui.selected.html(ui.status('selected', result.data));
		            });
		        },
		        sort: function(event, data) {
		            /* some magic has already set user.ui.sort
		            Ox.print(':', user.ui.sort[0])
		            if (data.key != user.ui.sort[0].key) {
		                app.$ui.mainMenu.checkItem('sort_sortmovies_' + data.key);
		            }
		            if (data.operator != user.ui.sort[0].operator) {
		                app.$ui.mainMenu.checkItem('sort_ordermovies_' + data.operator === '' ? 'ascending' : 'descending');
		            }
		            user.ui.sort[0] = data;
		            */
		            app.$ui.mainMenu.checkItem('sortMenu_sortmovies_' + data.key);
		            app.$ui.mainMenu.checkItem('sortMenu_ordermovies_' + (data.operator === '' ? 'ascending' : 'descending'));
		        }
		    });
            that.display = function() {
                
            };
		    return that;
		},
		mainMenu: function() {
			var isGuest = app.user.group == 'guest',
			    that = new Ox.MainMenu({
		            extras: [
		                app.$ui.loadingIcon = new Ox.LoadingIcon({
		                    size: 'medium'
		                })
		            ],
		            id: 'mainMenu',
		            menus: [
		                { id: app.config.site.id + 'Menu', title: app.config.site.name, items: [
	                        { id: 'home', title: 'Home' },
		                    {},
		                    { id: 'about', title: 'About ' + app.config.site.name },
		                    { id: 'news', title: app.config.site.name + ' News' },
		                    { id: 'tour', title: 'Take a Tour' },
		                    { id: 'faq', title: 'Frequently Asked Questions' },
		                    { id: 'tos', title: 'Terms of Service' },
		                    {},
		                    { id: 'software', title: 'Software', items: [
		                        { id: 'about', title: 'About' },
		                        { id: 'download', title: 'Download' },
		                        { id: 'report', title: 'Report a Bug' }
		                    ] },
		                    {},
		                    { id: 'contact', title: 'Contact ' + app.config.site.name }
		                ] },
		                { id: 'userMenu', title: 'User', items: [
		                    { id: 'username', title: 'User: ' + (isGuest ? 'not logged in' : app.user.username), disabled: true },
		                    {},
		                    { id: 'preferences', title: 'Preferences...', disabled: isGuest, keyboard: 'control ,' },
		                    {},
		                    { id: 'register', title: 'Register...', disabled: !isGuest },
		                    { id: 'loginlogout', title: isGuest ? 'Login...' : 'Logout...' }
		                ] },
		                { id: 'listMenu', title: 'List', items: [
		                    { id: 'history', title: 'History', items: [
		                        { id: 'allmovies', title: 'All Movies' }
		                    ] },
		                    { id: 'lists', title: 'View List', items: [
		                        { id: 'favorites', title: 'Favorites' }
		                    ] },
		                    { id: 'features', title: 'View Feature', items: [
		                        { id: 'situationistfilm', title: 'Situationist Film' },
		                        { id: 'timelines', title: 'Timelines' }
		                    ] },
		                    {},
		                    { id: 'newlist', title: 'New List...', keyboard: 'control n' },
		                    { id: 'newlistfromselection', title: 'New List from Selection...', disabled: true, keyboard: 'shift control n' },
		                    { id: 'newsmartlist', title: 'New Smart List...', keyboard: 'alt control n' },
		                    { id: 'newsmartlistfromresults', title: 'New Smart List from Results...', keyboard: 'shift alt control n' },
		                    {},
		                    { id: 'addmovietolist', title: ['Add Selected Movie to List...', 'Add Selected Movies to List...'], disabled: true },
		                    {},
		                    { id: 'setposterframe', title: 'Set Poster Frame', disabled: true }
		                ]},
		                { id: 'editMenu', title: 'Edit', items: [
		                    { id: 'undo', title: 'Undo', disabled: true, keyboard: 'control z' },
		                    { id: 'redo', title: 'Redo', disabled: true, keyboard: 'shift control z' },
		                    {},
		                    { id: 'cut', title: 'Cut', disabled: true, keyboard: 'control x' },
		                    { id: 'copy', title: 'Copy', disabled: true, keyboard: 'control c' },
		                    { id: 'paste', title: 'Paste', disabled: true, keyboard: 'control v' },
		                    { id: 'delete', title: 'Delete', disabled: true, keyboard: 'delete' },
		                    {},
		                    { id: 'selectall', title: 'Select All', disabled: true, keyboard: 'control a' },
		                    { id: 'selectnone', title: 'Select None', disabled: true, keyboard: 'shift control a' },
		                    { id: 'invertselection', title: 'Invert Selection', disabled: true, keyboard: 'alt control a' }
		                ] },
		                { id: 'viewMenu', title: 'View', items: [
		                    { id: 'movies', title: 'View Movies', items: [
		                        { group: 'viewmovies', min: 0, max: 1, items: $.map(app.config.listViews, function(view, i) {
		                            return $.extend({
		                                checked: app.user.ui.listView == view.id,
		                            }, view);
		                        }) },
		                    ]},
		                    { id: 'icons', title: 'Icons', items: [
		                        { id: 'poster', title: 'Poster' },
		                        { id: 'still', title: 'Still' },
		                        { id: 'timeline', title: 'Timeline' }
		                    ] },
		                    { id: 'info', title: 'Info', items: [
		                        { id: 'poster', title: 'Poster' },
		                        { id: 'video', title: 'Video' }
		                    ] },
		                    {},
		                    { id: 'openmovie', title: ['Open Movie', 'Open Movies'], disabled: true, items: [
		                        { group: 'movieview', min: 0, max: 1, items: $.map(app.config.itemViews, function(view, i) {
		                            return $.extend({
		                                checked: app.user.ui.itemView == view.id,
		                            }, view);
		                        }) },
		                    ]},
		                    {},
		                    { id: 'lists', title: 'Hide Lists', keyboard: 'shift l' },
		                    { id: 'info', title: 'Hide Info', keyboard: 'shift i' },
		                    { id: 'groups', title: 'Hide Groups', keyboard: 'shift g' },
		                    { id: 'movies', title: 'Hide Movies', disabled: true, keyboard: 'shift m' }
		                ]},
		                { id: 'sortMenu', title: 'Sort', items: [
		                    { id: 'sortmovies', title: 'Sort Movies by', items: [
		                        { group: 'sortmovies', min: 1, max: 1, items: $.map(app.config.sortKeys, function(key, i) {
		                            return $.extend({
		                                checked: app.user.ui.sort[0].key == key.id,
		                            }, key);
		                        }) }
		                    ] },
		                    { id: 'ordermovies', title: 'Order Movies', items: [
		                        { group: 'ordermovies', min: 1, max: 1, items: [
		                            { id: 'ascending', title: 'Ascending', checked: app.user.ui.sort[0].operator === '' },
		                            { id: 'descending', title: 'Descending', checked: app.user.ui.sort[0].operator == '-' }
		                        ]}
		                    ] },
		                    { id: 'advancedsort', title: 'Advanced Sort...', keyboard: 'shift control s' },
		                    {},
		                    { id: 'groupsstuff', title: 'Groups Stuff' }
		                ] },
		                { id: 'findMenu', title: 'Find', items: [
		                    { id: 'find', title: 'Find', items: [
		                        { group: 'find', min: 1, max: 1, items: $.map(app.config.findKeys, function(key, i) {
		                            return $.extend({
		                                checked: app.user.ui.findQuery.conditions.length && 
		                                        (app.user.ui.findQuery.conditions[0].key == key.id ||
		                                        (app.user.ui.findQuery.conditions[0].key === '' && key.id == 'all')),
		                            }, key)
		                        }) }
		                    ] },
		                    { id: 'advancedfind', title: 'Advanced Find...', keyboard: 'shift control f' }
		                ] },
		                { id: 'dataMenu', title: 'Data', items: [
		                    { id: 'titles', title: 'Manage Titles...' },
		                    { id: 'names', title: 'Manage Names...' },
		                    {},
		                    { id: 'posters', title: 'Manage Stills...' },
		                    { id: 'posters', title: 'Manage Posters...' },
		                    {},
		                    { id: 'places', title: 'Manage Places...' },
		                    { id: 'events', title: 'Manage Events...' },
		                    {},
		                    { id: 'users', title: 'Manage Users...' },
		                    { id: 'lists', title: 'Manage Lists...' },
		                ] },
		                { id: 'codeMenu', title: 'Code', items: [
		                    { id: 'download', title: 'Download' },
		                    { id: 'contribute', title: 'Contribute' },
		                    { id: 'report', title: 'Report a Bug' },
		                ] },
		                { id: 'helpMenu', title: 'Help', items: [
		                    { id: 'help', title: app.config.site.name + ' Help', keyboard: 'shift ?' }
		                ] },
		                { id: 'debugMenu', title: 'Debug', items: [
		                    { id: 'query', title: 'Show Query' }
		                ] },
		                { id: 'testMenu', title: 'Test', items: [
		                    { group: 'foogroup', items: [
		                        { id: 'item1', title: 'Item 1' },
		                        { id: 'item2', title: 'Item 2' }
		                    ] }
		                ] }
		            ]
		        })
		        .bindEvent({
		            change: function(event, data) {
		                if (data.id == 'find') {
		                    var id = data.checked[0].id;
		                    app.$ui.findSelect.selectItem(id);
		                } else if (data.id == 'movieview') {
		                    var view = data.checked[0].id;
		                    var id = document.location.pathname.split('/')[1];
		                    if (view == 'info')
		                        url(id + '/info');
		                    else
		                        url(id);
		                } else if (data.id == 'ordermovies') {
		                    var id = data.checked[0].id;
		                    app.$ui.list.sortList(app.user.ui.sort[0].key, id == 'ascending' ? '' : '-');
		                } else if (data.id == 'sortmovies') {
		                    var id = data.checked[0].id,
		                        operator = Ox.getObjectById(app.config.sortKeys, id).operator;
		                    app.$ui.mainMenu.checkItem('sortMenu_ordermovies_' + (operator === '' ? 'ascending' : 'descending'));
		                    app.$ui.list.sortList(id, operator);
		                } else if (data.id == 'viewmovies') {
		                    var view = data.checked[0].id;
		                    url('#view=' + view);
		                }
		            },
		            click: function(event, data) {
		                if (data.id == 'about') {
		                    var $dialog = new Ox.Dialog({
		                        buttons: [
		                            {
		                                click: function() {
		                                    $dialog.close();
		                                },
		                                id: 'close',
		                                title: 'Close'
		                            }
		                        ],
		                        id: 'about',
		                        title: 'About'
		                    }).open();
		                } else if (data.id == 'home') {
		                    var $dialog = new Ox.Dialog({
		                        buttons: [
		                            {
		                                click: function() {
		                                    $dialog.close();
		                                },
		                                id: 'close',
		                                title: 'Close'
		                            }
		                        ],
		                        height: 498,
		                        id: 'home',
		                        title: app.config.site.name,
		                        width: 800
		                    }).open();
		                } else if (data.id == 'register') {
		                    app.$ui.accountDialog = ui.accountDialog('register').open();
		                } else if (data.id == 'loginlogout') {
		                    app.$ui.accountDialog = (app.user.group == 'guest' ?
		                        ui.accountDialog('login') : ui.accountLogoutDialog()).open();
		                } else if (data.id == 'places') {
		                    var $manage = new Ox.SplitPanel({
		                            elements: [
		                                {
		                                    collapsible: true,
		                                    element: new Ox.SplitPanel({
		                                        elements: [
		                                            {
		                                                element: new Ox.Toolbar({
		                                                    orientation: 'horizontal',
		                                                    size: 44
		                                                }).append(
		                                                    app.$ui.findPlacesElement = new Ox.FormElementGroup({
		                                                        elements: [
		                                                            app.$ui.findPlacesSelect = new Ox.Select({
		                                                                    id: 'findPlacesSelect',
		                                                                    items: [
		                                                                        { id: 'name', title: 'Find: Name' },
		                                                                        { id: 'region', title: 'Find: Region' },
		                                                                        { id: 'user', title: 'Find: User' }
		                                                                    ],
		                                                                    overlap: 'right',
		                                                                    type: 'image'
		                                                                })
		                                                                .bindEvent({
		                                                                    change: function(event, data) {
		                                                                        app.$ui.findPlacesSelect.loseFocus();
		                                                                        app.$ui.findPlacesInput.options({
		                                                                            placeholder: data.selected[0].title
		                                                                        });
		                                                                    }
		                                                                }),
		                                                            app.$ui.findPlacesInput = new Ox.Input({
		                                                                clear: true,
		                                                                id: 'findPlacesInput',
		                                                                placeholder: 'Find: Name',
		                                                                width: 234
		                                                            })
		                                                        ],
		                                                        id: 'findPlacesElement'
		                                                    }) 
		                                                    .css({
		                                                        float: 'left',
		                                                        margin: '4px'
		                                                    })
		                                                ).append(
		                                                    app.$ui.sortPlacesSelect = new Ox.Select({
		                                                        id: 'sortPlacesSelect',
		                                                        items: [
		                                                            { id: 'name', title: 'Sort by Name', checked: true },
		                                                            { id: 'region', title: 'Sort by Region' },
		                                                            { id: 'size', title: 'Sort by Size' },
		                                                            { id: 'latitude', title: 'Sort by Latitude' },
		                                                            { id: 'longitude', title: 'Sort by Longitude' },
		                                                            { id: 'clips', title: 'Sort by Number of Clips' },
		                                                            { id: 'user', title: 'Sort by User' },
		                                                            { id: 'datecreated', title: 'Sort by Date Added' },
		                                                            { id: 'datemodified', title: 'Sort by Date Modified' }
		                                                        ],
		                                                        width: 246
		                                                    })
		                                                    .css({
		                                                        float: 'left',
		                                                        margin: '0 4px 4px 4px'
		                                                    })
		                                                ),
		                                                size: 44
		                                            },
		                                            {
		                                                element: new Ox.Element('div')
		                                            },
		                                            {
		                                                element: new Ox.Toolbar({
		                                                    orientation: 'horizontal',
		                                                    size: 16
		                                                }),
		                                                size: 16
		                                            }
		                                        ],
		                                        orientation: 'vertical'
		                                    }),
		                                    size: 256
		                                },
		                                {
		                                    element: new Ox.SplitPanel({
		                                        elements: [
		                                            {
		                                                element: new Ox.Toolbar({
		                                                    orientation: 'horizontal',
		                                                    size: 24
		                                                }).append(
		                                                    app.$ui.labelsButton = new Ox.Button({
		                                                            id: 'labelsButton',
		                                                            title: [
		                                                                {id: 'show', title: 'Show Labels'},
		                                                                {id: 'hide', title: 'Hide Labels'}
		                                                            ],
		                                                            width: 96
		                                                        })
		                                                        .css({
		                                                            float: 'left',
		                                                            margin: '4px'
		                                                        })
		                                                ).append(
		                                                    app.$ui.findMapInput = new Ox.Input({
		                                                        clear: true,
		                                                        id: 'findMapInput',
		                                                        placeholder: 'Find on Map',
		                                                        width: 192
		                                                    })
		                                                    .css({
		                                                        float: 'right',
		                                                        margin: '4px'
		                                                    })
															.bindEvent({
																submit: function(event, data) {
																	app.$ui.map.find(data.value, function(location) {
																		/*
																		app.$ui.placeNameInput.options({
																			disabled: false,
																			value: location.name
																		});
																		app.$ui.placeAliasesInput.options({
																			disabled: false
																		});
																		app.$ui.placeGeonameLabel.options({
																			disabled: false,
																			title: location.names.join(', ')
																		});
																		app.$ui.removePlaceButton.options({
																			disabled: false
																		});
																		app.$ui.addPlaceButton.options({
																			disabled: false
																		});
																		*/
																	});
																}
															})
		                                                ),
		                                                size: 24
		                                            },
		                                            {
		                                                element: app.$ui.map = new Ox.Map({
		                                                    	places: ['Boston', 'Brussels', 'Barcelona', 'Berlin', 'Beirut', 'Bombay', 'Bangalore', 'Beijing']
		                                                	})
															.css({
		                                                    	left: 0,
		                                                    	top: 0,
		                                                    	right: 0,
		                                                    	bottom: 0
		                                                	})
															.bindEvent({
																select: function(event, location) {
																	app.$ui.placeNameInput.options({
																		disabled: false,
																		value: location.name
																	});
																	app.$ui.placeAliasesInput.options({
																		disabled: false
																	});
																	app.$ui.placeGeonameLabel.options({
																		disabled: false,
																		title: location.names.join(', ')
																	});
																	app.$ui.removePlaceButton.options({
																		disabled: false
																	});
																	app.$ui.addPlaceButton.options({
																		disabled: false
																	});
																}
															})
		                                            },
		                                            {
		                                                element: app.$ui.bottomBar = new Ox.Toolbar({
		                                                    orientation: 'horizontal',
		                                                    size: 24
		                                                })
														.append(
															app.$ui.placeNameInput = new Ox.Input({
																disabled: true,
																id: 'placeName',
																placeholder: 'Name',
																width: 128
															})
															.css({
																float: 'left',
																margin: '4px 0 0 4px'
															})
														)
														.append(
															app.$ui.placeAliasesInput = new Ox.Input({
																disabled: true,
																id: 'aliases',
																placeholder: 'Aliases',
																width: 128
															})
															.css({
																float: 'left',
																margin: '4px 0 0 4px'
															})
														)
														.append(
															app.$ui.placeGeonameLabel = new Ox.Label({
																disabled: true,
																id: 'placeGeoname',
																title: 'Geoname',
																width: parseInt(app.$ui.document.width() * 0.8) - 256 - 256 - 32 - 24
															})
															.css({
		                                                        float: 'left',
		                                                        margin: '4px 0 0 4px'
		                                                    })
														)
														.append(
		                                                    app.$ui.addPlaceButton = new Ox.Button({
																disabled: true,
		                                                        id: 'addPlaceButton',
		                                                        title: 'add',
																type: 'image'
		                                                    })
		                                                    .css({
		                                                        float: 'right',
		                                                        margin: '4px 4px 0 0'
		                                                    })
		                                                )
														.append(
		                                                    app.$ui.removePlaceButton = new Ox.Button({
																disabled: true,
		                                                        id: 'removePlaceButton',
		                                                        title: 'remove',
																type: 'image'
		                                                    })
		                                                    .css({
		                                                        float: 'right',
		                                                        margin: '4px 4px 0 0'
		                                                    })
		                                                ),
		                                                size: 24
		                                            }
		                                        ],
		                                        orientation: 'vertical'
		                                    })
		                                }
		                            ],
		                            orientation: 'horizontal'
		                        }).css({
		                            top: '24px',
		                            bottom: '24px',
		                        }),
		                        $dialog = new Ox.Dialog({
		                            buttons: [
		                                {
		                                    click: function() {
		                                        $dialog.close();
		                                    },
		                                    id: 'close',
		                                    title: 'Close',
		                                    value: 'Close'
		                                }
		                            ],
		                            height: parseInt(app.$ui.document.height() * 0.8),
		                            id: 'places',
		                            minHeight: 400,
		                            minWidth: 600,
		                            padding: 0,
		                            title: 'Manage Places',
		                            width: parseInt(app.$ui.document.width() * 0.8)
		                        }).css({
		                            overflow: 'hidden'
		                        }).append($manage).open();
		                }
		            }
		        });
			return that;
		},
		mainPanel: function() {
			var that = new Ox.SplitPanel({
                elements: [
                    {
                        collapsible: true,
                        element: app.$ui.leftPanel = ui.leftPanel(),
                        resizable: true,
                        resize: [128, 256, 384, 512],
                        size: app.user.ui.listsSize
                    },
                    {
                        element: app.$ui.rightPanel = ui.rightPanel()
                    }
                ],
                orientation: 'horizontal'
            });
			return that;
		},
		rightPanel: function() {
			var that = new Ox.SplitPanel({
                elements: [
                    {
                        element: app.$ui.toolbar = ui.toolbar('list').css({ zIndex: 2 }), // fixme: remove later
                        size: 24
                    },
                    {
                        element: app.$ui.contentPanel = ui.contentPanel()
                    },
                    {
                        element: app.$ui.statusbar = ui.statusbar(),
                        size: 16
                    }
                ],
                id: 'rightPanel',
                orientation: 'vertical'
            })
            .bindEvent('resize', function(event, data) {
                if (!app.user.ui.item) {
                    resizeGroups(data);
                    app.$ui.list.size();
                    if (app.user.ui.listView == 'map') {
                        app.$ui.map.triggerResize();
                    }
                } else {
                    if (app.user.ui.itemView == 'timeline') {
                        app.$ui.editor.options({
    						width: data
    					});
    				}
                }
            });
			return that;
		},
		sectionbar: function(mode) {
			var that = new Ox.Bar({
		            size: 24
		        })
		        .append(
		            mode == 'buttons' ?
		            app.$ui.sectionButtons = ui.sectionButtons() :
		            app.$ui.sectionSelect = ui.sectionSelect()
		        );
			that.toggle = function() {
				
			};
			return that;
		},
		sectionButtons: function() {
		    var that = new Ox.ButtonGroup({
                    buttons: [
    					{id: 'site', selected: app.user.ui.section == 'site', title: app.config.site.name},
                        {id: 'items', selected: app.user.ui.section == 'items', title: app.config.itemName.plural},
                        {id: 'texts', selected: app.user.ui.section == 'texts', title: 'Texts'},
    					{id: 'admin', selected: app.user.ui.section == 'admin', title: 'Admin'}
                    ],
                    id: 'sectionButtons',
                    selectable: true
                }).css({
	                float: 'left',
                    margin: '4px'
	            });
	        return that;
		},
		sections: function() {
			var that = new Ox.Element();
			var $sections = [];
		    $.each(app.user.ui.sections, function(i, id) {
		        var menu = [];
		        if (id == 'lists') {
		            menu = [
		                { id: 'new', title: 'New List...' },
		                { id: 'newfromselection', title: 'New List from Selection...' },
		                { id: 'newsmart', title: 'New Smart List...' },
		                { id: 'newfromresults', title: 'New Smart List from Results...' },
		                {},
		                { id: 'addselection', title: 'Add Selection to List...' }
		            ];
		        } else if (id == 'public') {
		            menu = [
		                { id: 'browse', title: 'More Public Lists...' },
		            ];
		        }
		        var $section = new Ox.CollapsePanel({
		            id: id,
		            menu: menu,
		            size: 16,
		            title: Ox.getObjectById(app.config.sections, id).title
		        });
		        $sections.push($section);
		        $section.$content.append(
		            $('<div>').css({ height: '20px' }).append(
		                $('<div>').css({ float: 'left', width: '16px', height: '16px', margin: '1px'}).append(
		                    $('<img>').attr({ src: 'static/oxjs/build/png/ox.ui.modern/iconFind.png' }).css({ width: '16px', height: '16px', border: 0, background: 'rgb(64, 64, 64)', WebkitBorderRadius: '2px' })
		                )
		            ).append(
		                $('<div>').css({ float: 'left', width: '122px', height: '14px', margin: '2px' }).html('Foo')
		            ).append(
		                $('<div>').css({ float: 'left', width: '40px', height: '14px', margin: '2px', textAlign: 'right' }).html('23')
		            )
		        );
		    });
		    $.each($sections, function(i, $section) {
		        that.append($section);
		    });
			that.toggle = function() {
				
			}
			return that;
		},
        sectionSelect: function() {
		    var that = new Ox.Select({
		            id: 'sectionSelect',
                    items: [
    					{checked: app.user.ui.section == 'site', id: 'site', title: app.config.site.name},
                        {checked: app.user.ui.section == 'items', id: 'items', title: app.config.itemName.plural},
                        {checked: app.user.ui.section == 'texts', id: 'texts', title: 'Texts'},
    					{checked: app.user.ui.section == 'admin', id: 'admin', title: 'Admin'}
                    ]
                }).css({
	                float: 'left',
                    margin: '4px'
	            });
	        return that;
		},
		status: function(key, data) {
			var that = Ox.toTitleCase(key) + ': ' + [
		        Ox.formatNumber(data.items) + ' movie' + (data.items != 1 ? 's' : ''),
		        Ox.formatDuration(data.runtime, 'medium'),
		        data.files + ' file' + (data.files != 1 ? 's' : ''),
		        Ox.formatDuration(data.duration, 'short'),
		        Ox.formatValue(data.size, 'B'),
		        Ox.formatValue(data.pixels, 'px')
		    ].join(', ');
			return that;
		},
		statusbar: function() {
			var that = new Ox.Bar({
		            size: 16
		        })
		        .css({
		            textAlign: 'center'
		        })
		        .append(
		            new Ox.Element()
		                .css({
		                    marginTop: '2px',
		                    fontSize: '9px'
		                })
		                .append(
		                    app.$ui.total = new Ox.Element('span')
		                )
		                .append(
		                    new Ox.Element('span').html(' &mdash; ')
		                )
		                .append(
		                    app.$ui.selected = new Ox.Element('span')
		                )
		        );
		    return that;
		},
		toolbar: function() {
			var that = new Ox.Bar({
			            size: 24
			        })
			        .append(
			            app.$ui.viewSelect = ui.viewSelect() 
			        )
			        .append(
			            app.$ui.findElement = ui.findElement() 
			        );
			that.toggle = function() {
				
			}
			return that;
		},
		viewSelect: function() {
			var that = new Ox.Select({
                    id: 'viewSelect',
                    items: $.map(app.config.listViews, function(view, i) {
                        return $.extend($.extend({}, view), {
                            checked: app.user.ui.listView == view.id,
                            title: 'View ' + view.title
                        });
                    }),
                    width: 144
                })
                .css({
                    float: 'left',
                    margin: '4px'
                })
                .bindEvent('change', function(event, data) {
                    var id = data.selected[0].id;
                    app.user.ui.listView = id;
                    app.$ui.mainMenu.checkItem('viewMenu_movies_' + id);
                    app.$ui.contentPanel.replace(1, app.$ui.list = ui.list(id));
                });
			return that;
		}
	}

	function getGroupWidth(pos, panelWidth) {
	    var width = {};
	    width.list = Math.floor(panelWidth / 5) + (panelWidth % 5 > pos);
	    width.column = width.list - 40 - ($.browser.mozilla ? 16 : 12);
	    return width;
	}

    function resizeGroups(width) {
        var widths = $.map(app.ui.groups, function(v, i) {
            return getGroupWidth(i, width);
        });
        Ox.print('widths', widths);
        app.$ui.browser.size(0, widths[0].list).size(2, widths[4].list);
        app.$ui.groupsInnerPanel.size(0, widths[1].list).size(2, widths[3].list);
        $.each(app.$ui.groups, function(i, list) {
            list.resizeColumn('name', widths[i].column);
        });
    }

	var Query = (function() {

	    function constructFind(query) {
	        Ox.print('cF', query)
	        return /*encodeURI(*/$.map(query.conditions, function(v, i) {
	            if (!Ox.isUndefined(v.conditions)) {
	                return '[' + constructFind(v) + ']';
	            } else {
	                return v.value !== '' ? v.key + (v.key ? ':' : '') + constructValue(v.value, v.operator) : null;
	            }
	        }).join(query.operator)/*)*/;
	    }

	    function constructValue(value, operator) {
	        operator = operator.replace('=', '^$');
	        if (operator.indexOf('$') > -1) {
	            value = operator.substr(0, operator.length - 1) + value + '$'
	        } else {
	            value = operator + value;
	        }
	        return value;
	    }

	    function mergeFind() {
	    }

	    function parseFind(str) {
	        var find = {
	                conditions: [],
	                operator: ''
	            },
	            subconditions = str.match(/\[.*?\]/g) || [];
	        $.each(subconditions, function(i, v) {
	            subconditions[i] = v.substr(1, v.length - 2);
	            str = str.replace(v, '[' + i + ']');
	        });
	        if (str.indexOf(',') > -1) {
	            find.operator = '&';
	        } else if (str.indexOf('|') > -1) {
	            find.operator = '|';
	        }
	        Ox.print('pF', str, find.operator)
	        find.conditions = $.map(find.operator === '' ? [str] : str.split(find.operator == '&' ? ',' : '|'), function(v, i) {
	            Ox.print('v', v)
	            var ret, kv;
	            if (v[0] == '[') {
	                Ox.print('recursion', subconditions)
	                ret = parseFind(subconditions[parseInt(v.substr(1, v.length - 2))]);
	            } else {
	                kv = ((v.indexOf(':') > - 1 ? '' : ':') + v).split(':');
	                ret = $.extend({
	                    key: kv[0]
	                }, parseValue(kv[1]));
	            }
	            return ret;
	        });
	        return find;
	    }

	    function parseValue(str) {
	        var value = {
	                value: decodeURI(str),
	                operator: ''
	            };
	        if (value.value[0] == '!') {
	            value.operator = '!'
	            value.value = value.value.substr(1);
	        }
	        if ('^<>'.indexOf(value.value[0]) > -1) {
	            value.operator += value.value[0];
	            value.value = value.value.substr(1);
	        }
	        if (value.value.substr(-1) == '$') {
	            value.operator += '$';
	            value.value = value.value.substr(0, value.value.length - 1);
	        }
	        value.operator = value.operator.replace('^$', '=');
	        return value;
	    }

	    return {

	        fromString: function(str) {
	            var query = Ox.unserialize(str),
	                sort = [];
	            if ('find' in query) {
	                app.user.ui.findQuery = parseFind(query.find);
	                Ox.print('user.ui.findQuery', app.user.ui.findQuery)
	            }
	            if ('sort' in query) {
	                sort = query.sort.split(',')
	                app.user.ui.sort = $.map(query.sort.split(','), function(v, i) {
	                    var hasOperator = '+-'.indexOf(v[0]) > -1,
	                        key = hasOperator ? query.sort.substr(1) : query.sort,
	                        operator = hasOperator ? v[0].replace('+', '') : Ox.getObjectById(app.config.sortKeys, key).operator;
	                    return {
	                        key: key,
	                        operator: operator
	                    };
	                });
	            }
	            if ('view' in query) {
	                app.user.ui.listView = query.view;
	            }
	        },

	        toObject: function(groupId) {
	            Ox.print('tO', app.user.ui.findQuery.conditions)
	            // the inner $.merge() creates a clone
	            var conditions = $.merge(
	                    $.merge([], app.user.ui.listQuery.conditions),
	                    app.user.ui.findQuery.conditions
	                ),
	                operator;
	            $.merge(conditions, app.ui.groups ? $.map(app.ui.groups, function(v, i) {
                    if (v.id != groupId && v.query.conditions.length) {
                        return v.query.conditions.length == 1 ?
                            v.query.conditions : v.query;
                    }
                }) : []);
	            operator = conditions.length < 2 ? '' : ','; // fixme: should be &
	            Ox.print('>>', groupId, app.user.ui.find, conditions);
	            return {
	                conditions: conditions,
	                operator: operator
	            };
	        },

	        toString: function() {
	            Ox.print('tS', app.user.ui.find)
	            return Ox.serialize({
	                find: constructFind(Query.toObject()),
	                sort: app.user.ui.sort[0].operator + app.user.ui.sort[0].key,
	                view: app.user.ui.listView
	            });
	        }

	    };

	})();

    var URL = (function() {

        var old = {
                user: {
                    ui: {}
                }
            },
            regexps = {
    			'^(|about|faq|home|news|software|terms|tour)$': function(url) {
			        app.user.ui.section = 'site';
			        app.user.ui.sitePage = url;
    			},
    			'^(|find)$': function() {
			        app.user.ui.section = 'items';
			        app.user.ui.item = '';
    			},
    			'^(calendar|calendars|clips|icons|flow|map|maps|timelines)$': function() {
			        app.user.ui.section = 'items';
			        app.user.ui.item = '';
			        app.user.ui.listView = url;
    			},
                '^[0-9A-Z]': function() {
    				var split = url.split('/'),
					    item = split[0],
		        	    view = new RegExp(
		        	        '^(calendar|clips|files|info|map|player|statistics|timeline)$'
		        	    )(split[1]) || app.user.ui.itemView;
    				app.user.ui.section = 'items';
    				app.user.ui.item = item;
    				app.user.ui.itemView = view;
    			},
    			'^texts$': function() {
    			    app.user.ui.section = 'texts';
    			},
    			'^admin$': function() {
    			    app.user.ui.section = 'admin';
    			}
            };

        return {

            set: function(url) {
                history.pushState({}, '', '/' + url);
                old.user.ui = $.extend({}, app.user.ui); // make a clone
                URL.update();
            },

            parse: function() {
                url = document.location.pathname.substr(1) + document.location.hash;
                $.each(regexps, function(re, fn) {
                    Ox.print(url, 're', re)
                    re = new RegExp(re);
        			if (re(url)) {
        				fn(url);
        				return false;
        			}
                });
            },

            update: function() {
                URL.parse();
                if (!old.user.ui.item && app.user.ui.item) {
                    ui.item(app.user.ui.item, app.user.ui.itemView).display();
                } else if (old.user.ui.item && !app.user.ui.item) {
                    ui.list(app.user.ui.listView).display();
                }
            }

        }

    }());

	var url = function(url) {
		var currentURL = document.location.pathname.substr(1) + document.location.hash,
			match = false;
			regexps = {
				'^[0-9A-Z]': function() {
					var split = url.split('/'),
						id = split[0],
			        	view = split[1] || app.user.ui.itemView;
			        ui.item(id, view);
				}
			};
	    if (!url)
	        url = currentURL;
	    else {
	        if (url != currentURL) {
	            var title = document.title + ' ' + url;
	            history.pushState({}, title, url);
	        } else {
	            //FIXME: this is a workaround since open gets called twice
	            //       due to a bug with double click
	            Ox.print('ignore double call of app.url, double click need to be fixed');
	            return;
	        }
	    }
		$.each(regexps, function(re, fn) {
			re = new RegExp(re);
			if (re(url)) {
				fn();
				match = true;
				return false;
			}
		});
		if (!match) {
			Query.fromString(location.hash.substr(1));
	        app.$ui.rightPanel.replace(1, ui.contentPanel());
		}
	}

	load();

});



// Objects

/*
    // Menu


    Ox.Event.bind('change_viewmovies', function(event, data) {
        app.$ui.viewSelect.selectItem(data.id);
    });
    Ox.Event.bind('change_find', function(event, data) {
        app.$ui.findInput.changeLabel(data.id);
    });
    Ox.Event.bind('click_query', function(event, data) {
        var $dialog = new Ox.Dialog({
            buttons: [
                {
                    click: function() {
                        $dialog.close();
                    },
                    id: 'close',
                    title: 'Close',
                    value: 'Close'
                }
            ],
            id: 'query',
            title: 'Query'
        }).append(Query.toString() + '<br/><br/>' + JSON.stringify(Query.toObject())).open();
    });

    // Resize

    Ox.Event.bind('click_show_query', function(event, data) {
        var query = constructQuery(),
            html = 'Conditions<br/><br/>' + $.map(query.conditions, function(v) {
                return v.key + ' ' + v.operator + ' ' + v.value;
            }).join('<br/>') + '<br/><br/>Operator: ' + query.operator,
            $dialog = new Ox.Dialog({
                buttons: [
                    {
                        value: 'Close',
                        click: function() {
                            $dialog.close();
                        }
                    }
                ],
                title: 'Show Query'
            })
            .append(html)
            .open();
    });

// Functions
*/

/*

app.constructAnnotations = function() {
    var $annotations = new Ox.Element();
    $.each(app.constructBins(), function(i, $bin) {
        $annotations.append($bin);
    });
    return $annotations;
}

app.constructApp = function() {
    return new Ox.SplitPanel({
        elements: [
            {
                element: app.$ui.mainMenu,
                size: 20
            },
            {
                element: app.$ui.mainPanel = new Ox.SplitPanel({
                    elements: [
                        {
                            collapsible: true,
                            element: app.$ui.leftPanel = new Ox.SplitPanel({
                                    elements: [
										{
											element: app.$ui.sectionbar,
											size: 24
										},
                                        {
                                            element: app.$ui.lists.options({
                                                id: 'listsPanel'
                                            })
                                        },
                                        {
                                            collapsible: true,
                                            element: app.$ui.info.options({
                                                id: 'infoPanel'
                                            }),
                                            size: app.user.ui.listsSize / app.ui.infoRatio + 16
                                        }
                                    ],
                                    id: 'leftPanel',
                                    orientation: 'vertical'
                                })
                                .bindEvent('resize', function(event, data) {
                                    Ox.print('resize', data, data / app.ui.infoRatio + 16);
                                    app.$ui.leftPanel.size('infoPanel', Math.round(data / app.ui.infoRatio) + 16);
                                }),
                            resizable: true,
                            resize: [128, 256, 384, 512],
                            size: app.user.ui.listsSize
                        },
                        {
                            element: app.$ui.rightPanel = new Ox.SplitPanel({
                                elements: [
                                    {
                                        element: app.$ui.toolbar.css({ zIndex: 2 }), // fixme: remove later
                                        size: 24
                                    },
                                    {
                                        element: app.constructContentPanel()
                                    },
                                    {
                                        element: app.$ui.statusbar,
                                        size: 16
                                    }
                                ],
                                id: 'rightPanel',
                                orientation: 'vertical'
                            })
                            .bindEvent('resize', function(event, data) {
                                var widths = $.map(app.ui.groups, function(v, i) {
                                    return app.getGroupWidth(i, data);
                                });
                                Ox.print('widths', widths);
                                app.$ui.groupsOuterPanel.size(0, widths[0].list).size(2, widths[4].list);
                                app.$ui.groupsInnerPanel.size(0, widths[1].list).size(2, widths[3].list);
                                $.each(app.$ui.groups, function(i, list) {
                                    list.resizeColumn('name', widths[i].column);
                                });
                                app.$ui.list.size();
                            })
                        }
                    ],
                    orientation: 'horizontal'
                })
            }
        ],
        orientation: 'vertical'
    });
}

app.constructBins = function() {
    var $bins = [];
    $.each(app.config.layers, function(i, layer) {
        var $bin = new Ox.CollapsePanel({
            id: layer.id,
            size: 16,
            title: layer.title
        });
        $bins.push($bin);
        $bin.$content.append(
            $('<div>').css({ height: '20px' }).append(
                $('<div>').css({ float: 'left', width: '16px', height: '16px', margin: '1px'}).append(
                    $('<img>').attr({ src: 'static/oxjs/build/png/ox.ui.modern/iconFind.png' }).css({ width: '16px', height: '16px', border: 0, background: 'rgb(64, 64, 64)', WebkitBorderRadius: '2px' })
                )
            ).append(
                $('<div>').css({ float: 'left', width: '122px', height: '14px', margin: '2px' }).html('Foo')
            ).append(
                $('<div>').css({ float: 'left', width: '40px', height: '14px', margin: '2px', textAlign: 'right' }).html('23')
            )
        );
    });
    return $bins;	
}

app.constructGroups = function() {
    var $groups = [],
        panelWidth = app.$document.width() - app.user.ui.listsSize - 1;
    app.ui.groups = $.map(app.config.groups, function(id, i) {
        var title = Ox.getObjectById(app.config.sortKeys, id).title,
            width = app.getGroupWidth(i, panelWidth);
        return {
            id: id,
            element: $groups[i] = new Ox.TextList({
                    columns: [
                        {
                            align: 'left',
                            id: 'name',
                            operator: id == 'year' ? '-' : '+',
                            title: title,
                            unique: true,
                            visible: true,
                            width: width.column
                        },
                        {
                            align: 'right',
                            id: 'items',
                            operator: '-',
                            title: '#',
                            visible: true,
                            width: 40
                        }
                    ],
                    id: 'group_' + id,
                    request: function(data, callback) {
                        Ox.print('sending request', data)
                        delete data.keys;
                        return pandora.api.find($.extend(data, {
                            group: id,
                            query: app.Query.toObject()
                        }), callback);
                    },
                    sort: [
                        {
                            key: id == 'year' ? 'name' : 'items',
                            operator: '-'
                        }
                    ]
                })
                .bindEvent('select', function(event, data) {
                    Ox.print('select', i)
                    var group = app.ui.groups[i],
                        query;
                    app.ui.groups[i].query.conditions = $.map(data.ids, function(v) {
                        return {
                            key: group.id,
                            value: v,
                            operator: '='
                        };
                    });
                    query = app.Query.toObject();
                    app.$ui.list.options({
                        request: function(data, callback) {
                            return pandora.api.find($.extend(data, {
                                query: query
                            }), callback);
                        }
                    });
                    $.each(app.ui.groups, function(i_, group_) {
                        if (i_ != i) {
                            Ox.print('setting groups request', i, i_)
                            app.$ui.groups[i_].options({
                                request: function(data, callback) {
                                    delete data.keys;
                                    return pandora.api.find($.extend(data, {
                                        group: group_.id,
                                        query: app.Query.toObject(group_.id)
                                    }), callback);
                                }
                            });
                        }
                    });
                    history.pushState({}, '', '/#' + app.Query.toString(query));
                }),
            query: {
                conditions: [],
                operator: '|'
            },
            size: width.list,
            title: title
        };
        Ox.print('--OK--');
    });
    return $groups;
}

app.constructInfo = function() {
    return new Ox.Element()
        .append(
            app.$ui.infoStill = new Ox.Element('img')
                .css({
                    position: 'absolute',
                    left: 0,
                    top: 0
                })
        )
        .append(
            app.$ui.infoTimeline = new Ox.Element('img')
                .css({
                    position: 'absolute',
                    left: 0,
                    bottom: 0,
                    height: '16px',
                })
        );
}

app.constructContentPanel = function(listView) {
    listView = listView || app.user.ui.listView;
    return app.$ui.contentPanel = new Ox.SplitPanel({
        elements: [
            {
                collapsible: true,
                element: app.$ui.groupsOuterPanel = new Ox.SplitPanel({
                    elements: [
                        {
                            element: app.$ui.groups[0],
                            size: app.ui.groups[0].size
                        },
                        {
                            element: app.$ui.groupsInnerPanel = new Ox.SplitPanel({
                                elements: [
                                    {
                                        element: app.$ui.groups[1],
                                        size: app.ui.groups[1].size
                                    },
                                    {
                                        element: app.$ui.groups[2],
                                    },
                                    {
                                        element: app.$ui.groups[3],
                                        size: app.ui.groups[3].size
                                    }
                                ],
                                orientation: 'horizontal'
                            })
                        },
                        {
                            element: app.$ui.groups[4],
                            size: app.ui.groups[4].size
                        },
                    ],
                    id: 'browser',
                    orientation: 'horizontal'
                })
                .bindEvent('resize', function(event, data) {
                    Ox.print('resizing groups...')
                    $.each(app.$ui.groups, function(i, list) {
                        list.size();
                    });
                }),
                resizable: true,
                resize: [96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256],
                size: app.user.ui.groupsSize
            },
            {
                element: app.$ui.list = app.constructList(listView)
            }
        ],
        orientation: 'vertical'
    });
}

app.constructItem = function(id, view) {

}

app.constructList = function(view) {


}

app.constructLists = function() {
    var $lists = new Ox.Element;
    $.each(app.$ui.sections, function(i, $section) {
        $lists.append($section);
    });
    return $lists;
}

app.constructMainMenu = function() {
    return new Ox.MainMenu({
            extras: [
                app.$ui.loadingIcon = new Ox.LoadingIcon({
                    size: 'medium'
                })
            ],
            id: 'mainMenu',
            menus: [
                { id: app.config.site.id + 'Menu', title: app.config.site.name, items: [
                    { id: 'about', title: 'About' },
                    {},
                    { id: 'home', title: 'Home Screen' },
                    { id: 'faq', title: 'Frequently Asked Questions' },
                    { id: 'tos', title: 'Terms of Service' },
                    {},
                    { id: 'contact', title: 'Contact...' }
                ] },
                { id: 'userMenu', title: 'User', items: [
                    { id: 'username', title: 'User: not logged in', disabled: true },
                    {},
                    { id: 'preferences', title: 'Preferences...', disabled: true, keyboard: 'control ,' },
                    {},
                    { id: 'register', title: 'Create an Account...' },
                    { id: 'loginlogout', title: ['Login...', 'Logout...'] }
                ] },
                { id: 'listMenu', title: 'List', items: [
                    { id: 'history', title: 'History', items: [
                        { id: 'allmovies', title: 'All Movies' }
                    ] },
                    { id: 'lists', title: 'View List', items: [
                        { id: 'favorites', title: 'Favorites' }
                    ] },
                    { id: 'features', title: 'View Feature', items: [
                        { id: 'situationistfilm', title: 'Situationist Film' },
                        { id: 'timelines', title: 'Timelines' }
                    ] },
                    {},
                    { id: 'newlist', title: 'New List...', keyboard: 'control n' },
                    { id: 'newlistfromselection', title: 'New List from Selection...', disabled: true, keyboard: 'shift control n' },
                    { id: 'newsmartlist', title: 'New Smart List...', keyboard: 'alt control n' },
                    { id: 'newsmartlistfromresults', title: 'New Smart List from Results...', keyboard: 'shift alt control n' },
                    {},
                    { id: 'addmovietolist', title: ['Add Selected Movie to List...', 'Add Selected Movies to List...'], disabled: true },
                    {},
                    { id: 'setposterframe', title: 'Set Poster Frame', disabled: true }
                ]},
                { id: 'editMenu', title: 'Edit', items: [
                    { id: 'undo', title: 'Undo', disabled: true, keyboard: 'control z' },
                    { id: 'redo', title: 'Redo', disabled: true, keyboard: 'shift control z' },
                    {},
                    { id: 'cut', title: 'Cut', disabled: true, keyboard: 'control x' },
                    { id: 'copy', title: 'Copy', disabled: true, keyboard: 'control c' },
                    { id: 'paste', title: 'Paste', disabled: true, keyboard: 'control v' },
                    { id: 'delete', title: 'Delete', disabled: true, keyboard: 'delete' },
                    {},
                    { id: 'selectall', title: 'Select All', disabled: true, keyboard: 'control a' },
                    { id: 'selectnone', title: 'Select None', disabled: true, keyboard: 'shift control a' },
                    { id: 'invertselection', title: 'Invert Selection', disabled: true, keyboard: 'alt control a' }
                ] },
                { id: 'viewMenu', title: 'View', items: [
                    { id: 'movies', title: 'View Movies', items: [
                        { group: 'viewmovies', min: 0, max: 1, items: $.map(app.config.listViews, function(view, i) {
                            return $.extend({
                                checked: app.user.ui.listView == view.id,
                            }, view);
                        }) },
                    ]},
                    { id: 'icons', title: 'Icons', items: [
                        { id: 'poster', title: 'Poster' },
                        { id: 'still', title: 'Still' },
                        { id: 'timeline', title: 'Timeline' }
                    ] },
                    { id: 'info', title: 'Info', items: [
                        { id: 'poster', title: 'Poster' },
                        { id: 'video', title: 'Video' }
                    ] },
                    {},
                    { id: 'openmovie', title: ['Open Movie', 'Open Movies'], disabled: true, items: [
                        { group: 'movieview', min: 0, max: 1, items: $.map(app.config.itemViews, function(view, i) {
                            return $.extend({
                                checked: app.user.ui.itemView == view.id,
                            }, view);
                        }) },
                    ]},
                    {},
                    { id: 'lists', title: 'Hide Lists', keyboard: 'shift l' },
                    { id: 'info', title: 'Hide Info', keyboard: 'shift i' },
                    { id: 'groups', title: 'Hide Groups', keyboard: 'shift g' },
                    { id: 'movies', title: 'Hide Movies', disabled: true, keyboard: 'shift m' }
                ]},
                { id: 'sortMenu', title: 'Sort', items: [
                    { id: 'sortmovies', title: 'Sort Movies by', items: [
                        { group: 'sortmovies', min: 1, max: 1, items: $.map(app.config.sortKeys, function(key, i) {
                            return $.extend({
                                checked: app.user.ui.sort[0].key == key.id,
                            }, key);
                        }) }
                    ] },
                    { id: 'ordermovies', title: 'Order Movies', items: [
                        { group: 'ordermovies', min: 1, max: 1, items: [
                            { id: 'ascending', title: 'Ascending', checked: app.user.ui.sort[0].operator === '' },
                            { id: 'descending', title: 'Descending', checked: app.user.ui.sort[0].operator == '-' }
                        ]}
                    ] },
                    { id: 'advancedsort', title: 'Advanced Sort...', keyboard: 'shift control s' },
                    {},
                    { id: 'groupsstuff', title: 'Groups Stuff' }
                ] },
                { id: 'findMenu', title: 'Find', items: [
                    { id: 'find', title: 'Find', items: [
                        { group: 'find', min: 1, max: 1, items: $.map(app.config.findKeys, function(key, i) {
                            return $.extend({
                                checked: app.user.ui.findQuery.conditions.length && 
                                        (app.user.ui.findQuery.conditions[0].key == key.id ||
                                        (app.user.ui.findQuery.conditions[0].key === '' && key.id == 'all')),
                            }, key)
                        }) }
                    ] },
                    { id: 'advancedfind', title: 'Advanced Find...', keyboard: 'shift control f' }
                ] },
                { id: 'dataMenu', title: 'Data', items: [
                    { id: 'titles', title: 'Manage Titles...' },
                    { id: 'names', title: 'Manage Names...' },
                    {},
                    { id: 'posters', title: 'Manage Stills...' },
                    { id: 'posters', title: 'Manage Posters...' },
                    {},
                    { id: 'places', title: 'Manage Places...' },
                    { id: 'events', title: 'Manage Events...' },
                    {},
                    { id: 'users', title: 'Manage Users...' },
                    { id: 'lists', title: 'Manage Lists...' },
                ] },
                { id: 'codeMenu', title: 'Code', items: [
                    { id: 'download', title: 'Download' },
                    { id: 'contribute', title: 'Contribute' },
                    { id: 'report', title: 'Report a Bug' },
                ] },
                { id: 'helpMenu', title: 'Help', items: [
                    { id: 'help', title: app.config.site.name + ' Help', keyboard: 'shift ?' }
                ] },
                { id: 'debugMenu', title: 'Debug', items: [
                    { id: 'query', title: 'Show Query' }
                ] },
                { id: 'testMenu', title: 'Test', items: [
                    { group: 'foogroup', items: [
                        { id: 'item1', title: 'Item 1' },
                        { id: 'item2', title: 'Item 2' }
                    ] }
                ] }
            ]
        })
        .bindEvent({
            change: function(event, data) {
                if (data.id == 'find') {
                    var id = data.checked[0].id;
                    app.$ui.findSelect.selectItem(id);
                } else if (data.id == 'movieview') {
                    var view = data.checked[0].id;
                    var id = document.location.pathname.split('/')[1];
                    if (view == 'info')
                        app.url('/'+id+'/info');
                    else
                        app.url('/'+id);
                } else if (data.id == 'ordermovies') {
                    var id = data.checked[0].id;
                    app.$ui.list.sortList(user.ui.sort[0].key, id == 'ascending' ? '' : '-');
                } else if (data.id == 'sortmovies') {
                    var id = data.checked[0].id,
                        operator = Ox.getObjectById(app.config.sortKeys, id).operator;
                    app.$ui.mainMenu.checkItem('sortMenu_ordermovies_' + (operator === '' ? 'ascending' : 'descending'));
                    app.$ui.list.sortList(id, operator);
                } else if (data.id == 'viewmovies') {
                    var view = data.checked[0].id;
                    app.url('/#view='+view);
                }
            },
            click: function(event, data) {
                if (data.id == 'about') {
                    var $dialog = new Ox.Dialog({
                        buttons: [
                            {
                                click: function() {
                                    $dialog.close();
                                },
                                id: 'close',
                                title: 'Close'
                            }
                        ],
                        id: 'about',
                        title: 'About'
                    }).open();
                } else if (data.id == 'home') {
                    var $dialog = new Ox.Dialog({
                        buttons: [
                            {
                                click: function() {
                                    $dialog.close();
                                },
                                id: 'close',
                                title: 'Close'
                            }
                        ],
                        height: 498,
                        id: 'home',
                        title: app.options('name'),
                        width: 800
                    }).open();
                } else if (data.id == 'loginlogout') {
                    var $form = new Ox.Form({
                            error: 'Unknown username or wrong password',
                            id: 'login',
                            items: [
                                {
                                    element: new Ox.Input({
                                        autovalidate: function(value, blur, callback) {
                                            var length = value.length;
                                            value = $.map(value.toLowerCase().split(''), function(v, i) {
                                                if (new RegExp('[a-z0-9' + ((i == 0 || (i == length - 1 && blur)) ? '' : '\- ') + ']')(v)) {
                                                    return v
                                                } else {
                                                    return null;
                                                }
                                            }).join('');
                                            $.each(['--', '- ', ' -', '--'], function(i, v) {
                                                while (value.indexOf(v) > -1) {
                                                    value = value.replace(new RegExp(v, 'g'), v[0]);
                                                }
                                            })
                                            callback(value);
                                        },
                                        id: 'username',
                                        label: 'Username',
                                        labelWidth: 120,
                                        validate: function(value, callback) {
                                            pandora.api.findUser({
                                                key: 'username',
                                                value: value,
                                                operator: '='
                                            }, function(result) {
                                                Ox.print('result', result)
                                                var valid = result.data.users.length == 1;
                                                callback({
                                                    message: 'Unknown Username',
                                                    valid: valid
                                                });
                                            });
                                        },
                                        width: 300
                                    })
                                },
                                {
                                    element: new Ox.Input({
                                        id: 'password',
                                        label: 'Password',
                                        labelWidth: 120,
                                        type: 'password',
                                        validate: /.+/,
                                        width: 300
                                    })
                                }
                            ],
                            submit: function(data, callback) {
                                pandora.api.login(data, function(result) {
                                    if (result.status.code == 200) {
                                        $dialog.close();
                                        app.user = result.data.user;
                                        app.$ui.mainMenu.getItem('username').options({
                                            title: 'User: ' + app.user.name
                                        });
                                        app.$ui.mainMenu.getItem('preferences').options({
                                            disabled: false
                                        });
                                        app.$ui.mainMenu.getItem('register').options({
                                            disabled: true
                                        });
                                    } else {
                                        callback([{ id: 'password', message: 'Incorrect Password' }]);
                                    }
                                });
                            }
                        })
                        .bindEvent({
                            validate: function(event, data) {
                                $dialog[(data.valid ? 'enable' : 'disable') + 'Button']('signin');
                            }
                        }),
                        $dialog = new Ox.Dialog({
                            buttons: [
                                [
                                    {
                                        click: function() {

                                        },
                                        id: 'signup',
                                        title: 'Sign up...'
                                    },
                                    {
                                        click: function() {

                                        },
                                        id: 'reset',
                                        title: 'Reset Password...'
                                    }
                                ],
                                [
                                    {
                                        click: function() {
                                            $dialog.close();
                                            app.$ui.mainMenu.getItem('loginlogout').toggleTitle();
                                        },
                                        id: 'cancel',
                                        title: 'Cancel'
                                    },
                                    {
                                        click: $form.submit,
                                        disabled: true,
                                        id: 'signin',
                                        title: 'Sign in'
                                    }
                                ]
                            ],
                            id: 'login',
                            minWidth: 332,
                            title: 'Sign in',
                            width: 332
                        }).append($form).open();
                } else if (data.id == 'places') {
                    var $manage = new Ox.SplitPanel({
                            elements: [
                                {
                                    collapsible: true,
                                    element: new Ox.SplitPanel({
                                        elements: [
                                            {
                                                element: new Ox.Toolbar({
                                                    orientation: 'horizontal',
                                                    size: 44
                                                }).append(
                                                    app.$ui.findPlacesElement = new Ox.FormElementGroup({
                                                        elements: [
                                                            app.$ui.findPlacesSelect = new Ox.Select({
                                                                    id: 'findPlacesSelect',
                                                                    items: [
                                                                        { id: 'name', title: 'Find: Name' },
                                                                        { id: 'region', title: 'Find: Region' },
                                                                        { id: 'user', title: 'Find: User' }
                                                                    ],
                                                                    overlap: 'right',
                                                                    type: 'image'
                                                                })
                                                                .bindEvent({
                                                                    change: function(event, data) {
                                                                        app.$ui.findPlacesSelect.loseFocus();
                                                                        app.$ui.findPlacesInput.options({
                                                                            placeholder: data.selected[0].title
                                                                        });
                                                                    }
                                                                }),
                                                            app.$ui.findPlacesInput = new Ox.Input({
                                                                clear: true,
                                                                id: 'findPlacesInput',
                                                                placeholder: 'Find: Name',
                                                                width: 234
                                                            })
                                                        ],
                                                        id: 'findPlacesElement'
                                                    }) 
                                                    .css({
                                                        float: 'left',
                                                        margin: '4px'
                                                    })
                                                ).append(
                                                    app.$ui.sortPlacesSelect = new Ox.Select({
                                                        id: 'sortPlacesSelect',
                                                        items: [
                                                            { id: 'name', title: 'Sort by Name', checked: true },
                                                            { id: 'region', title: 'Sort by Region' },
                                                            { id: 'size', title: 'Sort by Size' },
                                                            { id: 'latitude', title: 'Sort by Latitude' },
                                                            { id: 'longitude', title: 'Sort by Longitude' },
                                                            { id: 'clips', title: 'Sort by Number of Clips' },
                                                            { id: 'user', title: 'Sort by User' },
                                                            { id: 'datecreated', title: 'Sort by Date Added' },
                                                            { id: 'datemodified', title: 'Sort by Date Modified' }
                                                        ],
                                                        width: 246
                                                    })
                                                    .css({
                                                        float: 'left',
                                                        margin: '0 4px 4px 4px'
                                                    })
                                                ),
                                                size: 44
                                            },
                                            {
                                                element: new Ox.Element('div')
                                            },
                                            {
                                                element: new Ox.Toolbar({
                                                    orientation: 'horizontal',
                                                    size: 16
                                                }),
                                                size: 16
                                            }
                                        ],
                                        orientation: 'vertical'
                                    }),
                                    size: 256
                                },
                                {
                                    element: new Ox.SplitPanel({
                                        elements: [
                                            {
                                                element: new Ox.Toolbar({
                                                    orientation: 'horizontal',
                                                    size: 24
                                                }).append(
                                                    app.$ui.labelsButton = new Ox.Button({
                                                            id: 'labelsButton',
                                                            title: [
                                                                {id: 'show', title: 'Show Labels'},
                                                                {id: 'hide', title: 'Hide Labels'}
                                                            ],
                                                            width: 96
                                                        })
                                                        .css({
                                                            float: 'left',
                                                            margin: '4px'
                                                        })
                                                ).append(
                                                    app.$ui.findMapInput = new Ox.Input({
                                                        clear: true,
                                                        id: 'findMapInput',
                                                        placeholder: 'Find on Map',
                                                        width: 192
                                                    })
                                                    .css({
                                                        float: 'right',
                                                        margin: '4px'
                                                    })
													.bindEvent({
														submit: function(event, data) {
															app.$ui.map.find(data.value, function(location) {
																app.$ui.placeNameInput.options({
																	disabled: false,
																	value: location.name
																});
																app.$ui.placeAliasesInput.options({
																	disabled: false
																});
																app.$ui.placeGeonameLabel.options({
																	disabled: false,
																	title: location.names.join(', ')
																});
																app.$ui.removePlaceButton.options({
																	disabled: false
																});
																app.$ui.addPlaceButton.options({
																	disabled: false
																});
															});
														}
													})
                                                ),
                                                size: 24
                                            },
                                            {
                                                element: app.$ui.map = new Ox.Map({
                                                    	places: ['Boston', 'Brussels', 'Barcelona', 'Berlin', 'Beirut', 'Bombay', 'Bangalore', 'Beijing']
                                                	})
													.css({
                                                    	left: 0,
                                                    	top: 0,
                                                    	right: 0,
                                                    	bottom: 0
                                                	})
													.bindEvent({
														select: function(event, location) {
															app.$ui.placeNameInput.options({
																disabled: false,
																value: location.name
															});
															app.$ui.placeAliasesInput.options({
																disabled: false
															});
															app.$ui.placeGeonameLabel.options({
																disabled: false,
																title: location.names.join(', ')
															});
															app.$ui.removePlaceButton.options({
																disabled: false
															});
															app.$ui.addPlaceButton.options({
																disabled: false
															});
														}
													})
                                            },
                                            {
                                                element: app.$ui.bottomBar = new Ox.Toolbar({
                                                    orientation: 'horizontal',
                                                    size: 24
                                                })
												.append(
													app.$ui.placeNameInput = new Ox.Input({
														disabled: true,
														id: 'placeName',
														placeholder: 'Name',
														width: 128
													})
													.css({
														float: 'left',
														margin: '4px 0 0 4px'
													})
												)
												.append(
													app.$ui.placeAliasesInput = new Ox.Input({
														disabled: true,
														id: 'aliases',
														placeholder: 'Aliases',
														width: 128
													})
													.css({
														float: 'left',
														margin: '4px 0 0 4px'
													})
												)
												.append(
													app.$ui.placeGeonameLabel = new Ox.Label({
														disabled: true,
														id: 'placeGeoname',
														title: 'Geoname',
														width: parseInt(app.$document.width() * 0.8) - 256 - 256 - 32 - 24
													})
													.css({
                                                        float: 'left',
                                                        margin: '4px 0 0 4px'
                                                    })
												)
												.append(
                                                    app.$ui.addPlaceButton = new Ox.Button({
														disabled: true,
                                                        id: 'addPlaceButton',
                                                        title: 'add',
														type: 'image'
                                                    })
                                                    .css({
                                                        float: 'right',
                                                        margin: '4px 4px 0 0'
                                                    })
                                                )
												.append(
                                                    app.$ui.removePlaceButton = new Ox.Button({
														disabled: true,
                                                        id: 'removePlaceButton',
                                                        title: 'remove',
														type: 'image'
                                                    })
                                                    .css({
                                                        float: 'right',
                                                        margin: '4px 4px 0 0'
                                                    })
                                                ),
                                                size: 24
                                            }
                                        ],
                                        orientation: 'vertical'
                                    })
                                }
                            ],
                            orientation: 'horizontal'
                        }).css({
                            top: '24px',
                            bottom: '24px',
                        }),
                        $dialog = new Ox.Dialog({
                            buttons: [
                                {
                                    click: function() {
                                        $dialog.close();
                                    },
                                    id: 'close',
                                    title: 'Close',
                                    value: 'Close'
                                }
                            ],
                            height: parseInt(app.$document.height() * 0.8),
                            id: 'places',
                            minHeight: 400,
                            minWidth: 600,
                            padding: 0,
                            title: 'Manage Places',
                            width: parseInt(app.$document.width() * 0.8)
                        }).css({
                            overflow: 'hidden'
                        }).append($manage).open();
                }
            }
        });
}

app.constructSectionbar = function() {
	return new Ox.Bar({
            size: 24
        })
        .append(
            app.$ui.sectionButtons = new Ox.ButtonGroup({
                    buttons: [
						{id: 'site', title: app.config.site.name},
                        {id: 'items', title: app.config.itemName.plural},
                        {id: 'texts', title: 'Texts'},
						{id: 'admin', title: 'Admin', type: 'image'}
                    ],
                    id: 'sectionButtons',
                    selectable: true
                })
                .css({
                    float: 'left',
	                margin: '4px'
                })
        );
}

app.constructSections = function() {
    var $sections = [];
    $.each(app.user.ui.sections, function(i, id) {
        var $section = new Ox.CollapsePanel({
            id: id,
            size: 16,
            title: Ox.getObjectById(app.config.sections, id).title
        });
        $sections.push($section);
        $section.$content.append(
            $('<div>').css({ height: '20px' }).append(
                $('<div>').css({ float: 'left', width: '16px', height: '16px', margin: '1px'}).append(
                    $('<img>').attr({ src: 'static/oxjs/build/png/ox.ui.modern/iconFind.png' }).css({ width: '16px', height: '16px', border: 0, background: 'rgb(64, 64, 64)', WebkitBorderRadius: '2px' })
                )
            ).append(
                $('<div>').css({ float: 'left', width: '122px', height: '14px', margin: '2px' }).html('Foo')
            ).append(
                $('<div>').css({ float: 'left', width: '40px', height: '14px', margin: '2px', textAlign: 'right' }).html('23')
            )
        );
    });
    return $sections;
}

app.constructStatusbar = function() {
    return new Ox.Bar({
            size: 16
        })
        .css({
            textAlign: 'center'
        })
        .append(
            new Ox.Element()
                .css({
                    marginTop: '2px',
                    fontSize: '9px'
                })
                .append(
                    app.$ui.total = new Ox.Element('span')
                )
                .append(
                    new Ox.Element('span').html(' &mdash; ')
                )
                .append(
                    app.$ui.selected = new Ox.Element('span')
                )
        );
}

app.constructStatus = function(key, data) {
    return Ox.toTitleCase(key) + ': ' + [
        Ox.formatNumber(data.items) + ' movie' + (data.items != 1 ? 's' : ''),
        Ox.formatDuration(data.runtime, 'medium'),
        data.files + ' file' + (data.files != 1 ? 's' : ''),
        Ox.formatDuration(data.duration, 'short'),
        Ox.formatValue(data.size, 'B'),
        Ox.formatValue(data.pixels, 'px')
    ].join(', ');
}

app.constructToolbar = function() {
    return new Ox.Bar({
            size: 24
        })
        .append(
            app.$ui.groupsButton = new Ox.Button({
                    id: 'groupsButton',
                    title: [
                        {id: 'show', title: 'Show Groups'},
                        {id: 'hide', title: 'Hide Groups'}
                    ],
                    width: 96
                })
                .css({
                    float: 'left',
                    margin: '4px'
                })
        )
        .append(
            app.$ui.viewSelect = new Ox.Select({
                    id: 'viewSelect',
                    items: $.map(app.config.listViews, function(view, i) {
                        view.title = 'View ' + view.title
                        return $.extend({
                            checked: app.user.ui.listView == view.id,
                        }, view);
                    }),
                    width: 144
                })
                .css({
                    float: 'left',
                    margin: '4px'
                })
                .bindEvent('change', function(event, data) {
                    var id = data.selected[0].id;
                    app.$ui.mainMenu.checkItem('viewMenu_movies_' + id);
                    //$ui.list.$element.replaceWith(constructList(id));
                    Ox.print('change ... id', id, list = app.constructList(id), list.options(), list.options('id'))
                    //$ui.contentPanel.replace('list', constructList(id));
                    app.$ui.contentPanel.replace(1, app.constructList(id));
                })
        )
        .append(
            app.$ui.findElement = new Ox.FormElementGroup({
                    elements: [
                        app.$ui.findSelect = new Ox.Select({
                                id: 'select',
                                items: $.map(app.config.findKeys, function(key, i) {
                                    return {
                                        id: key.id,
                                        title: 'Find: ' + key.title
                                    };
                                }),
                                overlap: 'right',
                                width: 112
                            })
                            .bindEvent('change', function(event, data) {
                                var key = data.selected[0].id;
                                app.user.ui.findQuery.conditions[0].key = key
                                app.$ui.mainMenu.checkItem('findMenu_find_' + key);
                                app.$ui.findInput.focus();
                            }),
                        app.$ui.findInput = new Ox.Input({
                                autocomplete: function(value, callback) {
                                    var key = 'title';
                                    var findKey = Ox.getObjectById(app.config.findKeys, key);
                                    Ox.print('autocomplete', key, value);
                                    value === '' && Ox.print('Warning: autocomplete function should never be called with empty value');
                                    if ('autocomplete' in findKey && findKey.autocomplete) {
                                        pandora.api.find({
                                            keys: [key],
                                            query: {
                                                conditions: [
                                                    {
                                                        key: key,
                                                        value: value,
                                                        operator: ''
                                                    }
                                                ],
                                                operator: ''
                                            },
                                            sort: [
                                                {
                                                    key: key,
                                                    operator: ''
                                                }
                                            ],
                                            range: [0, 10]
                                        }, function(result) {
                                            callback($.map(result.data.items, function(v) {
                                                return v.title;
                                            }));
                                        });
                                    } else {
                                        callback();                            
                                    }
                                },
                                autocompleteSelect: true,
                                autocompleteSelectHighlight: true,
                                autocompleteSelectSubmit: true,
                                clear: true,
                                id: 'input',
                                width: 192
                            })
                        .bindEvent('submit', function(event, data) {
                            var key = app.user.ui.findQuery.conditions[0].key,
                                query;
                            Ox.print('key', key);
                            app.user.ui.findQuery.conditions = [
                                {
                                    key: key == 'all' ? '' : key,
                                    value: data.value,
                                    operator: ''
                                }
                            ];
                            $.each(app.ui.groups, function(i, group) {
                                group.query.conditions = [];
                                app.$ui.groups[i].options({
                                    request: function(data, callback) {
                                        delete data.keys;
                                        return pandora.api.find($.extend(data, {
                                            group: group.id,
                                            query: app.Query.toObject(group.id)
                                        }), callback);
                                    }
                                });
                            });
                            app.$ui.list.options({
                                request: function(data, callback) {
                                    return pandora.api.find($.extend(data, {
                                        query: query = app.Query.toObject()
                                    }), callback);
                                }
                            });
                            history.pushState({}, '', '/#' + app.Query.toString(query));
                        })
                    ],
                    id: 'findElement'
                })
                .css({
                    float: 'right',
                    margin: '4px'
                })
        );
}

*/



/*

    //FIXME: how to properly overwrite functions without replacing them
    var super_launch = app.launch;
    app.launch = function() {
        app.request.send('hello', function(result) {
            app.user = result.data.user;
            if(app.user.group!='guest') {
                app.menu.getItem('status').options('title', 'User: ' + app.user.username);
                app.menu.getItem('login').options('title', 'Logout');
            }
        });
        super_launch();
    };


    var loadingIcon = new Ox.LoadingIcon({
            size: 'medium'
        })
        .css({
            marginLeft: '4px'
        });

    app.menu = new Ox.MainMenu({
                    extras: [
                        new Ox.Input({
                            autocomplete: function(option, value, callback) {
                                    var field = option.substring(6).toLowerCase();
                                    if(typeof(callback) == 'undefined') {
                                        callback = value;
                                        value = null;
                                    }
                                    Ox.debug('app.menu.find.autocomplete: option: ', option, 'value: ', value, ', callback:',callback);
                                    Ox.debug('app.menu.find.autocomplete: field: ', field);
                                    if(field == 'all') {
                                        callback([]);
                                    } else if (value) {
                                        value = value.toLowerCase();
                                        //var order = $.inArray(field, ['year', 'date'])?'-':'';
                                        app.request.send('find', {
                                            query: {
                                                conditions: [
                                                {
                                                    key: field,
                                                    value: value,
                                                    operator: '~'
                                                }
                                                ]
                                            },
                                            list: 'all',
                                            sort: [{key:field, operator: ''}],
                                            keys: [field],
                                            range: [0, 10]
                                        }, function(result) {
                                            var items = $.map(
                                                result.data.items,
                                                function(item, i) { return item.title; }
                                            );
                                            callback(items);
                                        });
                                    }
                            },
                            clear: true,
                            highlight: false,
                            id: 'find',
                            label: [
                                { id: 'all', title: 'Find: All' },
                                { id: 'title', title: 'Find: Title' },
                                { id: 'director', title: 'Find: Director' },
                                { id: 'country', title: 'Find: Country' },
                                { id: 'year', title: 'Find: Year' },
                                { id: 'language', title: 'Find: Language' },
                                { id: 'writer', title: 'Find: Writer' },
                                { id: 'producer', title: 'Find: Producer' },
                                { id: 'cinematographer', title: 'Find: Cinematographer' },
                                { id: 'editor', title: 'Find: Editor' },
                                { id: 'actor', title: 'Find: Actor' },
                                { id: 'character', title: 'Find: Character' },
                                { id: 'name', title: 'Find: Name' },
                                { id: 'genre', title: 'Find: Genre' },
                                { id: 'keyword', title: 'Find: Keyword' },
                                { id: 'summary', title: 'Find: Summary' },
                                { id: 'dialog', title: 'Find: Dialog' }
                            ],
                            labelWidth: 96
                        }).width(320),
                        loadingIcon
                    ],
        menus: [
            {
                id: 'pandoraMM',
                title: site.name,
                items: [
                    { id: 'about', title: 'About ' + site.name },
                    {},
                    { id: 'faq', title: 'Frequently Asked Questions'},
                    { id: 'tos', title: 'Terms of Service'},
                    { id: 'sas', title: 'Security Advisory System'},
                    {},
                    { id: 'contact', title: 'Contact'},
                    {},
                    { id: 'technology', title: 'Technology'},
                    { id: 'source', title: 'Source Code'},
                    { id: 'report', title: 'Report a Bug...'},
                ]
            },
            {
                id: 'user',
                id: 'user',
                title: 'User',
                items: [
                    { id: 'status', title: 'User: not logged in', disabled:true },
                    {},
                    { id: 'accunt', title: 'Account', disabled:true},
                    { id: 'preferences', title: 'Preferences', disabled:true},
                    {},
                    {id: 'login', title: 'Login'},
                ]
            },
            {
                id: 'file',
                title: 'File',
                items: [
                    { id: 'load', keyboard: 'control o', title: 'Open' },
                    { id: 'save', keyboard: 'control a', title: 'Save' },
                    { id: 'save_ad', keyboard: 'shift control s', title: 'Save As...'}
                ]
            },
            {
                id: 'edit',
                title: 'Edit',
                items: [
                    { id: 'undo', keyboard: 'control z', title: 'Undo' },
                    { id: 'redo', keyboard: 'shift control z', title: 'Redo' },
                    {},
                    { id: 'cut', keyboard: 'control x', title: 'Cut' },
                    { id: 'copy', keyboard: 'control c', title: 'Copy' },
                    { id: 'paste', keyboard: 'control v', title: 'Paste'},
                    { id: 'delete', keyboard: 'delete', title: 'Delete'},
                    {},
                    { id: 'select_all', keyboard: 'control a', title: 'Select All' },
                    { id: 'select_none', keyboard: 'shift control a', title: 'Select None' },
                    { id: 'invert_selection', keyboard: 'alt control a', title: 'Invert Selection' },
                ]
            },
            {
                id: 'sort',
                title: 'Sort',
                items: [
                    { id: 'sort_movies', title: 'Sort Movies by', items: [
                        { checked: true, group: 'sort_movies', id: 'title', title: 'Title'},
                        { checked: false, group: 'sort_movies', id: 'director', title: 'Director' },
                    ] },
                    { id: 'order_movies', title: 'Order Movies', items: [
                        { checked: false, group: 'order_movies', id: 'ascending', title: 'Ascending'},
                        { checked: true, group: 'order_movies', id: 'descending', title: 'Descending' },
                    ] }
                ]
            },
            {
                id: 'help',
                title: 'Help',
                items: [
                    { id: 'help', keyboard: 'control h', title: 'Help' }
                ]
            }
        ],
        size: 'large'
    });

    function pageDialog(title, page) {
        //Ox.debug(title, page);
        var $dialog = new Ox.Dialog({
            title: title,
            buttons: [
                {
                    title: 'Close',
                    click: function() { $dialog.close(); }
                }
            ]
        })
        .append(page)
        .open();
    };
    //this should be: mainMenu.bind('click_about', function(event) {
    app.menu.bindEvent('click_about', function() {
        pageDialog('About ' + site.name, site.pages.about);
    });
    app.menu.bindEvent('click_faq', function() {
        pageDialog(app.menu.getItem('faq').options('title')[0],
                   site.pages.faq);
    });
    app.menu.bindEvent('click_tos', function() {
        pageDialog(app.menu.getItem('tos').options('title')[0],
                   site.pages.tos);
    });
    app.menu.bindEvent('click_sas', function() {
        pageDialog(app.menu.getItem('sas').options('title')[0],
                   site.pages.sas);
    });

    OxForm = function(options, self) {
        var self = self || {},
            that = new Ox.Element({}, self)
                .defaults({
                    elements: [],
                })
                .options(options || {})
                .addClass('OxForm'),
            length = self.options.elements.length;
        $.each(self.options.elements, function(i, v) {
            that.append(Ox.Container().css({'margin': '5px'}).append(v));
        });
        that.values = function() {
            var values = {};
            $.each(self.options.elements, function(i, v) {
                values[v.$input.attr('name')] = v.$input.val();
            });
            return values;
        }
        return that;
    };

    app.menu.bindEvent('click_contact', function() {
        var labelWidth = 64;
        var inputWidth = 380;

        var u = new Ox.Input({
            label: 'Your Email',
            labelWidth: labelWidth,
            name:'email',
            size: 'medium'
        }).width(inputWidth).addClass('margin');
        if(app.user && app.user.preferences.email) {
            u.val(app.user.preferences.email);
        }

        var form = new OxForm({
            elements: [
                u,
                new Ox.Input({
                    label: 'Subject',
                    labelWidth: labelWidth,
                    name: 'subject',
                    size: 'medium'
                }).width(inputWidth).addClass('margin'),
                new Ox.Input({
                    label: 'Message',
                    labelWidth: labelWidth,
                    type: 'textarea',
                    size: 'medium',
                    name: 'message'
                }).width(380).height(176).addClass('margin')
            ]
        });

        var $dialog = new Ox.Dialog({
            title: 'Contact',
            width: 424,
            height: 320,
            buttons: [
                {
                    value: 'Cancel',
                    click: function() { $dialog.close(); }
                },
                {
                    value: 'Contact',
                    click: function() {
                        app.request.send('contact', form.values(),
                        function(result) {
                            if(result.status.code == 200) {
                                $dialog.close();                               
                            } else {
                                $dialog.append(result.status.text);
                            }
                        });
                    }
                }
            ]
        })
        .append(form)
        .open();
    });
    app.menu.bindEvent('click_technology', function() {
        pageDialog(app.menu.getItem('technology').options('title')[0],
                   site.pages.technology);
    });
    app.menu.bindEvent('click_source', function() {
        pageDialog(app.menu.getItem('source').options('title')[0],
                   site.pages.source);
    });
    app.menu.bindEvent('click_report', function() {
        pageDialog(app.menu.getItem('report').options('title')[0],
                   site.pages.report);
    });

    app.logout = function () {
        this.request('logout');
        this.user = {};
        this.menu.getItem('logout').toggle();
        this.menu.getItem('status').options('title', 'User: not logged in');
    };
    app.menu.bindEvent('click_logout', function(event, data) {
        app.logout();
    });

    app.menu.bindEvent('click_login', function(element) {
        var labelWidth = 64;
        var inputWidth = labelWidth+200;
        var loginForm = new OxForm({
            elements: [
                new Ox.Input({
                    label: 'Username',
                    labelWidth: labelWidth,
                    name:'username',
                    size: 'medium'
                }).addClass('margin').width(inputWidth),
                new Ox.Input({
                    label: 'Password',
                    labelWidth: labelWidth,
                    name:'password',
                    type: 'password',
                    size: 'medium'
                }).addClass('margin').width(inputWidth)
            ]
        }).css({
            'padding-top': '48px',
        });

        var submit = function() {
            app.request.send('login', loginForm.values(), function(result) {
                if(result.status.code == 200) {
                    $dialog.close();
                    app.user = result.data.user;
                    app.menu.getItem('status').options(title, 'User: ' + app.user.username);
                    app.menu.getItem('login').toggle();
                    
                } else {
                    $dialog.append('Login failed ' + result.status.text);
                }
            });
        }

        var d = new Ox.Container();
        var registerInfo = new Ox.Panel();

        registerInfo.append(Ox.Element().css({'margin-left': '4px'}).append('<br>Forgot your password? <a href="">Recover Password</a><br>Dont have an account? <a href="">Register Now</a>'));

        var panel = Ox.SplitPanel({
            elements: [
                {
                    element: loginForm,
                },
                {
                    element: registerInfo,
                    size: 80
                }
            ],
            orientation: 'vertical'
        }).appendTo(d);

        var $dialog = new Ox.Dialog({
            title: 'Login',
            width: inputWidth+24,
            height: 184,
            buttons: [
                [],[
                    {
                        value: 'Cancel',
                        click: function() { $dialog.close(); }
                    },
                    {
                        value: 'Login',
                        click: submit
                    }
                ]
            ]
        })
        .append(d)
        .open();
    });
    var bottomPanel = Ox.Toolbar({size: 'small'})
        .css({
            zIndex: 2,
            MozBoxShadow: '0 0 4px rgb(0, 0, 0)',
            WebkitBoxShadow: '0 0 4px rgb(0, 0, 0)'
        })
        .append(
            $('<div/>')
                .addClass('bottom')
                .html(site.url + ' - a rather unique kind of movie database.')
        );
    var mainSplitPanel = Ox.SplitPanel({
        elements: [
            {
                element: app.menu,
                size: 24
            },
            {
                element: middleSplitPanel
            },
            {
                element: bottomPanel,
                size: 24
            }
        ],
        orientation: 'vertical'
    }).appendTo($body);

    var listPanel = new Ox.CollapsePanel({
        title: 'Lists'
    }).appendTo(sidePanel);
    listPanel.$content.html('Nouvelle Vague<br/>Hollywood 40\'s<br/>Pirate Cinema Berlin')

    var historyPanel = new Ox.CollapsePanel({
        title: 'Search History'
    }).appendTo(sidePanel);

    historyPanel.$content.html('Paris<br/>Matirx<br/>Godard')

    app.results = new Ox.TextList({
        columns: [ {
                align: 'left',
                id: 'title',
                operator: '+',
                title: 'Title',
                visible: true,
                width: 160
            },
            {
                align: 'left',
                id: 'director',
                operator: '+',
                title: 'Director',
                visible: true,
                width: 160
            },
            {
                align: 'right',
                id: 'year',
                operator: '-',
                title: 'Year',
                visible: true,
                width: 80
            }	
        ],
        request: function(options) {
            app.request.send('find', $.extend(options, {
                query: {
                    conditions: [],
                    operator: ',' // fixme: should be &
                }
            }), options.callback);
        },
        id: 'results',
        sort: [{
            key: 'year',
            operator: '-'
        }]
    }).appendTo(content);

    app.menu.bindEvent('submit_find', function(event, data) {
        app.results.options({
            request: function(options) {
                app.request.send('find', $.extend(options, {
                    query: {
                        key: data.option.substr(6).toLowerCase(),
                        value: data.value,
                        operator: '~'
                    }
                }), options.callback);
            },
        });
    });
    app.launch();

});

*/