From 20772d4ff5525e78d42eaf3b997a0e6685bad486 Mon Sep 17 00:00:00 2001 From: Ruud Date: Sun, 2 Oct 2011 23:05:31 +0200 Subject: [PATCH] About page in config, base --- .../static/scripts/library/templated.js | 411 ++++++++++++++++++ couchpotato/static/scripts/page/about.js | 96 ++++ couchpotato/templates/_desktop.html | 2 + 3 files changed, 509 insertions(+) create mode 100644 couchpotato/static/scripts/library/templated.js create mode 100644 couchpotato/static/scripts/page/about.js diff --git a/couchpotato/static/scripts/library/templated.js b/couchpotato/static/scripts/library/templated.js new file mode 100644 index 00000000..6ba2343e --- /dev/null +++ b/couchpotato/static/scripts/library/templated.js @@ -0,0 +1,411 @@ +/* +--- +description: Templated is a MooTools mixin class which creates elements for classes using a string-based HTML template which may included embedded attach points and events. + +license: MIT-style + +authors: +- David Walsh + +requires: +- core/1.3: '*' + +provides: [Templated] + +... +*/ + +// Create scope limiter +(function(scope) { + + // Create some vars for strings + // This will save bytes when compressed + var strData = "data", + strWidget = "widget", + strAttach = "attach", + dataWidgetType = strData + "-" + strWidget + "-type", + dataWidgetProps = strData + "-" + strWidget + "-props", + dataWidgetAttachPoint = strData + "-" + strWidget + "-" + strAttach + "-point", + dataWidgetAttachEvent = strData + "-" + strWidget + "-" + strAttach + "-event", + dataWidgetized = strData + "-widgetized"; + + // Templated is a mixin for UI widgets + scope.Templated = new Class({ + + // The usual options object + options: { + // The default template + template: "
", + + // The URL to get the template from *instead* of the string + templateUrl: "", + + // A node reference for where this UI widget will be placed...in reference to + element: null, + + // Should this widget be parsed for sub-widgets? + widgetsInTemplate: true, + + // Property mappings (should be an object) + // These override defaults + propertyMappings: null, + + // Default property mappings + // Thse properties on the element will be moved to the respective nodes within the template + defaultPropertyMappings: null, + + // Should messages be debug to the console + debugMode: true + }, + + // Create placeholders for attached points and events + _attachPoints: [], + _attachEvents: [], + + // Parse + parse: function() { + + // Get shortcuts to the options and element + var options = this.options, + nodeRef = options.element = options.element || new Element("div").inject(document.body); + + // *IF* a templateUrl is specified, can't do anything until template is loaded + // Defer parsing until we've got it + if(options.templateUrl && Templated.templates && !Templated.templates[options.templateUrl]) { + this.debug("[Templated:parse] Need to load template from URL: " + options.templateUrl); + this.getTemplate(); + return false; + } + + // If already data-widgetized...gtfo + if(nodeRef.retrieve("widget")) { + this.debug("[Templated:parse] Node already widgetized, leaving ", nodeRef); + return nodeRef.domNode; + } + + // Mix noderef properties with options + options.defaultPropertyMappings = options.defaultPropertyMappings || { // THESE OVERRIDE CLASSES IN THE TEMPLATE!!!! + "id": "domNode", + "style": "domNode", + "class": "domNode" + }; + Object.merge(options, this.getNodeProps(nodeRef)); + + // postMixInProperties runs after options have been mixed with defaults but before + // any templating is done + this.postMixInProperties(); + + // Build rendering - creates the actual nodes, attachpoints, and attachevents + this.buildRendering(); + + // Fire the "postCreate" method, which runs after nodes are created *but* before the nodes are rendered to the page + this.postCreate(); + + // Cleanup creation + this.cleanupCreation(); + + // "Startup": The widget is in the DOM and the widget is ready to go + this.startup(); + + // Return the domNode + this.debug("[Templated:parse] At the end of parse, this is: ", this); + return this.domNode; + }, + + // Creates build rendering + buildRendering: function() { + // Get shortcuts to the options and element + var options = this.options, nodeRef = options.element; + + // Do string substitution on the template + var template = this.template = options.template.substitute(options || {}); + + // Create the DOM node within a DIV that's not rendered to the page + var bitchNode = this.bitchNode = new Element("div", { html: template.trim() }), + domNode = this.domNode = document.id(bitchNode.childNodes[0]); + + // Look for subwidgets if told to... + if(options.widgetsInTemplate) { + this.debug("[Templated:parse] Looking for subwidgets under domNode", domNode); + this.makeSubWidgets(this.domNode); + } + + // Create the attachpoints for me, then my kiddies + this.makeAttachPoints(domNode); + if(options.widgetsInTemplate) domNode.getElements("[" + dataWidgetAttachPoint + "]").each(this.makeAttachPoints, this); + this.debug("[Templated:parse] Creating attachpoints", this._attachPoints); + + // Create the attachevents for me, then my kiddies + this.makeAttachEvents(domNode); + if(options.widgetsInTemplate) domNode.getElements("[" + dataWidgetAttachEvent + "]").each(this.makeAttachEvents, this); + this.debug("Creating attachevents", this._attachEvents); + + // Map properties to nodes within the template + // Mix the custom mappings with the default + // This needs to happen after attachpoints + var mappings = options.propertyMappings ? Object.merge(options.defaultPropertyMappings, options.propertyMappings) : options.defaultPropertyMappings; + Object.each(mappings, function(value, key) { + // Ignore the value if not present in the object + if(!this[value]) return; + // Assign the value to the key + var currentProp = nodeRef.get(key); + if(currentProp != "") this[value].set(key, currentProp); + }.bind(this)); + + // If this widget has a "containerNode", grab it's childNodes *or* inject innerHTML + if(this.containerNode) { + var kids = nodeRef.childNodes; + kids.length ? $$(kids).inject(this.containerNode) : this.containerNode.set("html", nodeRef.get("html")); + } + }, + + // "postMixInProperties" -- Fired after options have been mixed in + postMixInProperties: function() { + this.debug("[Templated:postMixInProperties] postMixInProperties!"); + }, + + // "PostCreate" -- Fired after nodes are created, attachpoints and events are found + postCreate: function() { + this.debug("[Templated:postCreate] postCreate!"); + }, + + // "CleanupCreation" -- Removes the old element, destroys bitch node + cleanupCreation: function() { + // Get hold of the dom node and bitch nodes + var domNode = this.domNode, bitchNode = this.bitchNode, nodeRef = this.options.element; + + // Put the domNode where it should go and destroy the node reference + domNode.replaces(nodeRef); + nodeRef.destroy(); + + // Mark as data-widgetized and store the widget within data + domNode.set(dataWidgetized, true); + domNode.store("widget", this); + + // Remove the bitch node + bitchNode.destroy(); + }, + + // "StartUp" -- Fired when node is in place + startup: function(){ + this.debug("[Templated:startup] startup!"); + }, + + // Focus on focus node, if present + focus: function() { + var node = this.focusNode; + node && node.focus(); + }, + + // Create subwidgets from this + makeSubWidgets: function(domNode) { + if(!domNode) domNode = this.domNode; + domNode.getElements("["+ dataWidgetType +"]:not([" + dataWidgetized + "])").each(function(node){ + // Store the subwidget's attachpoints, attachevents, class type, and properties + var points = node.get(dataWidgetAttachPoint), + events = node.get(dataWidgetAttachEvent), + widgetProps = this.getNodeProps(node), + klass = node.get(dataWidgetType).trim(); + + // Create the widget + if(scope[klass]) { + var widget = new scope[klass](Object.merge(widgetProps, { element: node })); + this.debug("[parse:makeSubWidgets] Creating child widget: ", klass, widget); + // Get access to its dom node + widgetDomNode = widget.domNode; + // Add attachments back to the widget + points && widgetDomNode.set(dataWidgetAttachPoint, points.trim()); + events && widgetDomNode.set(dataWidgetAttachEvent, events.trim()); + } + }, this); + }, + + // Makes attachpoints + makeAttachPoints: function(node) { + var points = node.get(dataWidgetAttachPoint); + if(points) { + points.trim().split(",").each(function(attach) { + attach = attach.trim(); + this[attach] = node.retrieve("widget") || node; + this.debug("[Templated:makeAttachPoints] " + attach, node); + this._attachPoints.push({ node: node, name: attach }); + }, this); + node.set(dataWidgetAttachPoint, ""); + node.set("data-widgetized-attach-point", points); + } + }, + + // Makes attachevents + makeAttachEvents: function(node) { + // Temporarily store this widget's events so they may be added to this.domNode later + var events = node.get(dataWidgetAttachEvent); + // If there are events.... + if(events) { + // For every event found.... + events.trim().split(",").each(function(event) { + // Trim the event + event = event.trim(); + // Split the event:method pair + var eventFn = event.split(":"); + // Trim and rename each piece + var nativeEvent = eventFn[0].trim(), + classEvent = eventFn[1].trim(); + this.debug("[Templated:makeAttachEvents] " + nativeEvent + " / " + classEvent,node); + + // If the method isn't found on this, create a stub for it' + if(!this[classEvent]) { + this.debug("cant find ", classEvent, " in: ", this, " creating sub for it"); + this[classEvent] = function(){}; + } + + // Bind "this" to the event + var ev = this[classEvent].bind(this); + + // Add the event to the domNode + node.addEvent(nativeEvent, ev); + + // Store the event + this._attachEvents.push({ type: nativeEvent, event: ev, node: node }); + }, this); + + // Remove the event from its former place and add to -ized data item + var set = { + "data-widgetized-attach-event": events + }; + set[dataWidgetAttachEvent] = ""; + node.set(set); + //node.set("data-widget-attach-event",""); + //node.set("data-widgetized-attach-event",events); + } + }, + + // Destroy: removes node events + destroy: function() { + // Get reference to domNode + var domNode = this.domNode, events = this._attachEvents, points = this._attachPoints; + + // Clear out children + domNode.getElements("[" + dataWidgetized + "]").each(function(widget) { + widget.destroy(); + }); + + // Remove events + if(events.length) { + events.each(function(event) { + if(event.node) event.node.removeEvents(); + }); + } + // Remove node connections + if(points.length) { + points.each(function(point) { + if(point.name != "domNode") this[point.name] = null; + }, this); + } + // Destroy the dom node and its children, fin + domNode.store("widget", null).destroy(); + }, + + // Gets the in-node properties for a widget + getNodeProps: function(node) { + var props = node.get(dataWidgetProps), + widgetProps = {}; + // Create the widget + if(props) { + // Not using JSON.parse because it's too restricting, especially with quotes + var json = "{" + props.trim() + "}"; + if(JSON && JSON.decode) { // MooTools + widgetProps = JSON.decode(json); + } + else { // Native + eval("widgetProps = " + json); + } + } + return widgetProps; + }, + + debug: function(one, two, three, four) { + if(this.options.debugMode && console && console.log) { + console.log("[" + (this.domNode ? this.domNode.id : "") + "] ", one, two || "", three || "", four || ""); + } + } + }); + + // If Request is available.... + if(Request) { + Templated.templates = {}; + // Return the template + Templated.implement({ + // Method to return cached template or retrieve new one synchronously + getTemplate: function() { + /* + var url = this.options.templateUrl; + // Try to return cached first + if(Templated.templates[url]) { + return Templated.templates[url]; + } + else { + // Send a new request + return new Request({ + url: url, + async: false, // Used to ensure that necessary templates are there + onSuccess: function(template) { + this.options.template = template; + Templated.templates[url] = template; + this.parse(); + }.bind(this) + }).send(); + } + */ + + var url = this.options.templateUrl; + // Try to return cached first + if(!Templated.templates[url]) { + // Send a new request + return new Request({ + url: url, + async: false, // Used to ensure that necessary templates are there + onSuccess: function(template) { + this.options.template = template; + Templated.templates[url] = template; + this.parse(); + }.bind(this), + onFailure: function() { + Templated.templates[url] = Templated.prototype.options.template; + } + }).send(); + } + return Templated.templates[url]; + } + }); + } + + // Allow for parsing of an element and its children + Element.implement({ + parse: function() { + + var elFn = function(element) { + // Get the widget type + var klass = element.get(dataWidgetType); + // If the class exists.... + if(klass && scope[klass]) { + // Create the new class instance + new scope[klass]({ element: element }); + } + else { + window.console && console.log && console.log("klass does not exist! ", klass); + } + }; + + // Grab this and all nodes which are not already widgetized + elFn(this); + $$(this.getElements("["+ dataWidgetType +"]:not(" + dataWidgetized + ")")).each(elFn); + } + }); + + // Get widget from id + document.widget = function(idOrNode) { + return document.id(idOrNode).retrieve("widget") || null; + }; + + +})(this); // Scope limiter \ No newline at end of file diff --git a/couchpotato/static/scripts/page/about.js b/couchpotato/static/scripts/page/about.js new file mode 100644 index 00000000..246c9c9f --- /dev/null +++ b/couchpotato/static/scripts/page/about.js @@ -0,0 +1,96 @@ +var AboutSettingTab = new Class({ + + tab: '', + content: '', + + initialize: function(){ + var self = this; + + App.addEvent('load', self.addSettings.bind(self)) + + }, + + addSettings: function(){ + var self = this; + + self.settings = App.getPage('Settings') + self.settings.addEvent('create', function(){ + var tab = self.settings.createTab('about', { + 'label': 'About', + 'name': 'about' + }); + + self.tab = tab.tab; + self.content = tab.content; + + self.createAbout(); + + }) + + }, + + createAbout: function(){ + var self = this; + + var millennium = new Date(2008, 7, 16), + today = new Date(), + one_day = 1000*60*60*24; + + self.settings.createGroup({ + 'label': 'About CouchPotato' + }).inject(self.content).adopt( + new Element('div.shutdown').adopt( + new Element('a.button.red', { + 'text': 'Shutdown', + 'events': { + 'click': App.shutdown.bind(App) + } + }), + new Element('a.button.orange', { + 'text': 'Restart', + 'events': { + 'click': App.restart.bind(App) + } + }) + ), + new Element('div.usenet').adopt( + new Element('span', { + 'text': 'Help support CouchPotato and save some money for yourself by signing up for an account at' + }), + new Element('a', { + 'href': 'https://usenetserver.com/partners/?a_aid=couchpotato&a_bid=3f357c6f', + 'text': 'UsenetServer' + }), + new Element('a', { + 'href': 'http://www.newshosting.com/partners/?a_aid=couchpotato&a_bid=a0b022df', + 'text': 'Newshosting' + }), + new Element('span', { + 'text': 'For as low as $7.95 per month, you’ll get:' + }), + new Element('ul').adopt( + new Element('li[text=Unlimited downloads]'), + new Element('li[text=Uncapped speeds]'), + new Element('li[text=Free SSL Encrypted connections]'), + new Element('li', { + 'text': Math.ceil((today.getTime()-millennium.getTime())/(one_day))+" days retention" + }) + ) + ), + new Element('div.donate', { + 'html': '
+ + + +
+ I'm building CouchPotato in my spare time, so if you want to buy me a Pepsi while I'm coding, that would be awesome!' + }) + ); + + } + +}); + +window.addEvent('domready', function(){ + window.About = new AboutSettingTab(); +}); \ No newline at end of file diff --git a/couchpotato/templates/_desktop.html b/couchpotato/templates/_desktop.html index 46c19b4f..fa634b67 100644 --- a/couchpotato/templates/_desktop.html +++ b/couchpotato/templates/_desktop.html @@ -11,6 +11,7 @@ + @@ -33,6 +34,7 @@ +