From b5a401d9e5590a9011c3fe37d41758d12a7864be Mon Sep 17 00:00:00 2001 From: Ruud Date: Thu, 20 Aug 2015 09:41:10 +0200 Subject: [PATCH] Combined JS --- couchpotato/static/scripts/combined.plugins.min.js | 1967 +++++++++++++++++--- 1 file changed, 1717 insertions(+), 250 deletions(-) diff --git a/couchpotato/static/scripts/combined.plugins.min.js b/couchpotato/static/scripts/combined.plugins.min.js index fa0bf8d..c54529b 100644 --- a/couchpotato/static/scripts/combined.plugins.min.js +++ b/couchpotato/static/scripts/combined.plugins.min.js @@ -176,6 +176,185 @@ window.addEvent("domready", function() { new PutIODownloader(); }); +var QualityBase = new Class({ + tab: "", + content: "", + setup: function(data) { + var self = this; + self.qualities = data.qualities; + self.profiles_list = null; + self.profiles = []; + Array.each(data.profiles, self.createProfilesClass.bind(self)); + App.addEvent("loadSettings", self.addSettings.bind(self)); + }, + getProfile: function(id) { + return this.profiles.filter(function(profile) { + return profile.data._id == id; + }).pick(); + }, + getActiveProfiles: function() { + return Array.filter(this.profiles, function(profile) { + return !profile.data.hide; + }); + }, + getQuality: function(identifier) { + try { + return this.qualities.filter(function(q) { + return q.identifier == identifier; + }).pick(); + } catch (e) {} + return {}; + }, + addSettings: function() { + var self = this; + self.settings = App.getPage("Settings"); + self.settings.addEvent("create", function() { + var tab = self.settings.createSubTab("profile", { + label: "Quality", + name: "profile", + subtab_label: "Qualities" + }, self.settings.tabs.searcher, "searcher"); + self.tab = tab.tab; + self.content = tab.content; + self.createProfiles(); + self.createProfileOrdering(); + self.createSizes(); + }); + }, + createProfiles: function() { + var self = this; + var non_core_profiles = Array.filter(self.profiles, function(profile) { + return !profile.isCore(); + }); + var count = non_core_profiles.length; + self.settings.createGroup({ + label: "Quality Profiles", + description: "Create your own profiles with multiple qualities." + }).inject(self.content).adopt(self.profile_container = new Element("div.container"), new Element("a.add_new_profile", { + text: count > 0 ? "Create another quality profile" : "Click here to create a quality profile.", + events: { + click: function() { + var profile = self.createProfilesClass(); + $(profile).inject(self.profile_container); + } + } + })); + Array.each(non_core_profiles, function(profile) { + $(profile).inject(self.profile_container); + }); + }, + createProfilesClass: function(data) { + var self = this; + data = data || { + id: randomString() + }; + var profile = new Profile(data); + self.profiles.include(profile); + return profile; + }, + createProfileOrdering: function() { + var self = this; + self.settings.createGroup({ + label: "Profile Defaults", + description: "(Needs refresh '" + (App.isMac() ? "CMD+R" : "F5") + "' after editing)" + }).grab(new Element(".ctrlHolder#profile_ordering").adopt(new Element("label[text=Order]"), self.profiles_list = new Element("ul"), new Element("p.formHint", { + html: "Change the order the profiles are in the dropdown list. Uncheck to hide it completely.
First one will be default." + }))).inject(self.content); + Array.each(self.profiles, function(profile) { + var check; + new Element("li", { + "data-id": profile.data._id + }).adopt(check = new Element("input[type=checkbox]", { + checked: !profile.data.hide, + events: { + change: self.saveProfileOrdering.bind(self) + } + }), new Element("span.profile_label", { + text: profile.data.label + }), new Element("span.handle.icon-handle")).inject(self.profiles_list); + }); + var sorted_changed = false; + self.profile_sortable = new Sortables(self.profiles_list, { + revert: true, + handle: ".handle", + opacity: .5, + onSort: function() { + sorted_changed = true; + }, + onComplete: function() { + if (sorted_changed) { + self.saveProfileOrdering(); + sorted_changed = false; + } + } + }); + }, + saveProfileOrdering: function() { + var self = this, ids = [], hidden = []; + self.profiles_list.getElements("li").each(function(el, nr) { + ids.include(el.get("data-id")); + hidden[nr] = +!el.getElement("input[type=checkbox]").get("checked"); + }); + Api.request("profile.save_order", { + data: { + ids: ids, + hidden: hidden + } + }); + }, + createSizes: function() { + var self = this; + var group = self.settings.createGroup({ + label: "Sizes", + description: "Edit the minimal and maximum sizes (in MB) for each quality.", + advanced: true, + name: "sizes" + }).inject(self.content); + new Element("div.item.head.ctrlHolder").adopt(new Element("span.label", { + text: "Quality" + }), new Element("span.min", { + text: "Min" + }), new Element("span.max", { + text: "Max" + })).inject(group); + Array.each(self.qualities, function(quality) { + new Element("div.ctrlHolder.item").adopt(new Element("span.label", { + text: quality.label + }), new Element("input.min[type=text]", { + value: quality.size_min, + events: { + keyup: function(e) { + self.changeSize(quality.identifier, "size_min", e.target.get("value")); + } + } + }), new Element("input.max[type=text]", { + value: quality.size_max, + events: { + keyup: function(e) { + self.changeSize(quality.identifier, "size_max", e.target.get("value")); + } + } + })).inject(group); + }); + }, + size_timer: {}, + changeSize: function(identifier, type, value) { + var self = this; + if (self.size_timer[identifier + type]) clearTimeout(self.size_timer[identifier + type]); + self.size_timer[identifier + type] = function() { + Api.request("quality.size.save", { + data: { + identifier: identifier, + value_type: type, + value: value + } + }); + }.delay(300); + } +}); + +window.Quality = new QualityBase(); + var BlockSearch = new Class({ Extends: BlockBase, cache: {}, @@ -2459,85 +2638,1552 @@ var TraktAutomation = new Class({ new TraktAutomation(); -var NotificationBase = new Class({ - Extends: BlockBase, - Implements: [ Options, Events ], - initialize: function(options) { - var self = this; - self.setOptions(options); - App.addEvent("unload", self.stopPoll.bind(self)); - App.addEvent("reload", self.startInterval.bind(self, [ true ])); - App.on("notification", self.notify.bind(self)); - App.on("message", self.showMessage.bind(self)); - App.addEvent("loadSettings", self.addTestButtons.bind(self)); - self.notifications = []; - App.addEvent("load", function() { - App.block.notification = new BlockMenu(self, { - button_class: "icon-notifications", - class: "notification_menu", - onOpen: self.markAsRead.bind(self) +Page.Shows = new Class({ + Extends: PageBase, + name: "shows", + title: "List of TV Shows subscribed to", + folder_browser: null, + has_tab: false, + toggleShows: function(arg) { + var self = this; + var nav = App.getBlock("navigation"); + if (typeof arg === "object" && arg.data === true || arg === true) { + self.tab = nav.addTab(self.name, { + href: App.createUrl(self.name), + title: self.title, + text: self.name.capitalize() }); - $(App.block.notification).inject(App.getBlock("search"), "after"); - self.badge = new Element("div.badge").inject(App.block.notification, "top").hide(); - }); - window.addEvent("load", function() { - self.startInterval.delay($(window).getSize().x <= 480 ? 2e3 : 100, self); - }); - }, - notify: function(result) { - var self = this; - var added = new Date(); - added.setTime(result.added * 1e3); - result.el = App.getBlock("notification").addLink(new Element("span." + (result.read ? "read" : "")).adopt(new Element("span.message", { - html: result.message - }), new Element("span.added", { - text: added.timeDiffInWords(), - title: added - })), "top"); - self.notifications.include(result); - if ((result.important !== undefined || result.sticky !== undefined) && !result.read) { - var sticky = true; - App.trigger("message", [ result.message, sticky, result ]); - } else if (!result.read) { - self.setBadge(self.notifications.filter(function(n) { - return !n.read; - }).length); + self.has_tab = true; + } else { + self.has_tab = false; + self.tab = null; + nav.removeTab("shows"); } }, - setBadge: function(value) { + load: function() { var self = this; - self.badge.set("text", value); - self.badge[value ? "show" : "hide"](); - }, - markAsRead: function(force_ids) { - var self = this, ids = force_ids; - if (!force_ids) { - var rn = self.notifications.filter(function(n) { - return !n.read && n.important === undefined; - }); - ids = []; - rn.each(function(n) { - ids.include(n._id); - }); - } - if (ids.length > 0) Api.request("notification.markread", { - data: { - ids: ids.join(",") - }, - onSuccess: function() { - self.setBadge(""); + Api.request("settings", { + onComplete: function(json) { + self.toggleShows(json.values.shows.enabled); } }); + App.on("shows.enabled", self.toggleShows.bind(self)); }, - startInterval: function(force) { + indexAction: function() { var self = this; - if (self.stopped && !force) { - self.stopped = false; - return; - } - self.request = Api.request("notification.listener", { - data: { - init: true + if (!self.wanted) { + self.wanted = new ShowList({ + identifier: "wanted", + status: "active", + type: "show", + actions: [ MA.IMDB, MA.Trailer, MA.Release, MA.Edit, MA.Refresh, MA.Readd, MA.Delete ], + add_new: true, + on_empty_element: App.createUserscriptButtons().addClass("empty_wanted") + }); + $(self.wanted).inject(self.el); + } + } +}); + +var EpisodeAction = new Class({ + Implements: [ Options ], + class_name: "item-action icon2", + initialize: function(episode, options) { + var self = this; + self.setOptions(options); + self.show = episode.show; + self.episode = episode; + self.create(); + if (self.el) self.el.addClass(self.class_name); + }, + create: function() {}, + disable: function() { + if (this.el) this.el.addClass("disable"); + }, + enable: function() { + if (this.el) this.el.removeClass("disable"); + }, + getTitle: function() { + var self = this; + try { + return self.show.getTitle(); + } catch (e) { + try { + return self.show.original_title ? self.show.original_title : self.show.titles[0]; + } catch (e) { + return "Unknown"; + } + } + }, + get: function(key) { + var self = this; + try { + return self.show.get(key); + } catch (e) { + return self.show[key]; + } + }, + createMask: function() { + var self = this; + self.mask = new Element("div.mask", { + styles: { + "z-index": "1" + } + }).inject(self.show, "top").fade("hide"); + }, + toElement: function() { + return this.el || null; + } +}); + +var EA = {}; + +EA.IMDB = new Class({ + Extends: EpisodeAction, + id: null, + create: function() { + var self = this; + self.id = self.show.getIdentifier ? self.show.getIdentifier() : self.get("imdb"); + self.el = new Element("a.imdb", { + title: "Go to the IMDB page of " + self.getTitle(), + href: "http://www.imdb.com/title/" + self.id + "/", + target: "_blank" + }); + if (!self.id) self.disable(); + } +}); + +EA.Release = new Class({ + Extends: EpisodeAction, + create: function() { + var self = this; + self.el = new Element("a.releases.download", { + title: "Show the releases that are available for " + self.getTitle(), + events: { + click: self.toggle.bind(self) + } + }); + self.options = new Element("div.episode-options").inject(self.episode.el); + if (!self.episode.data.releases || self.episode.data.releases.length == 0) self.el.hide(); else self.showHelper(); + App.on("show.searcher.ended", function(notification) { + if (self.show.data._id != notification.data._id) return; + self.releases = null; + if (self.options_container) { + self.options_container.destroy(); + self.options_container = null; + } + }); + }, + toggle: function(e) { + var self = this; + if (self.options && self.options.hasClass("expanded")) { + self.close(); + } else { + self.open(); + } + }, + open: function(e) { + var self = this; + if (e) e.preventDefault(); + self.createReleases(); + }, + close: function(e) { + var self = this; + if (e) e.preventDefault(); + self.options.setStyle("height", 0).removeClass("expanded"); + }, + createReleases: function() { + var self = this; + if (!self.releases_table) { + self.options.adopt(self.releases_table = new Element("div.releases.table")); + new Element("div.item.head").adopt(new Element("span.name", { + text: "Release name" + }), new Element("span.status", { + text: "Status" + }), new Element("span.quality", { + text: "Quality" + }), new Element("span.size", { + text: "Size" + }), new Element("span.age", { + text: "Age" + }), new Element("span.score", { + text: "Score" + }), new Element("span.provider", { + text: "Provider" + })).inject(self.releases_table); + if (self.episode.data.releases) self.episode.data.releases.each(function(release) { + var quality = Quality.getQuality(release.quality) || {}, info = release.info || {}, provider = self.get(release, "provider") + (info["provider_extra"] ? self.get(release, "provider_extra") : ""); + var release_name = self.get(release, "name"); + if (release.files && release.files.length > 0) { + try { + var movie_file = release.files.filter(function(file) { + var type = File.Type.get(file.type_id); + return type && type.identifier == "movie"; + }).pick(); + release_name = movie_file.path.split(Api.getOption("path_sep")).getLast(); + } catch (e) {} + } + release["el"] = new Element("div", { + class: "item " + release.status, + id: "release_" + release._id + }).adopt(new Element("span.name", { + text: release_name, + title: release_name + }), new Element("span.status", { + text: release.status, + class: "status " + release.status + }), new Element("span.quality", { + text: quality.label + (release.is_3d ? " 3D" : "") || "n/a" + }), new Element("span.size", { + text: info["size"] ? Math.floor(self.get(release, "size")) : "n/a" + }), new Element("span.age", { + text: self.get(release, "age") + }), new Element("span.score", { + text: self.get(release, "score") + }), new Element("span.provider", { + text: provider, + title: provider + }), info["detail_url"] ? new Element("a.info.icon2", { + href: info["detail_url"], + target: "_blank" + }) : new Element("a"), new Element("a.download.icon2", { + events: { + click: function(e) { + e.preventDefault(); + if (!this.hasClass("completed")) self.download(release); + } + } + }), new Element("a.delete.icon2", { + events: { + click: function(e) { + e.preventDefault(); + self.ignore(release); + } + } + })).inject(self.releases_table); + if (release.status == "ignored" || release.status == "failed" || release.status == "snatched") { + if (!self.last_release || self.last_release && self.last_release.status != "snatched" && release.status == "snatched") self.last_release = release; + } else if (!self.next_release && release.status == "available") { + self.next_release = release; + } + var update_handle = function(notification) { + if (notification.data._id != release._id) return; + var q = self.show.quality.getElement(".q_" + release.quality), new_status = notification.data.status; + release.el.set("class", "item " + new_status); + var status_el = release.el.getElement(".release_status"); + status_el.set("class", "release_status " + new_status); + status_el.set("text", new_status); + if (!q && (new_status == "snatched" || new_status == "seeding" || new_status == "done")) q = self.addQuality(release.quality_id); + if (q && !q.hasClass(new_status)) { + q.removeClass(release.status).addClass(new_status); + q.set("title", q.get("title").replace(release.status, new_status)); + } + }; + App.on("release.update_status", update_handle); + }); + if (self.last_release) self.releases_table.getElements("#release_" + self.last_release._id).addClass("last_release"); + if (self.next_release) self.releases_table.getElements("#release_" + self.next_release._id).addClass("next_release"); + if (self.next_release || self.last_release && [ "ignored", "failed" ].indexOf(self.last_release.status) === false) { + self.trynext_container = new Element("div.buttons.try_container").inject(self.releases_table, "top"); + var nr = self.next_release, lr = self.last_release; + self.trynext_container.adopt(new Element("span.or", { + text: "If anything went wrong, download" + }), lr ? new Element("a.button.orange", { + text: "the same release again", + events: { + click: function() { + self.download(lr); + } + } + }) : null, nr && lr ? new Element("span.or", { + text: "," + }) : null, nr ? [ new Element("a.button.green", { + text: lr ? "another release" : "the best release", + events: { + click: function() { + self.download(nr); + } + } + }), new Element("span.or", { + text: "or pick one below" + }) ] : null); + } + self.last_release = null; + self.next_release = null; + self.episode.el.addEvent("outerClick", function() { + self.close(); + }); + } + self.options.setStyle("height", self.releases_table.getSize().y).addClass("expanded"); + }, + showHelper: function(e) { + var self = this; + if (e) e.preventDefault(); + var has_available = false, has_snatched = false; + if (self.episode.data.releases) self.episode.data.releases.each(function(release) { + if (has_available && has_snatched) return; + if ([ "snatched", "downloaded", "seeding" ].contains(release.status)) has_snatched = true; + if ([ "available" ].contains(release.status)) has_available = true; + }); + if (has_available || has_snatched) { + self.trynext_container = new Element("div.buttons.trynext").inject(self.show.info_container); + self.trynext_container.adopt(has_available ? [ new Element("a.icon2.readd", { + text: has_snatched ? "Download another release" : "Download the best release", + events: { + click: self.tryNextRelease.bind(self) + } + }), new Element("a.icon2.download", { + text: "pick one yourself", + events: { + click: function() { + self.show.quality.fireEvent("click"); + } + } + }) ] : null, new Element("a.icon2.completed", { + text: "mark this movie done", + events: { + click: self.markMovieDone.bind(self) + } + })); + } + }, + get: function(release, type) { + return release.info && release.info[type] !== undefined ? release.info[type] : "n/a"; + }, + download: function(release) { + var self = this; + var release_el = self.releases_table.getElement("#release_" + release._id), icon = release_el.getElement(".download.icon2"); + if (icon) icon.addClass("icon spinner").removeClass("download"); + Api.request("release.manual_download", { + data: { + id: release._id + }, + onComplete: function(json) { + if (icon) icon.removeClass("icon spinner"); + if (json.success) { + if (icon) icon.addClass("completed"); + release_el.getElement(".release_status").set("text", "snatched"); + } else if (icon) icon.addClass("attention").set("title", "Something went wrong when downloading, please check logs."); + } + }); + }, + ignore: function(release) { + Api.request("release.ignore", { + data: { + id: release._id + } + }); + }, + markMovieDone: function() { + var self = this; + Api.request("media.delete", { + data: { + id: self.show.get("_id"), + delete_from: "wanted" + }, + onComplete: function() { + var movie = $(self.show); + movie.set("tween", { + duration: 300, + onComplete: function() { + self.show.destroy(); + } + }); + movie.tween("height", 0); + } + }); + }, + tryNextRelease: function() { + var self = this; + Api.request("movie.searcher.try_next", { + data: { + media_id: self.show.get("_id") + } + }); + } +}); + +EA.Refresh = new Class({ + Extends: EpisodeAction, + create: function() { + var self = this; + self.el = new Element("a.refresh", { + title: "Refresh the movie info and do a forced search", + events: { + click: self.doRefresh.bind(self) + } + }); + }, + doRefresh: function(e) { + var self = this; + e.preventDefault(); + Api.request("media.refresh", { + data: { + id: self.episode.get("_id") + } + }); + } +}); + +var Episode = new Class({ + Extends: BlockBase, + action: {}, + initialize: function(show, options, data) { + var self = this; + self.setOptions(options); + self.show = show; + self.options = options; + self.data = data; + self.profile = self.show.profile; + self.el = new Element("div.item.episode").adopt(self.detail = new Element("div.item.data")); + self.create(); + }, + create: function() { + var self = this; + self.detail.set("id", "episode_" + self.data._id); + self.detail.adopt(new Element("span.episode", { + text: self.data.info.number || 0 + }), new Element("span.name", { + text: self.getTitle() + }), new Element("span.firstaired", { + text: self.data.info.firstaired + }), self.quality = new Element("span.quality", { + events: { + click: function(e) { + var releases = self.detail.getElement(".item-actions .releases"); + if (releases.isVisible()) releases.fireEvent("click", [ e ]); + } + } + }), self.actions = new Element("div.item-actions")); + if (self.profile.data) { + self.profile.getTypes().each(function(type) { + var q = self.addQuality(type.get("quality"), type.get("3d")); + if ((type.finish == true || type.get("finish")) && !q.hasClass("finish")) { + q.addClass("finish"); + q.set("title", q.get("title") + " Will finish searching for this movie if this quality is found."); + } + }); + } + self.updateReleases(); + Object.each(self.options.actions, function(action, key) { + self.action[key.toLowerCase()] = action = new self.options.actions[key](self); + if (action.el) self.actions.adopt(action); + }); + }, + updateReleases: function() { + var self = this; + if (!self.data.releases || self.data.releases.length == 0) return; + self.data.releases.each(function(release) { + var q = self.quality.getElement(".q_" + release.quality + (release.is_3d ? ".is_3d" : ":not(.is_3d)")), status = release.status; + if (!q && (status == "snatched" || status == "seeding" || status == "done")) q = self.addQuality(release.quality, release.is_3d || false); + if (q && !q.hasClass(status)) { + q.addClass(status); + q.set("title", (q.get("title") ? q.get("title") : "") + " status: " + status); + } + }); + }, + addQuality: function(quality, is_3d) { + var self = this, q = Quality.getQuality(quality); + return new Element("span", { + text: q.label + (is_3d ? " 3D" : ""), + class: "q_" + q.identifier + (is_3d ? " is_3d" : ""), + title: "" + }).inject(self.quality); + }, + getTitle: function() { + var self = this; + var title = ""; + if (self.data.info.titles && self.data.info.titles.length > 0) { + title = self.data.info.titles[0]; + } else { + title = "Episode " + self.data.info.number; + } + return title; + }, + getIdentifier: function() { + var self = this; + try { + return self.get("identifiers").imdb; + } catch (e) {} + return self.get("imdb"); + }, + get: function(attr) { + return this.data[attr] || this.data.info[attr]; + } +}); + +var ShowList = new Class({ + Implements: [ Events, Options ], + options: { + navigation: true, + limit: 50, + load_more: true, + loader: true, + menu: [], + add_new: false, + force_view: false + }, + movies: [], + movies_added: {}, + total_movies: 0, + letters: {}, + filter: null, + initialize: function(options) { + var self = this; + self.setOptions(options); + self.offset = 0; + self.filter = self.options.filter || { + starts_with: null, + search: null + }; + self.el = new Element("div.shows").adopt(self.title = self.options.title ? new Element("h2", { + text: self.options.title, + styles: { + display: "none" + } + }) : null, self.description = self.options.description ? new Element("div.description", { + html: self.options.description, + styles: { + display: "none" + } + }) : null, self.movie_list = new Element("div.list"), self.load_more = self.options.load_more ? new Element("a.load_more", { + events: { + click: self.loadMore.bind(self) + } + }) : null); + if ($(window).getSize().x <= 480 && !self.options.force_view) self.changeView("list"); else self.changeView(self.getSavedView() || self.options.view || "details"); + self.getMovies(); + App.on("movie.added", self.movieAdded.bind(self)); + App.on("movie.deleted", self.movieDeleted.bind(self)); + }, + movieDeleted: function(notification) { + var self = this; + if (self.movies_added[notification.data._id]) { + self.movies.each(function(movie) { + if (movie.get("_id") == notification.data._id) { + movie.destroy(); + delete self.movies_added[notification.data._id]; + self.setCounter(self.counter_count - 1); + self.total_movies--; + } + }); + } + self.checkIfEmpty(); + }, + movieAdded: function(notification) { + var self = this; + self.fireEvent("movieAdded", notification); + if (self.options.add_new && !self.movies_added[notification.data._id] && notification.data.status == self.options.status) { + window.scroll(0, 0); + self.createShow(notification.data, "top"); + self.setCounter(self.counter_count + 1); + self.checkIfEmpty(); + } + }, + create: function() { + var self = this; + if (self.options.navigation) self.createNavigation(); + if (self.options.load_more) self.scrollspy = new ScrollSpy({ + min: function() { + var c = self.load_more.getCoordinates(); + return c.top - window.document.getSize().y - 300; + }, + onEnter: self.loadMore.bind(self) + }); + self.created = true; + }, + addMovies: function(movies, total) { + var self = this; + if (!self.created) self.create(); + if (movies.length < self.options.limit && self.scrollspy) { + self.load_more.hide(); + self.scrollspy.stop(); + } + Object.each(movies, function(movie) { + self.createShow(movie); + }); + self.total_movies += total; + self.setCounter(total); + }, + setCounter: function(count) { + var self = this; + if (!self.navigation_counter) return; + self.counter_count = count; + self.navigation_counter.set("text", (count || 0) + " shows"); + if (self.empty_message) { + self.empty_message.destroy(); + self.empty_message = null; + } + if (self.total_movies && count == 0 && !self.empty_message) { + var message = (self.filter.search ? 'for "' + self.filter.search + '"' : "") + (self.filter.starts_with ? " in " + self.filter.starts_with + "" : ""); + self.empty_message = new Element(".message", { + html: "No shows found " + message + ".
" + }).grab(new Element("a", { + text: "Reset filter", + events: { + click: function() { + self.filter = { + starts_with: null, + search: null + }; + self.navigation_search_input.set("value", ""); + self.reset(); + self.activateLetter(); + self.getMovies(true); + self.last_search_value = ""; + } + } + })).inject(self.movie_list); + } + }, + createShow: function(show, inject_at) { + var self = this; + var m = new Show(self, { + actions: self.options.actions, + view: self.current_view, + onSelect: self.calculateSelected.bind(self) + }, show); + $(m).inject(self.movie_list, inject_at || "bottom"); + m.fireEvent("injected"); + self.movies.include(m); + self.movies_added[show._id] = true; + }, + createNavigation: function() { + var self = this; + var chars = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + self.el.addClass("with_navigation"); + self.navigation = new Element("div.alph_nav").adopt(self.mass_edit_form = new Element("div.mass_edit_form").adopt(new Element("span.select").adopt(self.mass_edit_select = new Element("input[type=checkbox].inlay", { + events: { + change: self.massEditToggleAll.bind(self) + } + }), self.mass_edit_selected = new Element("span.count", { + text: 0 + }), self.mass_edit_selected_label = new Element("span", { + text: "selected" + })), new Element("div.quality").adopt(self.mass_edit_quality = new Element("select"), new Element("a.button.orange", { + text: "Change quality", + events: { + click: self.changeQualitySelected.bind(self) + } + })), new Element("div.delete").adopt(new Element("span[text=or]"), new Element("a.button.red", { + text: "Delete", + events: { + click: self.deleteSelected.bind(self) + } + })), new Element("div.refresh").adopt(new Element("span[text=or]"), new Element("a.button.green", { + text: "Refresh", + events: { + click: self.refreshSelected.bind(self) + } + }))), new Element("div.menus").adopt(self.navigation_counter = new Element("span.counter[title=Total]"), self.filter_menu = new Block.Menu(self, { + class: "filter" + }), self.navigation_actions = new Element("ul.actions", { + events: { + "click:relay(li)": function(e, el) { + var a = "active"; + self.navigation_actions.getElements("." + a).removeClass(a); + self.changeView(el.get("data-view")); + this.addClass(a); + el.inject(el.getParent(), "top"); + el.getSiblings().hide(); + setTimeout(function() { + el.getSiblings().setStyle("display", null); + }, 100); + } + } + }), self.navigation_menu = new Block.Menu(self, { + class: "extra" + }))).inject(self.el, "top"); + self.mass_edit_select_class = new Form.Check(self.mass_edit_select); + Quality.getActiveProfiles().each(function(profile) { + new Element("option", { + value: profile.get("_id"), + text: profile.get("label") + }).inject(self.mass_edit_quality); + }); + self.filter_menu.addLink(self.navigation_search_input = new Element("input", { + title: "Search through " + self.options.identifier, + placeholder: "Search through " + self.options.identifier, + events: { + keyup: self.search.bind(self), + change: self.search.bind(self) + } + })).addClass("search"); + var available_chars; + self.filter_menu.addEvent("open", function() { + self.navigation_search_input.focus(); + if (!available_chars && (self.navigation.isDisplayed() || self.navigation.isVisible())) Api.request("media.available_chars", { + data: Object.merge({ + type: "show", + status: self.options.status + }, self.filter), + onSuccess: function(json) { + available_chars = json.chars; + available_chars.each(function(c) { + self.letters[c.capitalize()].addClass("available"); + }); + } + }); + }); + self.filter_menu.addLink(self.navigation_alpha = new Element("ul.numbers", { + events: { + "click:relay(li.available)": function(e, el) { + self.activateLetter(el.get("data-letter")); + self.getMovies(true); + } + } + })); + [ "mass_edit", "details", "list" ].each(function(view) { + var current = self.current_view == view; + new Element("li", { + class: "icon2 " + view + (current ? " active " : ""), + "data-view": view + }).inject(self.navigation_actions, current ? "top" : "bottom"); + }); + self.letters["all"] = new Element("li.letter_all.available.active", { + text: "ALL" + }).inject(self.navigation_alpha); + chars.split("").each(function(c) { + self.letters[c] = new Element("li", { + text: c, + class: "letter_" + c, + "data-letter": c + }).inject(self.navigation_alpha); + }); + if (self.options.menu.length > 0) self.options.menu.each(function(menu_item) { + self.navigation_menu.addLink(menu_item); + }); else self.navigation_menu.hide(); + }, + calculateSelected: function() { + var self = this; + var selected = 0, movies = self.movies.length; + self.movies.each(function(movie) { + selected += movie.isSelected() ? 1 : 0; + }); + var indeterminate = selected > 0 && selected < movies, checked = selected == movies && selected > 0; + self.mass_edit_select.set("indeterminate", indeterminate); + self.mass_edit_select_class[checked ? "check" : "uncheck"](); + self.mass_edit_select_class.element[indeterminate ? "addClass" : "removeClass"]("indeterminate"); + self.mass_edit_selected.set("text", selected); + }, + deleteSelected: function() { + var self = this, ids = self.getSelectedMovies(), help_msg = self.identifier == "wanted" ? "If you do, you won't be able to watch them, as they won't get downloaded!" : "Your files will be safe, this will only delete the reference from the CouchPotato manage list"; + var qObj = new Question("Are you sure you want to delete " + ids.length + " movie" + (ids.length != 1 ? "s" : "") + "?", help_msg, [ { + text: "Yes, delete " + (ids.length != 1 ? "them" : "it"), + class: "delete", + events: { + click: function(e) { + e.preventDefault(); + this.set("text", "Deleting.."); + Api.request("media.delete", { + method: "post", + data: { + id: ids.join(","), + delete_from: self.options.identifier + }, + onSuccess: function() { + qObj.close(); + var erase_movies = []; + self.movies.each(function(movie) { + if (movie.isSelected()) { + $(movie).destroy(); + erase_movies.include(movie); + } + }); + erase_movies.each(function(movie) { + self.movies.erase(movie); + movie.destroy(); + self.setCounter(self.counter_count - 1); + self.total_movies--; + }); + self.calculateSelected(); + } + }); + } + } + }, { + text: "Cancel", + cancel: true + } ]); + }, + changeQualitySelected: function() { + var self = this; + var ids = self.getSelectedMovies(); + Api.request("movie.edit", { + method: "post", + data: { + id: ids.join(","), + profile_id: self.mass_edit_quality.get("value") + }, + onSuccess: self.search.bind(self) + }); + }, + refreshSelected: function() { + var self = this; + var ids = self.getSelectedMovies(); + Api.request("media.refresh", { + method: "post", + data: { + id: ids.join(",") + } + }); + }, + getSelectedMovies: function() { + var self = this; + var ids = []; + self.movies.each(function(movie) { + if (movie.isSelected()) ids.include(movie.get("_id")); + }); + return ids; + }, + massEditToggleAll: function() { + var self = this; + var select = self.mass_edit_select.get("checked"); + self.movies.each(function(movie) { + movie.select(select); + }); + self.calculateSelected(); + }, + reset: function() { + var self = this; + self.movies = []; + if (self.mass_edit_select) self.calculateSelected(); + if (self.navigation_alpha) self.navigation_alpha.getElements(".active").removeClass("active"); + self.offset = 0; + if (self.scrollspy) { + self.scrollspy.start(); + } + }, + activateLetter: function(letter) { + var self = this; + self.reset(); + self.letters[letter || "all"].addClass("active"); + self.filter.starts_with = letter; + }, + changeView: function(new_view) { + var self = this; + self.el.removeClass(self.current_view + "_list").addClass(new_view + "_list"); + self.current_view = new_view; + Cookie.write(self.options.identifier + "_view2", new_view, { + duration: 1e3 + }); + }, + getSavedView: function() { + var self = this; + return Cookie.read(self.options.identifier + "_view2"); + }, + search: function() { + var self = this; + if (self.search_timer) clearTimeout(self.search_timer); + self.search_timer = function() { + var search_value = self.navigation_search_input.get("value"); + if (search_value == self.last_search_value) return; + self.reset(); + self.activateLetter(); + self.filter.search = search_value; + self.getMovies(true); + self.last_search_value = search_value; + }.delay(250); + }, + update: function() { + var self = this; + self.reset(); + self.getMovies(true); + }, + getMovies: function(reset) { + var self = this; + if (self.scrollspy) { + self.scrollspy.stop(); + self.load_more.set("text", "loading..."); + } + if (self.movies.length == 0 && self.options.loader) { + self.loader_first = new Element("div.loading").adopt(new Element("div.message", { + text: self.options.title ? "Loading '" + self.options.title + "'" : "Loading..." + })).inject(self.el, "top"); + createSpinner(self.loader_first, { + radius: 4, + length: 4, + width: 1 + }); + self.el.setStyle("min-height", 93); + } + Api.request(self.options.api_call || "media.list", { + data: Object.merge({ + type: self.options.type || "movie", + status: self.options.status, + limit_offset: self.options.limit ? self.options.limit + "," + self.offset : null + }, self.filter), + onSuccess: function(json) { + if (reset) self.movie_list.empty(); + if (self.loader_first) { + var lf = self.loader_first; + self.loader_first.addClass("hide"); + self.loader_first = null; + setTimeout(function() { + lf.destroy(); + }, 2e4); + self.el.setStyle("min-height", null); + } + self.store(json.shows); + self.addMovies(json.shows, json.total || json.shows.length); + if (self.scrollspy) { + self.load_more.set("text", "load more movies"); + self.scrollspy.start(); + } + self.checkIfEmpty(); + self.fireEvent("loaded"); + } + }); + }, + loadMore: function() { + var self = this; + if (self.offset >= self.options.limit) self.getMovies(); + }, + store: function(movies) { + var self = this; + self.offset += movies.length; + }, + checkIfEmpty: function() { + var self = this; + var is_empty = self.movies.length == 0 && (self.total_movies == 0 || self.total_movies === undefined); + if (self.title) self.title[is_empty ? "hide" : "show"](); + if (self.description) self.description.setStyle("display", [ is_empty ? "none" : "" ]); + if (is_empty && self.options.on_empty_element) { + self.options.on_empty_element.inject(self.loader_first || self.title || self.movie_list, "after"); + if (self.navigation) self.navigation.hide(); + self.empty_element = self.options.on_empty_element; + } else if (self.empty_element) { + self.empty_element.destroy(); + if (self.navigation) self.navigation.show(); + } + }, + toElement: function() { + return this.el; + } +}); + +Block.Search.ShowItem = new Class({ + Implements: [ Options, Events ], + initialize: function(info, options) { + var self = this; + self.setOptions(options); + self.info = info; + self.alternative_titles = []; + self.create(); + }, + create: function() { + var self = this, info = self.info; + self.el = new Element("div.media_result", { + id: info.id + }).adopt(self.thumbnail = info.images && info.images.poster.length > 0 ? new Element("img.thumbnail", { + src: info.images.poster[0], + height: null, + width: null + }) : null, self.options_el = new Element("div.options.inlay"), self.data_container = new Element("div.data", { + events: { + click: self.showOptions.bind(self) + } + }).adopt(self.info_container = new Element("div.info").adopt(new Element("h2").adopt(self.title = new Element("span.title", { + text: info.titles && info.titles.length > 0 ? info.titles[0] : "Unknown" + }), self.year = info.year ? new Element("span.year", { + text: info.year + }) : null)))); + if (info.titles) info.titles.each(function(title) { + self.alternativeTitle({ + title: title + }); + }); + }, + alternativeTitle: function(alternative) { + var self = this; + self.alternative_titles.include(alternative); + }, + getTitle: function() { + var self = this; + try { + return self.info.original_title ? self.info.original_title : self.info.titles[0]; + } catch (e) { + return "Unknown"; + } + }, + get: function(key) { + return this.info[key]; + }, + showOptions: function() { + var self = this; + self.createOptions(); + self.data_container.addClass("open"); + self.el.addEvent("outerClick", self.closeOptions.bind(self)); + }, + closeOptions: function() { + var self = this; + self.data_container.removeClass("open"); + self.el.removeEvents("outerClick"); + }, + add: function(e) { + var self = this; + if (e) e.preventDefault(); + self.loadingMask(); + Api.request("show.add", { + data: { + identifiers: self.info.identifiers, + type: self.info.type, + title: self.title_select.get("value"), + profile_id: self.profile_select.get("value"), + category_id: self.category_select.get("value") + }, + onComplete: function(json) { + self.options_el.empty(); + self.options_el.adopt(new Element("div.message", { + text: json.success ? "Show successfully added." : "Show didn't add properly. Check logs" + })); + self.mask.fade("out"); + self.fireEvent("added"); + }, + onFailure: function() { + self.options_el.empty(); + self.options_el.adopt(new Element("div.message", { + text: "Something went wrong, check the logs for more info." + })); + self.mask.fade("out"); + } + }); + }, + createOptions: function() { + var self = this, info = self.info; + if (!self.options_el.hasClass("set")) { + if (self.info.in_library) { + var in_library = []; + self.info.in_library.releases.each(function(release) { + in_library.include(release.quality.label); + }); + } + self.options_el.grab(new Element("div", { + class: self.info.in_wanted && self.info.in_wanted.profile_id || in_library ? "in_library_wanted" : "" + }).adopt(self.info.in_wanted && self.info.in_wanted.profile_id ? new Element("span.in_wanted", { + text: "Already in wanted list: " + Quality.getProfile(self.info.in_wanted.profile_id).get("label") + }) : in_library ? new Element("span.in_library", { + text: "Already in library: " + in_library.join(", ") + }) : null, self.title_select = new Element("select", { + name: "title" + }), self.profile_select = new Element("select", { + name: "profile" + }), self.category_select = new Element("select", { + name: "category" + }).grab(new Element("option", { + value: -1, + text: "None" + })), self.add_button = new Element("a.button", { + text: "Add", + events: { + click: self.add.bind(self) + } + }))); + Array.each(self.alternative_titles, function(alt) { + new Element("option", { + text: alt.title + }).inject(self.title_select); + }); + var categories = CategoryList.getAll(); + if (categories.length == 0) self.category_select.hide(); else { + self.category_select.show(); + categories.each(function(category) { + new Element("option", { + value: category.data._id, + text: category.data.label + }).inject(self.category_select); + }); + } + var profiles = Quality.getActiveProfiles(); + if (profiles.length == 1) self.profile_select.hide(); + profiles.each(function(profile) { + new Element("option", { + value: profile.get("_id"), + text: profile.get("label") + }).inject(self.profile_select); + }); + self.options_el.addClass("set"); + if (categories.length == 0 && self.title_select.getElements("option").length == 1 && profiles.length == 1 && !(self.info.in_wanted && self.info.in_wanted.profile_id || in_library)) self.add(); + } + }, + loadingMask: function() { + var self = this; + self.mask = new Element("div.mask").inject(self.el).fade("hide"); + createSpinner(self.mask); + self.mask.fade("in"); + }, + toElement: function() { + return this.el; + } +}); + +var Season = new Class({ + Extends: BlockBase, + action: {}, + initialize: function(show, options, data) { + var self = this; + self.setOptions(options); + self.show = show; + self.options = options; + self.data = data; + self.profile = self.show.profile; + self.el = new Element("div.item.season").adopt(self.detail = new Element("div.item.data")); + self.create(); + }, + create: function() { + var self = this; + self.detail.set("id", "season_" + self.data._id); + self.detail.adopt(new Element("span.name", { + text: self.getTitle() + }), self.quality = new Element("span.quality", { + events: { + click: function(e) { + var releases = self.detail.getElement(".item-actions .releases"); + if (releases.isVisible()) releases.fireEvent("click", [ e ]); + } + } + }), self.actions = new Element("div.item-actions")); + if (self.profile.data) { + self.profile.getTypes().each(function(type) { + var q = self.addQuality(type.get("quality"), type.get("3d")); + if ((type.finish == true || type.get("finish")) && !q.hasClass("finish")) { + q.addClass("finish"); + q.set("title", q.get("title") + " Will finish searching for this movie if this quality is found."); + } + }); + } + self.updateReleases(); + Object.each(self.options.actions, function(action, key) { + self.action[key.toLowerCase()] = action = new self.options.actions[key](self); + if (action.el) self.actions.adopt(action); + }); + }, + updateReleases: function() { + var self = this; + if (!self.data.releases || self.data.releases.length == 0) return; + self.data.releases.each(function(release) { + var q = self.quality.getElement(".q_" + release.quality + (release.is_3d ? ".is_3d" : ":not(.is_3d)")), status = release.status; + if (!q && (status == "snatched" || status == "seeding" || status == "done")) q = self.addQuality(release.quality, release.is_3d || false); + if (q && !q.hasClass(status)) { + q.addClass(status); + q.set("title", (q.get("title") ? q.get("title") : "") + " status: " + status); + } + }); + }, + addQuality: function(quality, is_3d) { + var self = this, q = Quality.getQuality(quality); + return new Element("span", { + text: q.label + (is_3d ? " 3D" : ""), + class: "q_" + q.identifier + (is_3d ? " is_3d" : ""), + title: "" + }).inject(self.quality); + }, + getTitle: function() { + var self = this; + var title = ""; + if (self.data.info.number) { + title = "Season " + self.data.info.number; + } else { + title = "Specials"; + } + return title; + }, + getIdentifier: function() { + var self = this; + try { + return self.get("identifiers").imdb; + } catch (e) {} + return self.get("imdb"); + }, + get: function(attr) { + return this.data[attr] || this.data.info[attr]; + } +}); + +var Episodes = new Class({ + initialize: function(show, options) { + var self = this; + self.show = show; + self.options = options; + }, + open: function() { + var self = this; + if (!self.container) { + self.container = new Element("div.options").grab(self.episodes_container = new Element("div.episodes.table")); + self.container.inject(self.show, "top"); + Api.request("library.tree", { + data: { + media_id: self.show.data._id + }, + onComplete: function(json) { + self.data = json.result; + self.createEpisodes(); + } + }); + } + self.show.slide("in", self.container, true); + }, + createEpisodes: function() { + var self = this; + self.data.seasons.sort(self.sortSeasons); + self.data.seasons.each(function(season) { + self.createSeason(season); + season.episodes.sort(self.sortEpisodes); + season.episodes.each(function(episode) { + self.createEpisode(episode); + }); + }); + }, + createSeason: function(season) { + var self = this, s = new Season(self.show, self.options, season); + $(s).inject(self.episodes_container); + }, + createEpisode: function(episode) { + var self = this, e = new Episode(self.show, self.options, episode); + $(e).inject(self.episodes_container); + }, + sortSeasons: function(a, b) { + if (!a.info.number) { + return 1; + } + if (!b.info.number) { + return -1; + } + if (a.info.number < b.info.number) return -1; + if (a.info.number > b.info.number) return 1; + return 0; + }, + sortEpisodes: function(a, b) { + if (a.info.number < b.info.number) return -1; + if (a.info.number > b.info.number) return 1; + return 0; + } +}); + +var Show = new Class({ + Extends: BlockBase, + action: {}, + initialize: function(list, options, data) { + var self = this; + self.data = data; + self.view = options.view || "details"; + self.list = list; + self.el = new Element("div.show"); + self.episodes = new Episodes(self, { + actions: [ EA.IMDB, EA.Release, EA.Refresh ] + }); + self.profile = Quality.getProfile(data.profile_id) || {}; + self.category = CategoryList.getCategory(data.category_id) || {}; + self.parent(self, options); + self.addEvents(); + }, + addEvents: function() { + var self = this; + self.global_events = {}; + self.global_events["movie.update"] = function(notification) { + if (self.data._id != notification.data._id) return; + self.busy(false); + self.removeView(); + self.update.delay(2e3, self, notification); + }; + App.on("movie.update", self.global_events["movie.update"]); + [ "media.busy", "movie.searcher.started" ].each(function(listener) { + self.global_events[listener] = function(notification) { + if (notification.data && (self.data._id == notification.data._id || typeOf(notification.data._id) == "array" && notification.data._id.indexOf(self.data._id) > -1)) self.busy(true); + }; + App.on(listener, self.global_events[listener]); + }); + self.global_events["movie.searcher.ended"] = function(notification) { + if (notification.data && self.data._id == notification.data._id) self.busy(false); + }; + App.on("movie.searcher.ended", self.global_events["movie.searcher.ended"]); + self.global_events["release.update_status"] = function(notification) { + var data = notification.data; + if (data && self.data._id == data.movie_id) { + if (!self.data.releases) self.data.releases = []; + self.data.releases.push({ + quality: data.quality, + status: data.status + }); + self.updateReleases(); + } + }; + App.on("release.update_status", self.global_events["release.update_status"]); + }, + destroy: function() { + var self = this; + self.el.destroy(); + delete self.list.movies_added[self.get("id")]; + self.list.movies.erase(self); + self.list.checkIfEmpty(); + Object.each(self.global_events, function(handle, listener) { + App.off(listener, handle); + }); + }, + busy: function(set_busy, timeout) { + var self = this; + if (!set_busy) { + setTimeout(function() { + if (self.spinner) { + self.mask.fade("out"); + setTimeout(function() { + if (self.mask) self.mask.destroy(); + if (self.spinner) self.spinner.el.destroy(); + self.spinner = null; + self.mask = null; + }, timeout || 400); + } + }, timeout || 1e3); + } else if (!self.spinner) { + self.createMask(); + self.spinner = createSpinner(self.mask); + self.mask.fade("in"); + } + }, + createMask: function() { + var self = this; + self.mask = new Element("div.mask", { + styles: { + "z-index": 4 + } + }).inject(self.el, "top").fade("hide"); + }, + update: function(notification) { + var self = this; + self.data = notification.data; + self.el.empty(); + self.removeView(); + self.profile = Quality.getProfile(self.data.profile_id) || {}; + self.category = CategoryList.getCategory(self.data.category_id) || {}; + self.create(); + self.busy(false); + }, + create: function() { + var self = this; + self.el.addClass("status_" + self.get("status")); + var eta = null, eta_date = null, now = Math.round(+new Date() / 1e3); + if (self.data.info.release_date) [ self.data.info.release_date.dvd, self.data.info.release_date.theater ].each(function(timestamp) { + if (timestamp > 0 && (eta == null || Math.abs(timestamp - now) < Math.abs(eta - now))) eta = timestamp; + }); + if (eta) { + eta_date = new Date(eta * 1e3); + eta_date = eta_date.toLocaleString("en-us", { + month: "long" + }) + " " + eta_date.getFullYear(); + } + self.el.adopt(self.select_checkbox = new Element("input[type=checkbox].inlay", { + events: { + change: function() { + self.fireEvent("select"); + } + } + }), self.thumbnail = self.data.files && self.data.files.image_poster ? new Element("img", { + class: "type_image poster", + src: Api.createUrl("file.cache") + self.data.files.image_poster[0].split(Api.getOption("path_sep")).pop() + }) : null, self.data_container = new Element("div.data.inlay.light").adopt(self.info_container = new Element("div.info").adopt(new Element("div.title").adopt(self.title = new Element("a", { + events: { + click: function(e) { + self.episodes.open(); + } + }, + text: self.getTitle() || "n/a" + }), self.year = new Element("div.year", { + text: self.data.info.year || "n/a" + })), self.description = new Element("div.description.tiny_scroll", { + text: self.data.info.plot + }), self.eta = eta_date && now + 8035200 > eta ? new Element("div.eta", { + text: eta_date, + title: "ETA" + }) : null, self.quality = new Element("div.quality", { + events: { + click: function(e) { + var releases = self.el.getElement(".actions .releases"); + if (releases.isVisible()) releases.fireEvent("click", [ e ]); + } + } + })), self.actions = new Element("div.actions"))); + if (!self.thumbnail) self.el.addClass("no_thumbnail"); + self.select_checkbox_class = new Form.Check(self.select_checkbox); + if (self.profile.data) self.profile.getTypes().each(function(type) { + var q = self.addQuality(type.get("quality"), type.get("3d")); + if ((type.finish == true || type.get("finish")) && !q.hasClass("finish")) { + q.addClass("finish"); + q.set("title", q.get("title") + " Will finish searching for this movie if this quality is found."); + } + }); + self.updateReleases(); + Object.each(self.options.actions, function(action, key) { + self.action[key.toLowerCase()] = action = new self.options.actions[key](self); + if (action.el) self.actions.adopt(action); + }); + }, + updateReleases: function() { + var self = this; + if (!self.data.releases || self.data.releases.length == 0) return; + self.data.releases.each(function(release) { + var q = self.quality.getElement(".q_" + release.quality + (release.is_3d ? ".is_3d" : ":not(.is_3d)")), status = release.status; + if (!q && (status == "snatched" || status == "seeding" || status == "done")) q = self.addQuality(release.quality, release.is_3d || false); + if (q && !q.hasClass(status)) { + q.addClass(status); + q.set("title", (q.get("title") ? q.get("title") : "") + " status: " + status); + } + }); + }, + addQuality: function(quality, is_3d) { + var self = this; + var q = Quality.getQuality(quality); + return new Element("span", { + text: q.label + (is_3d ? " 3D" : ""), + class: "q_" + q.identifier + (is_3d ? " is_3d" : ""), + title: "" + }).inject(self.quality); + }, + getTitle: function() { + var self = this; + if (self.data.title) return self.getUnprefixedTitle(self.data.title); else if (self.data.info.titles.length > 0) return self.getUnprefixedTitle(self.data.info.titles[0]); + return "Unknown movie"; + }, + getUnprefixedTitle: function(t) { + if (t.substr(0, 4).toLowerCase() == "the ") t = t.substr(4) + ", The"; else if (t.substr(0, 3).toLowerCase() == "an ") t = t.substr(3) + ", An"; else if (t.substr(0, 2).toLowerCase() == "a ") t = t.substr(2) + ", A"; + return t; + }, + slide: function(direction, el, expand) { + var self = this; + if (direction == "in") { + self.temp_view = self.view; + self.changeView("details"); + self.el.addEvent("outerClick", function() { + self.removeView(); + self.slide("out"); + }); + el.show(); + if (expand === true) { + self.el.addClass("expanded"); + self.el.getElements(".table").addClass("expanded"); + } + self.data_container.addClass("hide_right"); + } else { + self.el.removeEvents("outerClick"); + setTimeout(function() { + if (self.el) { + self.el.getElements("> :not(.data):not(.poster):not(.movie_container)").hide(); + self.el.getElements(".table").removeClass("expanded"); + } + }, 600); + self.el.removeClass("expanded"); + self.data_container.removeClass("hide_right"); + } + }, + changeView: function(new_view) { + var self = this; + if (self.el) self.el.removeClass(self.view + "_view").addClass(new_view + "_view"); + self.view = new_view; + }, + removeView: function() { + var self = this; + self.el.removeClass(self.view + "_view"); + }, + getIdentifier: function() { + var self = this; + try { + return self.get("identifiers").imdb; + } catch (e) {} + return self.get("imdb"); + }, + get: function(attr) { + return this.data[attr] || this.data.info[attr]; + }, + select: function(bool) { + var self = this; + self.select_checkbox_class[bool ? "check" : "uncheck"](); + }, + isSelected: function() { + return this.select_checkbox.get("checked"); + }, + toElement: function() { + return this.el; + } +}); + +var NotificationBase = new Class({ + Extends: BlockBase, + Implements: [ Options, Events ], + initialize: function(options) { + var self = this; + self.setOptions(options); + App.addEvent("unload", self.stopPoll.bind(self)); + App.addEvent("reload", self.startInterval.bind(self, [ true ])); + App.on("notification", self.notify.bind(self)); + App.on("message", self.showMessage.bind(self)); + App.addEvent("loadSettings", self.addTestButtons.bind(self)); + self.notifications = []; + App.addEvent("load", function() { + App.block.notification = new BlockMenu(self, { + button_class: "icon-notifications", + class: "notification_menu", + onOpen: self.markAsRead.bind(self) + }); + $(App.block.notification).inject(App.getBlock("search"), "after"); + self.badge = new Element("div.badge").inject(App.block.notification, "top").hide(); + }); + window.addEvent("load", function() { + self.startInterval.delay($(window).getSize().x <= 480 ? 2e3 : 100, self); + }); + }, + notify: function(result) { + var self = this; + var added = new Date(); + added.setTime(result.added * 1e3); + result.el = App.getBlock("notification").addLink(new Element("span." + (result.read ? "read" : "")).adopt(new Element("span.message", { + html: result.message + }), new Element("span.added", { + text: added.timeDiffInWords(), + title: added + })), "top"); + self.notifications.include(result); + if ((result.important !== undefined || result.sticky !== undefined) && !result.read) { + var sticky = true; + App.trigger("message", [ result.message, sticky, result ]); + } else if (!result.read) { + self.setBadge(self.notifications.filter(function(n) { + return !n.read; + }).length); + } + }, + setBadge: function(value) { + var self = this; + self.badge.set("text", value); + self.badge[value ? "show" : "hide"](); + }, + markAsRead: function(force_ids) { + var self = this, ids = force_ids; + if (!force_ids) { + var rn = self.notifications.filter(function(n) { + return !n.read && n.important === undefined; + }); + ids = []; + rn.each(function(n) { + ids.include(n._id); + }); + } + if (ids.length > 0) Api.request("notification.markread", { + data: { + ids: ids.join(",") + }, + onSuccess: function() { + self.setBadge(""); + } + }); + }, + startInterval: function(force) { + var self = this; + if (self.stopped && !force) { + self.stopped = false; + return; + } + self.request = Api.request("notification.listener", { + data: { + init: true }, onSuccess: function(json) { self.processData(json, true); @@ -3415,185 +5061,6 @@ Profile.Type = new Class({ } }); -var QualityBase = new Class({ - tab: "", - content: "", - setup: function(data) { - var self = this; - self.qualities = data.qualities; - self.profiles_list = null; - self.profiles = []; - Array.each(data.profiles, self.createProfilesClass.bind(self)); - App.addEvent("loadSettings", self.addSettings.bind(self)); - }, - getProfile: function(id) { - return this.profiles.filter(function(profile) { - return profile.data._id == id; - }).pick(); - }, - getActiveProfiles: function() { - return Array.filter(this.profiles, function(profile) { - return !profile.data.hide; - }); - }, - getQuality: function(identifier) { - try { - return this.qualities.filter(function(q) { - return q.identifier == identifier; - }).pick(); - } catch (e) {} - return {}; - }, - addSettings: function() { - var self = this; - self.settings = App.getPage("Settings"); - self.settings.addEvent("create", function() { - var tab = self.settings.createSubTab("profile", { - label: "Quality", - name: "profile", - subtab_label: "Qualities" - }, self.settings.tabs.searcher, "searcher"); - self.tab = tab.tab; - self.content = tab.content; - self.createProfiles(); - self.createProfileOrdering(); - self.createSizes(); - }); - }, - createProfiles: function() { - var self = this; - var non_core_profiles = Array.filter(self.profiles, function(profile) { - return !profile.isCore(); - }); - var count = non_core_profiles.length; - self.settings.createGroup({ - label: "Quality Profiles", - description: "Create your own profiles with multiple qualities." - }).inject(self.content).adopt(self.profile_container = new Element("div.container"), new Element("a.add_new_profile", { - text: count > 0 ? "Create another quality profile" : "Click here to create a quality profile.", - events: { - click: function() { - var profile = self.createProfilesClass(); - $(profile).inject(self.profile_container); - } - } - })); - Array.each(non_core_profiles, function(profile) { - $(profile).inject(self.profile_container); - }); - }, - createProfilesClass: function(data) { - var self = this; - data = data || { - id: randomString() - }; - var profile = new Profile(data); - self.profiles.include(profile); - return profile; - }, - createProfileOrdering: function() { - var self = this; - self.settings.createGroup({ - label: "Profile Defaults", - description: "(Needs refresh '" + (App.isMac() ? "CMD+R" : "F5") + "' after editing)" - }).grab(new Element(".ctrlHolder#profile_ordering").adopt(new Element("label[text=Order]"), self.profiles_list = new Element("ul"), new Element("p.formHint", { - html: "Change the order the profiles are in the dropdown list. Uncheck to hide it completely.
First one will be default." - }))).inject(self.content); - Array.each(self.profiles, function(profile) { - var check; - new Element("li", { - "data-id": profile.data._id - }).adopt(check = new Element("input[type=checkbox]", { - checked: !profile.data.hide, - events: { - change: self.saveProfileOrdering.bind(self) - } - }), new Element("span.profile_label", { - text: profile.data.label - }), new Element("span.handle.icon-handle")).inject(self.profiles_list); - }); - var sorted_changed = false; - self.profile_sortable = new Sortables(self.profiles_list, { - revert: true, - handle: ".handle", - opacity: .5, - onSort: function() { - sorted_changed = true; - }, - onComplete: function() { - if (sorted_changed) { - self.saveProfileOrdering(); - sorted_changed = false; - } - } - }); - }, - saveProfileOrdering: function() { - var self = this, ids = [], hidden = []; - self.profiles_list.getElements("li").each(function(el, nr) { - ids.include(el.get("data-id")); - hidden[nr] = +!el.getElement("input[type=checkbox]").get("checked"); - }); - Api.request("profile.save_order", { - data: { - ids: ids, - hidden: hidden - } - }); - }, - createSizes: function() { - var self = this; - var group = self.settings.createGroup({ - label: "Sizes", - description: "Edit the minimal and maximum sizes (in MB) for each quality.", - advanced: true, - name: "sizes" - }).inject(self.content); - new Element("div.item.head.ctrlHolder").adopt(new Element("span.label", { - text: "Quality" - }), new Element("span.min", { - text: "Min" - }), new Element("span.max", { - text: "Max" - })).inject(group); - Array.each(self.qualities, function(quality) { - new Element("div.ctrlHolder.item").adopt(new Element("span.label", { - text: quality.label - }), new Element("input.min[type=text]", { - value: quality.size_min, - events: { - keyup: function(e) { - self.changeSize(quality.identifier, "size_min", e.target.get("value")); - } - } - }), new Element("input.max[type=text]", { - value: quality.size_max, - events: { - keyup: function(e) { - self.changeSize(quality.identifier, "size_max", e.target.get("value")); - } - } - })).inject(group); - }); - }, - size_timer: {}, - changeSize: function(identifier, type, value) { - var self = this; - if (self.size_timer[identifier + type]) clearTimeout(self.size_timer[identifier + type]); - self.size_timer[identifier + type] = function() { - Api.request("quality.size.save", { - data: { - identifier: identifier, - value_type: type, - value: value - } - }); - }.delay(300); - } -}); - -window.Quality = new QualityBase(); - Page.Userscript = new Class({ Extends: PageBase, order: 80,