diff --git a/pandora/static/js/pandora.js b/pandora/static/js/pandora.js index c38a30660..d0fb9dc80 100644 --- a/pandora/static/js/pandora.js +++ b/pandora/static/js/pandora.js @@ -1,34 +1,673 @@ -$(function(){ +$(function() { + Ox.debug = Ox.print; Ox.print = function() {}; - Ox.theme("modern"); + var $body = $("body"), + $document = $(document), + $window = $(window), + config = { + archiveId: "oxdb", + archiveName: "0xDB", + findKeys: [ + { id: "all", title: "All" }, + { id: "title", title: "Title" }, + { id: "director", title: "Director" }, + { id: "country", title: "Country" }, + { id: "year", title: "Year" }, + { id: "language", title: "Language" }, + { id: "writer", title: "Writer" }, + { id: "producer", title: "Producer" }, + { id: "cinematographer", title: "Cinematographer" }, + { id: "editor", title: "Editor" }, + { id: "actor", title: "Actor" }, + { id: "character", title: "Character" }, + { id: "name", title: "Name" }, + { id: "genre", title: "Genre" }, + { id: "keyword", title: "Keyword" }, + { id: "summary", title: "Summary" }, + { id: "dialog", title: "Dialog" } + ], + groups: ["director", "country", "year", "language", "genre"], + itemViews: [ + { id: "info", title: "Info" }, + { id: "statistics", title: "Statistics" }, + { id: "clips", title: "Clips" }, + { id: "timeline", title: "Timeline" }, + { id: "map", title: "Map" }, + { id: "calendar", title: "Calendar" }, + { id: "files", title: "Files", admin: true } + ], + listViews: [ + { id: "list", title: "View as List" }, + { id: "icons", title: "View as Icons" }, + { id: "clips", title: "View with Clips" }, + { id: "timelines", title: "View with Timelines" }, + { id: "maps", title: "View with Maps" }, + { id: "calendars", title: "View with Calendars" }, + { id: "clip", title: "View as Clips" }, + { id: "map", title: "View on Map" }, + { id: "calendar", title: "View on Calendar" }, + ], + sortKeys: [ + { id: "title", title: "Title", operator: "", align: "left", width: 180 }, + { id: "director", title: "Director", operator: "", align: "left", width: 180 }, + { id: "country", title: "Country", operator: "", align: "left", width: 120 }, + { id: "year", title: "Year", operator: "-", align: "right", width: 60 }, + { id: "language", title: "Language", operator: "", align: "left", width: 120 }, + { id: "writer", title: "Writer", operator: "", align: "left", width: 180 }, + { id: "producer", title: "Producer", operator: "", align: "left", width: 180 }, + { id: "cinematographer", title: "Cinematographer", operator: "", align: "left", width: 180 }, + { id: "editor", title: "Editor", operator: "", align: "left", width: 180 }, + { id: "actors", title: "Number of Actors", operator: "-", align: "right", width: 60 }, + { id: "genre", title: "Genre", operator: "", align: "left", width: 120 }, + { id: "keywords", title: "Number of Keywords", operator: "-", align: "right", width: 60 }, + { id: "summary", title: "Words in Summary", operator: "-", align: "right", width: 60 }, + { id: "trivia", title: "Words in Trivia", operator: "-", align: "right", width: 60 }, + { id: "releasedate", title: "Release Date", operator: "-", align: "left", width: 90 }, + { id: "budget", title: "Budget", operator: "-", align: "right", width: 90 }, + { id: "gross", title: "Gross", operator: "-", align: "right", width: 90 }, + { id: "profit", title: "Profit", operator: "-", align: "right", width: 90 }, + { id: "rating", title: "Rating", operator: "-", align: "right", width: 60 }, + { id: "votes", title: "Votes", operator: "-", align: "right", width: 90 }, + { id: "id", title: "ID", operator: "", align: "left", width: 90 } + { id: "aspectratio", title: "Aspect Ratio", operator: "-", align: "left", width: 90 }, + { id: "duration", title: "Duration", operator: "-", align: "right", width: 90 }, + { id: "color", title: "Color", operator: "", align: "left", width: 90 }, + { id: "saturation", title: "Saturation", operator: "-", align: "right", width: 60 }, + { id: "brightness", title: "Brightness", operator: "-", align: "right", width: 60 }, + { id: "volume", title: "Volume", operator: "-", align: "right", width: 60 }, + { id: "clips", title: "Clips", operator: "-", align: "right", width: 60 }, + { id: "cuts", title: "Cuts", operator: "-", align: "right", width: 60 }, + { id: "cutsperminute", title: "Cuts per Minute", operator: "-", align: "right", width: 60 }, + { id: "words", title: "Words", operator: "-", align: "right", width: 60 }, + { id: "wordsperminute", title: "Words per Minute", operator: "-", align: "right", width: 60 }, + { id: "resolution", title: "Resolution", operator: "-", align: "left", width: 90 }, + { id: "pixels", title: "Pixels", operator: "-", align: "right", width: 90 }, + { id: "size", title: "Size", operator: "-", align: "right", width: 90 }, + { id: "bitrate", title: "Bitrate", operator: "-", align: "right", width: 90 }, + { id: "files", title: "Files", operator: "-", align: "right", width: 60 }, + { id: "filename", title: "Filename", operator: "", align: "left", width: 180 }, + { id: "published", title: "Date Published", operator: "-", align: "left", width: 90 }, + { id: "modified", title: "Date Modified", operator: "-", align: "left", width: 90 } + ], + userSettings: { + group: "guest", + ui: { + columns: ["title", "director", "country", "year", "language", "runtime", "genre"], + find: { key: "all", value: "", operator: "" }, + itemView: "info", + listView: "list", + showGroups: true, + showInfo: true, + showList: true, + showMovies: true, + sort: [ + { key: "director", operator: "" } + ], + theme: "modern" + }, + username: "" + } + }, + user = config.userSettings, + $ui = {}; + +// App + + Ox.theme(user.ui.theme); app = new Ox.App({ requestURL: "/api/" }); - var size = window.location.hash.substr(1) || "medium", - $body = $("body"), - $toolbars = []; - var sidePanel = new Ox.Panel(); - var mainPanel = new Ox.Panel()/*.css({ - //borderLeft: "1px solid rgb(160, 160, 160)" - })*/; - var middleSplitPanel = Ox.SplitPanel({ +// MainMenu + + $ui.mainMenu = new Ox.MainMenu({ + extras: [ + $ui.loadingIcon = new Ox.LoadingIcon({ + size: "medium" + }) + ], + menus: [ + { id: config.archiveId, title: config.archiveName, 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: "user", title: "User", items: [ + { id: "username", title: "User: not logged in", disabled: true }, + {}, + { id: "preferences", title: "Preferences", disabled: true }, + {}, + { id: "login", title: "Login" } + ] }, + { id: "edit", title: "Edit", items: [ + { id: "undo", title: "Undo", disabled: true }, + { id: "redo", title: "Redo", disabled: true }, + {}, + { id: "cut", title: "Cut", disabled: true }, + { id: "copy", title: "Copy", disabled: true }, + { id: "paste", title: "Paste", disabled: true }, + { id: "delete", title: "Delete", disabled: true }, + {}, + { id: "selectall", title: "Select All", disabled: true }, + { id: "selectnone", title: "Select None", disabled: true }, + { id: "invertselection", title: "Invert Selection", disabled: true } + ] }, + { id: "list", 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..." }, + { id: "newlistfromselection", title: "New List from Selection...", disabled: true}, + { id: "newsmartlist", title: "New Smart List..." } + {}, + { id: "addtolist", title: "Add Selected Movie to List...", disabled: true }, + {}, + { id: "setposterframe", title: "Set Poster Frame", disabled: true } + ]}, + { id: "view", title: "View", items: [ + { id: "movies", title: "View Movies", items: $.map(config.listViews, function(view, i) { + return $.extend(view, { + checked: user.ui.listView == view.id, + group: "viewmovies" + }); + }) }, + { 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: "movie", title: "Open Movie", items: $.map(config.listViews, function(view, i) { + return view; + }) }, + {}, + { id: "lists", title: "Hide Lists" }, + { id: "info", title: "Hide Info" }, + { id: "groups", title: "Hide Groups" }, + { id: "movies", title: "Hide Movies", disabled: true } + ]}, + { id: "sort", title: "Sort", items: [ + { id: "sortmovies", title: "Sort Movies by", items: $.map(config.sortKeys, function(key, i) { + return $.extend(key, { + checked: user.ui.sort[0].key == key, + group: "sortmovies" + }); + }) }, + { id: "ordermovies", title: "Order Movies", items: [ + { id: "ascending", title: "Ascending", group: "ordermovies", checked: user.ui.sort[0].operator == "" }, + { id: "descending", title: "Descending", group: "ordermovies", checked: user.ui.sort[0].operator == "-" } + ]}, + { id: "advancedsort", title: "Advanced Sort..." } + {}, + { id: "groupsstuff", title: "Groups Stuff" } + ] }, + { id: "find", title: "Find", items: [ + { id: "find", title: "Find", items: $.map(config.findKeys, function(key, i) { + return $.extend(key, { + checked: user.ui.find.key == key, + group: "find" + }) + }) }, + { id: "advancedfind", "Advanced Find..." } + ] }, + { id: "code", title: "Code", items: [ + { id: "download", title: "Download" }, + { id: "contribute", title: "Contribute" }, + { id: "report", title: "Report a Bug" }, + ] }, + { id: "help", title: "Help", items: [ + { id: "help", title: config.archiveName + " Help" } + ] }, + { id: "debug", title: "Debug", items: [ + { id: "query", title: "Show Query" } + ]} + ] + }); + +// Toolbar + + $ui.toolbar = new Ox.Bar({ + size: 24 + }) + .append( + $ui.groupsButton = new Ox.Button({ + id: "groupsButton", + value: ["Show Groups", "Hide Groups"] + }) + .css({ + float: "left", + margin: "4px" + }) + .width(80) + ) + .append( + $ui.viewSelect = new Ox.Select({ + id: "viewSelect", + items: $.map(config.listViews, function(view, i) { + return $.extend(view, { + checked: user.ui.listView == view.id + }); + }) + }) + .css({ + float: "left", + margin: "4px" + }) + .width(120) + ) + .append( + $ui.findInput = new Ox.Input({ + autocomplete: function(key, value, callback) { + Ox.print("autocomplete", key, value); + value === "" && Ox.print("Warning: autocomplete function should never be called with empty value"); + if (key == "all") { + callback(); + } else { + app.request("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; + })); + }); + } + }, + clear: true, + highlight: true, + id: "findInput", + label: $.map(config.findKeys, function(key, i) { + return { + id: key.id, + title: "Find: " + key.title + } + }), + labelWidth: 85 + }) + css({ + float: "right", + margin: "4px" + }) + .width(300) + ); + + +// Groups + + var rightPanelWidth = $document.width() - 256, + groups = $.map(config.groups, function(id, i) { + var title = Ox.getObjectById(sortKeys, id).title; + return { + id: id, + conditions: [], + element: new Ox.TextList({ + columns: [ + { + align: "left", + id: "name", + operator: id == "year" ? "-" : "+", + title: title, + unique: true, + visible: true, + width: size - 40 - ($.browser.mozilla ? 16 : 12) + }, + { + align: "right", + id: "items", + operator: "-", + title: "#", + visible: true, + width: 40 + } + ], + id: "group_" + id, + request: function(options) { + delete options.keys; + app.request("find", $.extend(options, { + group: id, + query: constructQuery() + }), options.callback); + }, + sort: [ + { + key: id == "year" ? "name" : "items", + operator: "-" + } + ] + }), + size: rightPanelWidth / 5 + (rightPanelWidth % 5 > i), + title: title + }; + }); + +// Interface + + $ui.app = new Ox.SplitPanel({ elements: [ { - element: sidePanel, - size: 256 + element: $ui.mainMenu, + size: 20 }, { - element: mainPanel + element: $ui.mainPanel = new Ox.SplitPanel({ + elements: [ + { + element: $ui.leftPanel = new Ox.SplitPanel({ + elements: [ + { + element: $ui.sidebar = new Ox.Element() + }, + { + element: $ui.info = new Ox.Element(), + size: 128 + } + ] + }), + size: 256 + }, + { + element: $ui.rightPanel = new Ox.SplitPanel({ + elements: [ + { + element: $ui.toolbar, + size: 24 + }, + { + element: $ui.contentPanel = new Ox.SplitPanel({ + elements: [ + { + element: $ui.groupsOuterPanel = new Ox.SplitPanel({ + elements: [ + { + element: groups[0].element, + size: groups[0].size + }, + { + element: $ui.groupsInnerPanel = new Ox.SplitPanel({ + elements: [ + { + element: groups[1].element, + size: groups[1].size + }, + { + element: groups[2].element, + size: groups[2].size + }, + { + element: groups[3].element, + size: groups[3].size + } + ] + }) + }, + { + element: groups[4].element, + size: groups[4].size + } + ] + }), + size: 128 + }, + { + element: $ui.list = constructList(user.ui.listView) + } + ] + }) + } + { + element: $ui.statusbar = new Ox.Bar({ size: 16 }), + size: 16 + } + ] + }), + } + ] + }) } - ], - orientation: "horizontal" - })/*.css({ - borderTop: "1px solid rgb(160, 160, 160)", - borderBottom: "1px solid rgb(160, 160, 160)" - })*/; + ] + }).appendTo($body); + +// Events + + Ox.Request.requests() && $loadingIcon.start(); + Ox.Event.bind("requestStart", function() { + Ox.print("requestStart") + $loadingIcon.start(); + }); + Ox.Event.bind("requestStop", function() { + Ox.print("requestStop") + $loadingIcon.stop(); + }); + + Ox.Event.bind("change_viewSelect", function(event, data) { + $list.replaceWith(constructList(data.id)); + }); + + Ox.Event.bind("submit_find", function(event, data) { + findCondition = { + key: data.key == "all" ? "" : data.key, + value: data.value, + operator: "" + }; + $.each(groups, function(i, group) { + groups[i].conditions = []; + $group[i].options({ + request: function(options) { + delete options.keys; + return app.request("find", $.extend(options, { + group: group.id, + query: constructQuery(group.id) + }), options.callback); + } + }); + }); + $list.options({ + request: function(options) { + return app.request("find", $.extend(options, { + query: constructQuery() + }), options.callback); + } + }) + }); + + $.each(groups, function(i, group) { + Ox.Event.bind("select_group_" + group.id, function(event, data) { + $list.options({ + request: function(options) { + groups[i].conditions = $.map(data.ids, function(v) { + return { + key: group.id, + value: v, + operator: "=" + }; + }); + return app.request("find", $.extend(options, { + query: constructQuery() + }), options.callback); + } + }); + $.each(groups, function(i_, group_) { + if (i_ != i) { + $group[i_].options({ + request: function(options) { + delete options.keys; + return app.request("find", $.extend(options, { + group: group_.id, + query: constructQuery(group_.id) + }), options.callback); + } + }); + } + }); + }); + }); + Ox.Event.bind("change_sort_movies", function(event, data) { + + }); + Ox.Event.bind("load_list", function(event, data) { + $totals.html(constructStatus({ + "total": data, + "selected": {} + })); + var html = [ + 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") + ]; + $totals.html("Total: " + constructStatus(data) + " — Selected: " + constructStatus({ + duration: 0, + files: 0, + items: 0, + pixels: 0, + runtime: 0, + size: 0 + })); + }); + Ox.Event.bind("sort_list", function(event, data) { + + }); + Ox.Event.bind("select_list", function(event, data) { + + }); + Ox.Event.bind("click_show_query", function(event, data) { + var query = constructQuery(), + html = "Conditions

" + $.map(query.conditions, function(v) { + return v.key + " " + v.operator + " " + v.value; + }).join("
") + "

Operator: " + query.operator, + $dialog = new Ox.Dialog({ + title: "Show Query", + buttons: [ + { + value: "Close", + click: function() { + $dialog.close(); + } + } + ] + }) + .append(html) + .open(); + }); + +// Functions + + function constructList(view) { + var $list; + if (view == "text") { + $list = new Ox.TextList({ + columns: $.map(config.sortKeys, function(key, i) { + return $.extend(key, { + visible: $.inArray(key.id, user.ui.columns) > -1, + unique: key.id == "id" + }); + }), + id: "list", + request: function(options) { + app.request("find", $.extend(options, { + query: constructQuery() + }), options.callback); + }, + sort: user.ui.sort + }); + } else if (view == "icons") { + $list = new Ox.IconList({ + id: "list", + item: function(data, sort, size) { + return { + height: data.posterHeight, + id: data["id"], + info: data[$.inArray(sort[0].key, ["title", "director"]) > -1 ? "year" : sort[0].key], + title: data.title + (data.director ? " (" + data.director + ")" : ""), + url: "http://0xdb.org/" + data.id + "/poster." + size + "." + "jpg", + width: data.posterWidth + }; + }, + keys: ["director", "id", "posterHeight", "posterWidth", "posterURL", "title"], + request: function(options) { + app.request("find", $.extend(options, { + query: constructQuery() + }), options.callback); + }, + size: 128, + sort: [ + { + key: "director", + operator: "+" + } + ], + unique: "id" + }); + } + return $list; + } + + function constructQuery(groupId) { + var conditions = $.merge(!Ox.isUndefined(user.ui.find.key) ? [user.ui.find] : [], $.map(groups, function(v, i) { + if (v.id != groupId) { + return v.conditions; + } + })); + return { + conditions: conditions, + operator: conditions.length ? "," : "" + }; + } + + function constructStatus(data) { + var html = []; + $.each(data, function(k, v) { + html.push(Ox.toTitleCase(k) + ": " + [ + 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 html.join(" — "); + } + + + + //FIXME: how to properly overwrite functions without replacing them var super_launch = app.launch; @@ -43,12 +682,7 @@ $(function(){ super_launch(); }; - var loadingIcon = new Ox.LoadingIcon({ - size: "medium" - }) - .css({ - marginLeft: "4px" - }); + return; app.menu = new Ox.MainMenu({ extras: [