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}}
+