From 2e44a1f845ec38f4d95ebd2833520f00900ae0d8 Mon Sep 17 00:00:00 2001 From: Matthew Harrison-Jones Date: Sun, 9 Jun 2013 10:56:10 +0100 Subject: [PATCH] Converted static Widgets to Backbone. Moved static HTML Widgets to Backbone. Might need to move Widget data else where to keep it clean. Will need to also implement API for future use. --- core/admin/assets/js/models/widget.js | 43 ++ core/admin/assets/js/router.js | 415 ++++++++++++++++++ core/admin/assets/js/views/dashboard.js | 190 ++++++-- core/admin/assets/sass/layouts/dashboard.scss | 4 + core/admin/assets/tmpl/content/widget.hbs | 44 ++ .../tmpl/content/widgets/custom/instagram.hbs | 1 + .../tmpl/content/widgets/custom/lastfm.hbs | 5 + .../content/widgets/custom/popular-posts.hbs | 15 + .../content/widgets/custom/post-ideas.hbs | 2 + .../tmpl/content/widgets/custom/time.hbs | 8 + .../tmpl/content/widgets/custom/tweets.hbs | 18 + .../content/widgets/custom/upcoming-posts.hbs | 9 + .../tmpl/content/widgets/default/blank.hbs | 0 .../tmpl/content/widgets/default/number.hbs | 4 + core/admin/views/dashboard.hbs | 325 -------------- core/admin/views/default.hbs | 4 + 16 files changed, 716 insertions(+), 371 deletions(-) create mode 100644 core/admin/assets/js/models/widget.js create mode 100644 core/admin/assets/tmpl/content/widget.hbs create mode 100644 core/admin/assets/tmpl/content/widgets/custom/instagram.hbs create mode 100644 core/admin/assets/tmpl/content/widgets/custom/lastfm.hbs create mode 100644 core/admin/assets/tmpl/content/widgets/custom/popular-posts.hbs create mode 100644 core/admin/assets/tmpl/content/widgets/custom/post-ideas.hbs create mode 100644 core/admin/assets/tmpl/content/widgets/custom/time.hbs create mode 100644 core/admin/assets/tmpl/content/widgets/custom/tweets.hbs create mode 100644 core/admin/assets/tmpl/content/widgets/custom/upcoming-posts.hbs create mode 100644 core/admin/assets/tmpl/content/widgets/default/blank.hbs create mode 100644 core/admin/assets/tmpl/content/widgets/default/number.hbs diff --git a/core/admin/assets/js/models/widget.js b/core/admin/assets/js/models/widget.js new file mode 100644 index 0000000000..82be49f241 --- /dev/null +++ b/core/admin/assets/js/models/widget.js @@ -0,0 +1,43 @@ +/*global window, document, Ghost, $, Backbone, _ */ +(function () { + "use strict"; + + Ghost.Models.Widget = Backbone.Model.extend({ + + defaults: { + title: "", + name: "", + author: "", + applicationID: "", + size: "", + content: { + template: '', + data: { + number: { + count: 0, + sub: { + value: 0, + dir: "", // "up" or "down" + item: "", + period: "" + } + } + } + }, + settings: { + settingsPane: false, + enabled: false, + options: [{ + title: "ERROR", + value: "Widget options not set" + }] + } + } + }); + + Ghost.Collections.Widgets = Backbone.Collection.extend({ + // url: Ghost.settings.apiRoot + '/widgets', // What will this be? + model: Ghost.Models.Widget + }); + +}()); \ No newline at end of file diff --git a/core/admin/assets/js/router.js b/core/admin/assets/js/router.js index c2c332d406..dd2f0b958a 100644 --- a/core/admin/assets/js/router.js +++ b/core/admin/assets/js/router.js @@ -6,11 +6,426 @@ Ghost.Router = Backbone.Router.extend({ routes: { + '': 'dashboard', 'content/': 'blog', 'editor': 'editor', 'editor/': 'editor', 'editor/:id': 'editor' }, + dashboard: function () { + var widgets = new Ghost.Collections.Widgets(); + + widgets.add({ + title: "LINZ, AUSTRIA", + name: "time", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.time", + content: { + template: 'custom/time', + data: { + day: "Today", + weather: "12", + time: "12:42PM", + date: "Monday / March 5 / 2013" + } + }, + settings: { + settingsPane: true, + options: [{ + title: "Timezone", + value: "GMT" + }] + } + }); + + widgets.add({ + title: "Ghost", + name: "image", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.image", + size: "2x1", + content: { + template: 'default/blank' + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Upcoming Posts", + name: "posts", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.posts", + content: { + template: 'custom/upcoming-posts', + data: { + ready: 9, + pending: 4, + draft: 1 + } + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Unique Visitors (7 days)", + name: "stats", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.stats", + size: "2x2", + content: { + template: 'default/number', + data: { + number: { + count: "293,051", + sub: { + value: "+14%", + dir: "up", + item: "", + period: "in the last 7 days" + } + } + } + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Facebook Likes", + name: "facebook", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.facebook", + content: { + template: 'default/number', + data: { + number: { + count: "12,329", + sub: { + value: "-3", + dir: "down", + item: "likes", + period: "today" + } + } + } + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Google Plus", + name: "gplus", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.gplus", + content: { + template: 'default/number', + data: { + number: { + count: "4,103", + sub: { + item: "have you in circles" + } + } + } + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Twitter", + name: "twitter", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.twitter", + content: { + template: 'default/blank' + }, + settings: { + settingsPane: true, + enabled: true, + options: [ + { + title: "Account", + value: "@JohnONolan" + }, + { + title: "Display", + value: "Last Tweets" + }, + { + title: "Quantity", + value: 6 + } + ] + } + }); + + widgets.add({ + title: "Campaign Monitor", + name: "campaignmonitor", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.campaignmonitor", + content: { + template: 'default/number', + data: { + number: { + count: "5,693", + sub: { + value: "+63", + dir: "up", + item: "subscribers", + period: "this week" + } + } + } + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Most Popular Posts", + name: "popular", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.popular", + size: "1x2", + content: { + template: 'custom/popular-posts', + data: { + posts: [ + { + title: "The Night of The Headless Horseman Part II", + time: "Yesterday", + count: "3,128" + }, + { + title: "Latin Script & Why it's Particularly Boring to Read", + time: "Wednesday", + count: "1,345" + }, + { + title: "59 Signs Your Cat and/or Dog Might be Planning To Kill You", + time: "Tuesday", + count: "824" + }, + { + title: "A Love Letter to Emma Stone", + time: "Today", + count: "293" + }, + { + title: "Lorem Ipsum Dolor Sit Amet & Other Funny Moments", + time: "Yesterday", + count: "124" + }, + { + title: "Matt Does Git", + time: "Thursday", + count: "100" + } + ] + } + } + }); + + widgets.add({ + title: "Posts This Week (Out Of 20)", + name: "postsWeek", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.postsWeek", + content: { + template: 'default/blank' + } + }); + + widgets.add({ + title: "Your RSS News Feed", + name: "rss", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.rss", + size: "2x2", + content: { + template: 'default/blank' + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Instagram", + name: "instagram", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.instagram", + content: { + template: 'custom/instagram', + data: { + image: "http://f.cl.ly/items/303f3y1n3I2L1F10343E/instagram.jpg" + } + }, + settings: { + settingsPane: true, + options: [{ + title: "Account", + value: "@JohnONolan" + }] + } + }); + + widgets.add({ + title: "Klout", + name: "klout", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.klout", + content: { + template: 'default/number', + data: { + number: { + count: "64.23", + sub: { + value: "-0.42", + dir: "down", + item: "", + period: "in the last 30 days" + } + } + } + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Bounce Rate", + name: "bounce", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.bounce", + content: { + template: 'default/number', + data: { + number: { + count: "40.21%", + sub: { + value: "-2.53%", + dir: "up", + item: "", + period: "in the last month" + } + } + } + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Average Time On Site", + name: "avgTime", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.avgTime", + content: { + template: 'default/number', + data: { + number: { + count: "2m 16s", + sub: { + value: "+31.4%", + dir: "up", + item: "", + period: "in the last month" + } + } + } + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Last.fm", + name: "lastfm", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.lastfm", + content: { + template: 'custom/lastfm', + data: { + cover: "http://f.cl.ly/items/0p0r3T3v3M0R0H1k1p0S/imagine_dragons.png", + artist: "Imagine Dragons", + title: "On Top of The World" + } + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Post Ideas", + name: "ideas", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.ideas", + size: "2x1", + content: { + template: 'custom/post-ideas' + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Twitter", + name: "tweets", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.tweets", + content: { + template: 'custom/tweets', + data: { + avatar: "http://f.cl.ly/items/1A1S0D3T3p1g1B2Z3J0u/ghost_twitter.jpeg", + name: "Ghost", + handle: "@TryGhost", + tweet: "If you're exploring the @twitterapi, be sure and bring the new field guide along. dev.twitter.com/blog/...", + time: "3 May 12" + } + }, + settings: { + settingsPane: true + } + }); + + widgets.add({ + title: "Backups", + name: "backups", + author: "Matthew Harrison-Jones", + applicationID: "com.ghost.backups", + content: { + template: 'default/blank' + }, + settings: { + settingsPane: true + } + }); + + + + + + //widgets.fetch().then(function () { + Ghost.currentView = new Ghost.Views.Dashboard({ el: '#main', collection: widgets }); + //}); + }, blog: function () { var posts = new Ghost.Collections.Posts(); diff --git a/core/admin/assets/js/views/dashboard.js b/core/admin/assets/js/views/dashboard.js index 4e2f58c8ba..568c329546 100644 --- a/core/admin/assets/js/views/dashboard.js +++ b/core/admin/assets/js/views/dashboard.js @@ -1,8 +1,13 @@ -/*global window, document, localStorage, Ghost, Backbone, $, _ */ +/*global window, document, localStorage, Ghost, Backbone, confirm, JST, $, _ */ (function () { "use strict"; - var $widgetContainer = $('.js-widget-container'), $itemElems, widgetPositions; + var Widgets, + Widget, + WidgetContent, + $widgetContainer, + $itemElems, + widgetPositions; widgetPositions = { mobile: {}, @@ -11,62 +16,155 @@ desktop: {} }; - $widgetContainer.packery({ - itemSelector: '.js-widget', - gutter: 10, - columnWidth: 340, - rowHeight: 300 + // Base view + // ---------- + Ghost.Views.Dashboard = Ghost.View.extend({ + initialize: function (options) { + this.addSubview(new Widgets({ el: '.js-widget-container', collection: this.collection })).render(); + } }); - $itemElems = $($widgetContainer.packery('getItemElements')); - // make item elements draggable - $itemElems.draggable(); - // bind Draggable events to Packery - $widgetContainer.packery('bindUIDraggableEvents', $itemElems); + // Widgets + // ---------- + Widgets = Ghost.View.extend({ + initialize: function () { + $widgetContainer = this.$el; + }, - // show item order after layout - function orderItems() { - // items are in order within the layout - var $itemElems = $($widgetContainer.packery('getItemElements')), order = {}; + packeryInit: function () { + var self = this; + $widgetContainer.packery({ + itemSelector: '.js-widget', + gutter: 10, + columnWidth: 340, + rowHeight: 300 + }); - $.each($itemElems, function (index, key) { - order[key.getAttribute("data-widget-id")] = index; - }); - return order; - } + $itemElems = $($widgetContainer.packery('getItemElements')); - // On resize button click - $(".js-widget-resizer").on("click", function () { - var $parent = $(this).closest('.js-widget'), data = $(this).data('size'); + // make item elements draggable + $itemElems.draggable(); + // bind Draggable events to Packery + $widgetContainer.packery('bindUIDraggableEvents', $itemElems); - $parent.removeClass("widget-1x2 widget-2x1 widget-2x2"); + $widgetContainer.packery('on', 'dragItemPositioned', function () { + var viewportSize = $(window).width(); + if (viewportSize <= 400) { // Mobile + widgetPositions.mobile = self.getWidgetOrder($itemElems); + } else if (viewportSize > 400 && viewportSize <= 800) { // Tablet + widgetPositions.tablet = self.getWidgetOrder($itemElems); + } else if (viewportSize > 800 && viewportSize <= 1000) { // Netbook + widgetPositions.netbook = self.getWidgetOrder($itemElems); + } else if (viewportSize > 1000) { + widgetPositions.desktop = self.getWidgetOrder($itemElems); + } + localStorage.setItem('widgetPositions', JSON.stringify(widgetPositions)); - if (data !== "1x1") { - $parent.addClass('widget-' + data); - $widgetContainer.packery('fit', $parent.get(0)); - } else { - $widgetContainer.packery(); + // Retrieve the object from storage with `JSON.parse(localStorage.getItem('widgetPositions'));` + }); + }, + + getWidgetOrder: function (itemElems) { + // items are in order within the layout + var order = {}; + + _.each(itemElems, function (widget, index) { + order[widget.getAttribute("data-widget-id")] = index; + }); + return order; + }, + + render: function () { + this.collection.each(function (model) { + this.$el.append(this.addSubview(new Widget({model: model})).render().el); + }, this); + this.packeryInit(); } - $(this).siblings('.active').removeClass('active'); - $(this).addClass('active'); - }); - $widgetContainer.packery('on', 'dragItemPositioned', function () { - var viewportSize = $(window).width(); - if (viewportSize <= 400) { // Mobile - widgetPositions.mobile = orderItems(); - } else if (viewportSize > 400 && viewportSize <= 800) { // Tablet - widgetPositions.tablet = orderItems(); - } else if (viewportSize > 800 && viewportSize <= 1000) { // Netbook - widgetPositions.netbook = orderItems(); - } else if (viewportSize > 1000) { - widgetPositions.desktop = orderItems(); - } - localStorage.setItem('widgetPositions', JSON.stringify(widgetPositions)); + // Widget + // ---------- + Widget = Ghost.View.extend({ + + tagName: 'article', + attributes: function () { + var size = (this.model.get('size')) + ? " widget-" + this.model.get('size') + : "", + settings = (this.model.attributes.settings.enabled) + ? " widget-settings" + : ""; + + return { + class: 'widget-' + this.model.get('name') + size + settings + ' js-widget', + 'data-widget-id': this.model.get('applicationID') + }; + }, + + events: { + 'click .js-widget-resizer': 'resizeWidget', + 'click .js-view-settings': 'showSettings', + 'click .js-view-widget': 'showWidget' + }, + + resizeWidget: function (e) { + e.preventDefault(); + var data = $(e.currentTarget).data('size'); + + this.$el.removeClass("widget-1x2 widget-2x1 widget-2x2"); + + if (data !== "1x1") { + this.$el.addClass('widget-' + data); + $widgetContainer.packery('fit', this.el); + } else { + $widgetContainer.packery(); + } + + $(e.currentTarget).siblings('.active').removeClass('active'); + $(e.currentTarget).addClass('active'); + }, + + showSettings: function (e) { + e.preventDefault(); + this.model.attributes.settings.enabled = true; + this.$el.addClass("widget-settings"); + this.render(); + }, + + showWidget: function (e) { + e.preventDefault(); + this.model.attributes.settings.enabled = false; + this.$el.removeClass("widget-settings"); + this.render(); + }, + + template: JST['content/widget'], + + render: function () { + this.$el.html(this.template(this.model.toJSON())); + if (!this.model.attributes.settings.enabled) { + this.$(".widget-content").html(this.addSubview(new WidgetContent({model: this.model})).render().el); + } + return this; + } + + }); + + // Widget Content + // ---------- + WidgetContent = Ghost.View.extend({ + + getTemplate: function () { + return JST['content/widgets/' + this.model.attributes.content.template]; + }, + + render: function () { + this.template = this.getTemplate(); + this.$el.html(this.template(this.model.toJSON())); + return this; + } - // Retrieve the object from storage with `JSON.parse(localStorage.getItem('widgetPositions'));` }); }()); \ No newline at end of file diff --git a/core/admin/assets/sass/layouts/dashboard.scss b/core/admin/assets/sass/layouts/dashboard.scss index 6dc58a260a..28fc2ba362 100644 --- a/core/admin/assets/sass/layouts/dashboard.scss +++ b/core/admin/assets/sass/layouts/dashboard.scss @@ -81,6 +81,10 @@ } } +.ui-draggable-dragging { + z-index: 9999; // Keep dragged Widget ontop +} + /* ========================================================================== Widget Sizes ========================================================================== */ diff --git a/core/admin/assets/tmpl/content/widget.hbs b/core/admin/assets/tmpl/content/widget.hbs new file mode 100644 index 0000000000..8d83aa06ed --- /dev/null +++ b/core/admin/assets/tmpl/content/widget.hbs @@ -0,0 +1,44 @@ +{{#if settings.enabled}} +
+ {{Title}} Settings +
+
+
+ {{#each settings.options}} + + {{/each}} +
+ +{{else}} +
+
+ +{{/if}} \ No newline at end of file diff --git a/core/admin/assets/tmpl/content/widgets/custom/instagram.hbs b/core/admin/assets/tmpl/content/widgets/custom/instagram.hbs new file mode 100644 index 0000000000..3b774f9a18 --- /dev/null +++ b/core/admin/assets/tmpl/content/widgets/custom/instagram.hbs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/core/admin/assets/tmpl/content/widgets/custom/lastfm.hbs b/core/admin/assets/tmpl/content/widgets/custom/lastfm.hbs new file mode 100644 index 0000000000..e6a7dce689 --- /dev/null +++ b/core/admin/assets/tmpl/content/widgets/custom/lastfm.hbs @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/core/admin/assets/tmpl/content/widgets/custom/popular-posts.hbs b/core/admin/assets/tmpl/content/widgets/custom/popular-posts.hbs new file mode 100644 index 0000000000..2c9e89b073 --- /dev/null +++ b/core/admin/assets/tmpl/content/widgets/custom/popular-posts.hbs @@ -0,0 +1,15 @@ + + \ No newline at end of file diff --git a/core/admin/assets/tmpl/content/widgets/custom/post-ideas.hbs b/core/admin/assets/tmpl/content/widgets/custom/post-ideas.hbs new file mode 100644 index 0000000000..cd7b032396 --- /dev/null +++ b/core/admin/assets/tmpl/content/widgets/custom/post-ideas.hbs @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/core/admin/assets/tmpl/content/widgets/custom/time.hbs b/core/admin/assets/tmpl/content/widgets/custom/time.hbs new file mode 100644 index 0000000000..32a6898b14 --- /dev/null +++ b/core/admin/assets/tmpl/content/widgets/custom/time.hbs @@ -0,0 +1,8 @@ +
+ {{content.data.day}} + {{content.data.weather}}° +
+ \ No newline at end of file diff --git a/core/admin/assets/tmpl/content/widgets/custom/tweets.hbs b/core/admin/assets/tmpl/content/widgets/custom/tweets.hbs new file mode 100644 index 0000000000..4c6757efb2 --- /dev/null +++ b/core/admin/assets/tmpl/content/widgets/custom/tweets.hbs @@ -0,0 +1,18 @@ +
+ + + +
+
+{{{content.data.tweet}}} +
+ \ No newline at end of file diff --git a/core/admin/assets/tmpl/content/widgets/custom/upcoming-posts.hbs b/core/admin/assets/tmpl/content/widgets/custom/upcoming-posts.hbs new file mode 100644 index 0000000000..9023da4b95 --- /dev/null +++ b/core/admin/assets/tmpl/content/widgets/custom/upcoming-posts.hbs @@ -0,0 +1,9 @@ +
+ +
+ +
\ No newline at end of file diff --git a/core/admin/assets/tmpl/content/widgets/default/blank.hbs b/core/admin/assets/tmpl/content/widgets/default/blank.hbs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/core/admin/assets/tmpl/content/widgets/default/number.hbs b/core/admin/assets/tmpl/content/widgets/default/number.hbs new file mode 100644 index 0000000000..4920e48c6b --- /dev/null +++ b/core/admin/assets/tmpl/content/widgets/default/number.hbs @@ -0,0 +1,4 @@ +
+ {{content.data.number.count}} + {{content.data.number.sub.value}} {{content.data.number.sub.item}} {{content.data.number.sub.period}} +
\ No newline at end of file diff --git a/core/admin/views/dashboard.hbs b/core/admin/views/dashboard.hbs index 8bd89ba5dc..361b1ac208 100644 --- a/core/admin/views/dashboard.hbs +++ b/core/admin/views/dashboard.hbs @@ -1,12 +1,7 @@ {{#contentFor 'bodyScripts'}} - - - {{/contentFor}} {{!< default}}
-
-
-
- Today - 12° -
- -
-
- Linz, Austria -
-
-
- -
-
- -
-
- Ghost -
-
-
- -
-
-
- -
-
    -
  • 9 Ready
  • -
  • 4 Pending
  • -
  • 1 Draft
  • -
-
-
-
- Upcoming Posts -
-
-
- -
-
-
- 293,051 - +14% in the last 7 days -
-
-
- Unique Visitors (7 days) -
-
-
- -
-
-
- 12,329 - -3 likes today - -
-
-
- Facebook Likes -
-
-
- -
-
-
- 4,103 - have you in circles -
-
-
- Google Plus -
-
-
- -
-
- Twitter Settings -
-
-
- - - -
-
-
-
- - - - -
-
- - - -
-
- - - -
-
- -
-
-
-
-
- -
-
-
- 5,693 - +63 subscribers this week -
-
-
- Campaign Monitor -
-
-
- - - -
-
- -
-
- Posts This Week (Out Of 20) -
-
-
- - -
-
- -
-
- Your RSS News Feed -
-
-
- -
-
- -
-
- Instagram -
-
-
- -
-
-
- 64.23 - -0.42 in the last 30 days -
-
-
- Klout -
-
-
- -
-
-
- 40.21% - -2.53% in the last month -
-
-
- Bounce Rate -
-
-
- -
-
-
- 2m 16s - +31.4% in the last month -
-
-
- Average Time On Site -
-
-
- -
-
- - -
-
- Last.fm -
-
-
- -
-
- - -
-
- Post Ideas -
-
-
- -
-
-
- - - -
- -
- - -
-
-
- Twitter -
-
-
- -
-
- -
-
- Backups -
-
-
\ No newline at end of file diff --git a/core/admin/views/default.hbs b/core/admin/views/default.hbs index dce72828b2..b12205f94d 100644 --- a/core/admin/views/default.hbs +++ b/core/admin/views/default.hbs @@ -45,6 +45,8 @@ + + @@ -55,7 +57,9 @@ + +