diff --git a/.gitignore b/.gitignore
index f7a845f0c9..a8e9a2a368 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,10 +42,6 @@ Session.vim
.tmp
-/core/clientold/tpl/hbs-tpl.js
-/core/clientold/assets/css
-/core/clientold/assets/fonts
-/core/clientold/assets/vendor
/core/client/assets/css
!/core/client/assets/css/ember-hacks.css
/core/client/assets/fonts
diff --git a/Gruntfile.js b/Gruntfile.js
index 1d2e84ffa1..725879b222 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -42,8 +42,6 @@ var path = require('path'),
var cfg = {
// #### Common paths used by tasks
paths: {
- // adminAssets: './core/client/', ?? who knows...
- adminOldAssets: './core/clientold/assets',
build: buildDirectory,
releaseBuild: path.join(buildDirectory, 'release'),
dist: distDirectory,
@@ -58,15 +56,11 @@ var path = require('path'),
// Watch files and livereload in the browser during development.
// See the [grunt dev](#live%20reload) task for how this is used.
watch: {
- handlebars: {
- files: ['core/clientold/tpl/**/*.hbs'],
- tasks: ['handlebars']
- },
shared: {
files: ['core/shared/**/*.js'],
- tasks: ['concat:dev', 'concat:dev-ember']
+ tasks: ['concat:dev']
},
- 'handlebars-ember': {
+ 'emberTemplates': {
files: ['core/client/**/*.hbs'],
tasks: ['emberTemplates:dev']
},
@@ -74,13 +68,6 @@ var path = require('path'),
files: ['core/client/**/*.js'],
tasks: ['clean:tmp', 'transpile', 'concat_sourcemap']
},
- concat: {
- files: [
- 'core/clientold/*.js',
- 'core/clientold/**/*.js'
- ],
- tasks: ['concat']
- },
'ghost-ui': {
files: [
'bower_components/ghost-ui/dist/css/*.css'
@@ -225,7 +212,6 @@ var path = require('path'),
}
},
-
// ### grunt-shell
// Command line tools where it's easier to run a command directly than configure a grunt plugin
shell: {
@@ -253,23 +239,6 @@ var path = require('path'),
}
},
- // ### grunt-contrib-handlebars
- // Compile handlebars templates into a JST file for the admin client (old)
- handlebars: {
- core: {
- options: {
- namespace: 'JST',
- processName: function (filename) {
- filename = filename.replace('core/clientold/tpl/', '');
- return filename.replace('.hbs', '');
- }
- },
- files: {
- 'core/clientold/tpl/hbs-tpl.js': 'core/clientold/tpl/**/*.hbs'
- }
- }
- },
-
// ### grunt-ember-templates
// Compiles handlebar templates for ember
emberTemplates: {
@@ -278,11 +247,13 @@ var path = require('path'),
templateBasePath: /core\/client\//,
templateFileExtensions: /\.hbs/,
templateRegistration: function (name, template) {
- return grunt.config.process("define('ghost/") + name + "', ['exports'], function(__exports__){ __exports__['default'] = " + template + "; });";
+ return grunt.config.process('define(\'ghost/') +
+ name + '\', [\'exports\'], function(__exports__){ __exports__[\'default\'] = ' +
+ template + '; });';
}
},
files: {
- "core/built/scripts/templates-ember.js": "core/client/templates/**/*.hbs"
+ 'core/built/scripts/templates-ember.js': 'core/client/templates/**/*.hbs'
}
}
},
@@ -362,11 +333,6 @@ var path = require('path'),
src: ['**'],
dest: 'core/client/assets/',
expand: true
- }, {
- cwd: 'bower_components/ghost-ui/dist/',
- src: ['**'],
- dest: 'core/clientold/assets/',
- expand: true
}]
},
prod: {
@@ -380,11 +346,6 @@ var path = require('path'),
src: ['**'],
dest: 'core/client/assets/',
expand: true
- }, {
- cwd: 'bower_components/ghost-ui/dist/',
- src: ['**'],
- dest: 'core/clientold/assets/',
- expand: true
}]
},
release: {
@@ -398,11 +359,6 @@ var path = require('path'),
src: ['**'],
dest: 'core/client/assets/',
expand: true
- }, {
- cwd: 'bower_components/ghost-ui/dist/',
- src: ['**'],
- dest: 'core/clientold/assets/',
- expand: true
}, {
expand: true,
src: buildGlob,
@@ -427,69 +383,7 @@ var path = require('path'),
// ### grunt-contrib-concat
// concatenate multiple JS files into a single file ready for use
concat: {
- dev: {
- files: {
- 'core/built/scripts/vendor.js': [
- 'bower_components/jquery/dist/jquery.js',
- 'bower_components/jquery-ui/ui/jquery-ui.js',
- 'core/clientold/assets/lib/jquery-utils.js',
- 'core/clientold/assets/lib/uploader.js',
-
- 'bower_components/lodash/dist/lodash.underscore.js',
- 'bower_components/backbone/backbone.js',
- 'bower_components/handlebars/handlebars.runtime.js',
- 'bower_components/moment/moment.js',
- 'bower_components/jquery-file-upload/js/jquery.fileupload.js',
- 'bower_components/codemirror/lib/codemirror.js',
- 'bower_components/codemirror/addon/mode/overlay.js',
- 'bower_components/codemirror/mode/markdown/markdown.js',
- 'bower_components/codemirror/mode/gfm/gfm.js',
- 'bower_components/showdown/src/showdown.js',
- 'bower_components/validator-js/validator.js',
-
- 'core/shared/lib/showdown/extensions/ghostimagepreview.js',
- 'core/shared/lib/showdown/extensions/ghostgfm.js',
-
- // TODO: Remove or replace
- 'core/clientold/assets/vendor/shortcuts.js',
- 'core/clientold/assets/vendor/to-title-case.js',
-
- 'bower_components/Countable/Countable.js',
- 'bower_components/fastclick/lib/fastclick.js',
- 'bower_components/nprogress/nprogress.js'
- ],
-
- 'core/built/scripts/helpers.js': [
- 'core/clientold/init.js',
-
- 'core/clientold/mobile-interactions.js',
- 'core/clientold/toggle.js',
- 'core/clientold/markdown-actions.js',
- 'core/clientold/helpers/index.js',
- 'core/clientold/assets/lib/editor/index.js',
- 'core/clientold/assets/lib/editor/markerManager.js',
- 'core/clientold/assets/lib/editor/uploadManager.js',
- 'core/clientold/assets/lib/editor/markdownEditor.js',
- 'core/clientold/assets/lib/editor/htmlPreview.js',
- 'core/clientold/assets/lib/editor/scrollHandler.js',
- 'core/clientold/assets/lib/editor/mobileCodeMirror.js'
- ],
-
- 'core/built/scripts/templates.js': [
- 'core/clientold/tpl/hbs-tpl.js'
- ],
-
- 'core/built/scripts/models.js': [
- 'core/clientold/models/**/*.js'
- ],
-
- 'core/built/scripts/views.js': [
- 'core/clientold/views/**/*.js',
- 'core/clientold/router.js'
- ]
- }
- },
- 'dev-ember': {
+ 'dev': {
files: {
'core/built/scripts/vendor-ember.js': [
'bower_components/loader.js/loader.js',
@@ -520,62 +414,6 @@ var path = require('path'),
'core/shared/lib/showdown/extensions/ghostgfm.js',
]
}
- },
- prod: {
- files: {
- 'core/built/scripts/ghost.js': [
- 'bower_components/jquery/dist/jquery.js',
- 'bower_components/jquery-ui/ui/jquery-ui.js',
- 'core/clientold/assets/lib/jquery-utils.js',
- 'core/clientold/assets/lib/uploader.js',
-
- 'bower_components/lodash/dist/lodash.underscore.js',
- 'bower_components/backbone/backbone.js',
- 'bower_components/handlebars/handlebars.runtime.js',
- 'bower_components/moment/moment.js',
- 'bower_components/jquery-file-upload/js/jquery.fileupload.js',
- 'bower_components/codemirror/lib/codemirror.js',
- 'bower_components/codemirror/addon/mode/overlay.js',
- 'bower_components/codemirror/mode/markdown/markdown.js',
- 'bower_components/codemirror/mode/gfm/gfm.js',
- 'bower_components/showdown/src/showdown.js',
- 'bower_components/validator-js/validator.js',
-
- 'core/shared/lib/showdown/extensions/ghostimagepreview.js',
- 'core/shared/lib/showdown/extensions/ghostgfm.js',
-
- // TODO: Remove or replace
- 'core/clientold/assets/vendor/shortcuts.js',
- 'core/clientold/assets/vendor/to-title-case.js',
-
- 'bower_components/Countable/Countable.js',
- 'bower_components/fastclick/lib/fastclick.js',
- 'bower_components/nprogress/nprogress.js',
-
- 'core/clientold/init.js',
-
- 'core/clientold/mobile-interactions.js',
- 'core/clientold/toggle.js',
- 'core/clientold/markdown-actions.js',
- 'core/clientold/helpers/index.js',
-
- 'core/clientold/assets/lib/editor/index.js',
- 'core/clientold/assets/lib/editor/markerManager.js',
- 'core/clientold/assets/lib/editor/uploadManager.js',
- 'core/clientold/assets/lib/editor/markdownEditor.js',
- 'core/clientold/assets/lib/editor/htmlPreview.js',
- 'core/clientold/assets/lib/editor/scrollHandler.js',
- 'core/clientold/assets/lib/editor/mobileCodeMirror.js',
-
- 'core/clientold/tpl/hbs-tpl.js',
-
- 'core/clientold/models/**/*.js',
-
- 'core/clientold/views/**/*.js',
-
- 'core/clientold/router.js'
- ]
- }
}
},
@@ -584,7 +422,6 @@ var path = require('path'),
uglify: {
prod: {
files: {
- 'core/built/scripts/ghost.min.js': 'core/built/scripts/ghost.js',
'core/built/public/jquery.min.js': 'core/built/public/jquery.js'
}
}
@@ -592,10 +429,10 @@ var path = require('path'),
// ### grunt-update-submodules
// Grunt task to update git submodules
- "update_submodules": {
+ 'update_submodules': {
default: {
options: {
- params: "--init"
+ params: '--init'
}
}
}
@@ -611,7 +448,7 @@ var path = require('path'),
// This really ought to be refactored into a separate grunt task module
grunt.registerTask('spawnCasperJS', function (target) {
- target = _.contains(['client', 'clientold', 'frontend'], target) ? target + '/' : undefined;
+ target = _.contains(['client', 'frontend'], target) ? target + '/' : undefined;
var done = this.async(),
options = ['host', 'noPort', 'port', 'email', 'password'],
@@ -808,7 +645,7 @@ var path = require('path'),
// The purpose of the functional tests is to ensure that Ghost is working as is expected from a user perspective
// including buttons and other important interactions in the admin UI.
grunt.registerTask('test-functional', 'Run functional interface tests (CasperJS)',
- ['clean:test', 'emberBuild', 'setTestEnv', 'loadConfig', 'copy:dev', 'express:test', 'spawnCasperJS', 'express:test:stop']
+ ['clean:test', 'setTestEnv', 'loadConfig', 'express:test', 'spawnCasperJS', 'express:test:stop']
);
// ### Coverage
@@ -881,15 +718,15 @@ var path = require('path'),
//
// It is otherwise the same as running `grunt`, but is only used when running Ghost in the `production` env.
grunt.registerTask('prod', 'Build JS & templates for production',
- ['handlebars', 'concat', 'uglify', 'copy:prod', 'master-warn']);
+ ['concat', 'uglify', 'copy:prod', 'master-warn']);
// ### Default asset build
// `grunt` - default grunt task
//
- // Compiles handlebars templates, concatenates javascript files for the admin UI into a handful of files instead
+ // Compiles concatenates javascript files for the admin UI into a handful of files instead
// of many files, and makes sure the bower dependencies are in the right place.
grunt.registerTask('default', 'Build JS & templates for development',
- ['handlebars', 'concat', 'copy:dev', 'emberBuild']);
+ ['concat', 'copy:dev', 'emberBuild']);
// ### Live reload
// `grunt dev` - build assets on the fly whilst developing
@@ -903,7 +740,7 @@ var path = require('path'),
//
// Note that the current implementation of watch only works with casper, not other themes.
grunt.registerTask('dev', 'Dev Mode; watch files and restart server on changes',
- ['handlebars', 'concat', 'copy:dev', 'emberBuild', 'express:dev', 'watch']);
+ ['concat', 'copy:dev', 'emberBuild', 'express:dev', 'watch']);
// ### Release
// Run `grunt release` to create a Ghost release zip file.
@@ -912,11 +749,11 @@ var path = require('path'),
// either environment, and packages all the files up into a zip.
grunt.registerTask('release',
'Release task - creates a final built zip\n' +
- ' - Do our standard build steps (handlebars, etc)\n' +
+ ' - Do our standard build steps \n' +
' - Copy files to release-folder/#/#{version} directory\n' +
' - Clean out unnecessary files (travis, .git*, etc)\n' +
' - Zip files in release-folder to dist-folder/#{version} directory',
- ['shell:bower', 'update_submodules', 'handlebars', 'concat', 'uglify', 'clean:release', 'copy:release', 'compress:release']);
+ ['shell:bower', 'update_submodules', 'concat', 'uglify', 'clean:release', 'copy:release', 'compress:release']);
};
// Export the configuration
diff --git a/bower.json b/bower.json
index 3ee05013ff..288b0c73f0 100644
--- a/bower.json
+++ b/bower.json
@@ -1,13 +1,13 @@
{
"name": "ghost",
"dependencies": {
- "backbone": "1.0.0",
"codemirror": "4.0.1",
"Countable": "2.0.2",
"ember": "1.5.0",
"ember-data": "~1.0.0-beta.8",
"ember-load-initializers": "git://github.com/stefanpenner/ember-load-initializers.git#0.0.1",
"ember-resolver": "git://github.com/stefanpenner/ember-jj-abrams-resolver.git#181251821cf513bb58d3e192faa13245a816f75e",
+ "ember-simple-auth": "https://github.com/simplabs/ember-simple-auth-component.git#0.5.3",
"fastclick": "1.0.0",
"ghost-ui": "0.8.1",
"handlebars": "1.3.0",
@@ -17,13 +17,12 @@
"jquery-hammerjs": "1.0.1",
"jquery-ui": "1.10.4",
"keymaster": "madrobby/keymaster#0f09fc1b7e66c2b7e07afe89a419366dcf2d1cd8",
+ "loader.js": "stefanpenner/loader.js#1.0.0",
"lodash": "2.4.1",
"moment": "2.4.0",
"nprogress": "0.1.2",
"showdown": "https://github.com/ErisDS/showdown.git#v0.3.2-ghost",
- "validator-js": "3.4.0",
- "loader.js": "stefanpenner/loader.js#1.0.0",
- "ember-simple-auth": "https://github.com/simplabs/ember-simple-auth-component.git#0.5.3"
+ "validator-js": "3.4.0"
},
"resolutions": {
"ember": "~1.4.0"
diff --git a/core/client/README.md b/core/client/README.md
deleted file mode 100644
index 2a1f06bbb6..0000000000
--- a/core/client/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-## What's this?
-
-This is the shiny new Ghost admin UI built in Ember.js. It gets served if you visit the URL `/ghost/ember/`.
-
-We're currently in the process of building this awesome new UI to replace the old one which was written in backbone,
-lives in the `/clientold/` folder and is still served when you visit the URL `/ghost/`.
-
-In short, we currently have 2 admins:
-
-* Old, Backbone Admin UI lives in `/clientold/` and is served from `/ghost/`
-* New, Ember Admin UI lives in `/client/` and is served from `/ghost/ember/`
-
-For more information, please read the [Ember admin wiki page](https://github.com/TryGhost/Ghost/wiki/Ember-Admin-UI)
\ No newline at end of file
diff --git a/core/client/router.js b/core/client/router.js
index 3a25c93bb6..ca2225dce0 100644
--- a/core/client/router.js
+++ b/core/client/router.js
@@ -6,7 +6,7 @@ var Router = Ember.Router.extend();
Router.reopen({
location: 'trailing-history', // use HTML5 History API instead of hash-tag based URLs
- rootURL: ghostPaths().subdir + '/ghost/ember/', // admin interface lives under sub-directory /ghost
+ rootURL: ghostPaths().subdir + '/ghost/', // admin interface lives under sub-directory /ghost
clearNotifications: function () {
// @TODO This should call closePassive() to only close passive notifications
diff --git a/core/clientold/README.md b/core/clientold/README.md
deleted file mode 100644
index cc84dae7ac..0000000000
--- a/core/clientold/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-## What's this?
-
-This is the old Ghost admin UI built in backbone.js. It gets served if you visit the URL `/ghost/`.
-
-We're currently in the process of replacing this UI with a new one written in Ember which lives in the `/client/`
-folder, and is served when you visit the URL `/ghost/ember/`.
-
-In short, we currently have 2 admins:
-
-* Old, Backbone Admin UI lives in `/clientold/` and is served from `/ghost/`
-* New, Ember Admin UI lives in `/client/` and is served from `/ghost/ember/`
-
-For more information, please read the [Ember admin wiki page](https://github.com/TryGhost/Ghost/wiki/Ember-Admin-UI)
\ No newline at end of file
diff --git a/core/clientold/assets/img/404-ghost.png b/core/clientold/assets/img/404-ghost.png
deleted file mode 100644
index 3475674dfd..0000000000
Binary files a/core/clientold/assets/img/404-ghost.png and /dev/null differ
diff --git a/core/clientold/assets/img/404-ghost@2x.png b/core/clientold/assets/img/404-ghost@2x.png
deleted file mode 100644
index 9260c48514..0000000000
Binary files a/core/clientold/assets/img/404-ghost@2x.png and /dev/null differ
diff --git a/core/clientold/assets/img/large.png b/core/clientold/assets/img/large.png
deleted file mode 100644
index 57a0db9e50..0000000000
Binary files a/core/clientold/assets/img/large.png and /dev/null differ
diff --git a/core/clientold/assets/img/loadingcat.gif b/core/clientold/assets/img/loadingcat.gif
deleted file mode 100644
index 014d484414..0000000000
Binary files a/core/clientold/assets/img/loadingcat.gif and /dev/null differ
diff --git a/core/clientold/assets/img/medium.png b/core/clientold/assets/img/medium.png
deleted file mode 100644
index 5ab6efc2c2..0000000000
Binary files a/core/clientold/assets/img/medium.png and /dev/null differ
diff --git a/core/clientold/assets/img/small.png b/core/clientold/assets/img/small.png
deleted file mode 100644
index 962c9cd48f..0000000000
Binary files a/core/clientold/assets/img/small.png and /dev/null differ
diff --git a/core/clientold/assets/img/touch-icon-ipad.png b/core/clientold/assets/img/touch-icon-ipad.png
deleted file mode 100644
index 0b8bdf6a85..0000000000
Binary files a/core/clientold/assets/img/touch-icon-ipad.png and /dev/null differ
diff --git a/core/clientold/assets/img/touch-icon-iphone.png b/core/clientold/assets/img/touch-icon-iphone.png
deleted file mode 100644
index bafed226a5..0000000000
Binary files a/core/clientold/assets/img/touch-icon-iphone.png and /dev/null differ
diff --git a/core/clientold/assets/lib/editor/htmlPreview.js b/core/clientold/assets/lib/editor/htmlPreview.js
deleted file mode 100644
index 52f6d7239d..0000000000
--- a/core/clientold/assets/lib/editor/htmlPreview.js
+++ /dev/null
@@ -1,44 +0,0 @@
-// # Ghost Editor HTML Preview
-//
-// HTML Preview is the right pane in the split view editor.
-// It is effectively just a scrolling container for the HTML output from showdown
-// It knows how to update itself, and that's pretty much it.
-
-/*global Ghost, Showdown, Countable, _, $ */
-(function () {
- 'use strict';
-
- var HTMLPreview = function (markdown, uploadMgr) {
- var converter = new Showdown.converter({extensions: ['ghostimagepreview', 'ghostgfm']}),
- preview = document.getElementsByClassName('rendered-markdown')[0],
- update;
-
- // Update the preview
- // Includes replacing all the HTML, intialising upload dropzones, and updating the counter
- update = function () {
- preview.innerHTML = converter.makeHtml(markdown.value());
-
- uploadMgr.enable();
-
- Countable.once(preview, function (counter) {
- $('.entry-word-count').text($.pluralize(counter.words, 'word'));
- $('.entry-character-count').text($.pluralize(counter.characters, 'character'));
- $('.entry-paragraph-count').text($.pluralize(counter.paragraphs, 'paragraph'));
- });
- };
-
- // Public API
- _.extend(this, {
- scrollViewPort: function () {
- return $('.entry-preview-content');
- },
- scrollContent: function () {
- return $('.rendered-markdown');
- },
- update: update
- });
- };
-
- Ghost.Editor = Ghost.Editor || {};
- Ghost.Editor.HTMLPreview = HTMLPreview;
-} ());
\ No newline at end of file
diff --git a/core/clientold/assets/lib/editor/index.js b/core/clientold/assets/lib/editor/index.js
deleted file mode 100644
index cdeac320df..0000000000
--- a/core/clientold/assets/lib/editor/index.js
+++ /dev/null
@@ -1,79 +0,0 @@
-// # Ghost Editor
-//
-// Ghost Editor contains a set of modules which make up the editor component
-// It manages the left and right panes, and all of the communication between them
-// Including scrolling,
-
-/*global document, $, _, Ghost */
-(function () {
- 'use strict';
-
- var Editor = function () {
- var self = this,
- $document = $(document),
- // Create all the needed editor components, passing them what they need to function
- markdown = new Ghost.Editor.MarkdownEditor(),
- uploadMgr = new Ghost.Editor.UploadManager(markdown),
- preview = new Ghost.Editor.HTMLPreview(markdown, uploadMgr),
- scrollHandler = new Ghost.Editor.ScrollHandler(markdown, preview),
- unloadDirtyMessage,
- handleChange,
- handleDrag;
-
- unloadDirtyMessage = function () {
- return '==============================\n\n' +
- 'Hey there! It looks like you\'re in the middle of writing' +
- ' something and you haven\'t saved all of your content.' +
- '\n\nSave before you go!\n\n' +
- '==============================';
- };
-
- handleChange = function () {
- self.setDirty(true);
- preview.update();
- };
-
- handleDrag = function (e) {
- e.preventDefault();
- };
-
- // Public API
- _.extend(this, {
- enable: function () {
- // Listen for changes
- $document.on('markdownEditorChange', handleChange);
-
- // enable editing and scrolling
- markdown.enable();
- scrollHandler.enable();
- },
-
- disable: function () {
- // Don't listen for changes
- $document.off('markdownEditorChange', handleChange);
-
- // disable editing and scrolling
- markdown.disable();
- scrollHandler.disable();
- },
-
- // Get the markdown value from the editor for saving
- // Upload manager makes sure the upload markers are removed beforehand
- value: function () {
- return uploadMgr.value();
- },
-
- setDirty: function (dirty) {
- window.onbeforeunload = dirty ? unloadDirtyMessage : null;
- }
- });
-
- // Initialise
- $document.on('drop dragover', handleDrag);
- preview.update();
- this.enable();
- };
-
- Ghost.Editor = Ghost.Editor || {};
- Ghost.Editor.Main = Editor;
-}());
\ No newline at end of file
diff --git a/core/clientold/assets/lib/editor/markdownEditor.js b/core/clientold/assets/lib/editor/markdownEditor.js
deleted file mode 100644
index 050a69de29..0000000000
--- a/core/clientold/assets/lib/editor/markdownEditor.js
+++ /dev/null
@@ -1,100 +0,0 @@
-// # Ghost Editor Markdown Editor
-//
-// Markdown Editor is a light wrapper around CodeMirror
-
-/*global Ghost, CodeMirror, shortcut, _, $ */
-(function () {
- 'use strict';
-
- var MarkdownShortcuts,
- MarkdownEditor;
-
- MarkdownShortcuts = [
- {'key': 'Ctrl+Alt+U', 'style': 'strike'},
- {'key': 'Ctrl+Shift+K', 'style': 'code'},
- {'key': 'Meta+K', 'style': 'code'},
- {'key': 'Ctrl+Alt+1', 'style': 'h1'},
- {'key': 'Ctrl+Alt+2', 'style': 'h2'},
- {'key': 'Ctrl+Alt+3', 'style': 'h3'},
- {'key': 'Ctrl+Alt+4', 'style': 'h4'},
- {'key': 'Ctrl+Alt+5', 'style': 'h5'},
- {'key': 'Ctrl+Alt+6', 'style': 'h6'},
- {'key': 'Ctrl+Shift+L', 'style': 'link'},
- {'key': 'Ctrl+Shift+I', 'style': 'image'},
- {'key': 'Ctrl+Q', 'style': 'blockquote'},
- {'key': 'Ctrl+Shift+1', 'style': 'currentDate'},
- {'key': 'Ctrl+U', 'style': 'uppercase'},
- {'key': 'Ctrl+Shift+U', 'style': 'lowercase'},
- {'key': 'Ctrl+Alt+Shift+U', 'style': 'titlecase'},
- {'key': 'Ctrl+Alt+W', 'style': 'selectword'},
- {'key': 'Ctrl+L', 'style': 'list'}
- ];
-
- if (navigator.userAgent.indexOf('Mac') !== -1) {
- MarkdownShortcuts.push({'key': 'Meta+B', 'style': 'bold'});
- MarkdownShortcuts.push({'key': 'Meta+I', 'style': 'italic'});
- MarkdownShortcuts.push({'key': 'Meta+Alt+C', 'style': 'copyHTML'});
- MarkdownShortcuts.push({'key': 'Meta+Enter', 'style': 'newLine'});
- } else {
- MarkdownShortcuts.push({'key': 'Ctrl+B', 'style': 'bold'});
- MarkdownShortcuts.push({'key': 'Ctrl+I', 'style': 'italic'});
- MarkdownShortcuts.push({'key': 'Ctrl+Alt+C', 'style': 'copyHTML'});
- MarkdownShortcuts.push({'key': 'Ctrl+Enter', 'style': 'newLine'});
-
- }
-
- MarkdownEditor = function () {
- var codemirror = CodeMirror.fromTextArea(document.getElementById('entry-markdown'), {
- mode: 'gfm',
- tabMode: 'indent',
- tabindex: '2',
- cursorScrollMargin: 10,
- lineWrapping: true,
- dragDrop: false,
- extraKeys: {
- Home: 'goLineLeft',
- End: 'goLineRight'
- }
- });
-
- // Markdown shortcuts for the editor
- _.each(MarkdownShortcuts, function (combo) {
- shortcut.add(combo.key, function () {
- return codemirror.addMarkdown({style: combo.style});
- });
- });
-
- // Public API
- _.extend(this, {
- codemirror: codemirror,
-
- scrollViewPort: function () {
- return $('.CodeMirror-scroll');
- },
- scrollContent: function () {
- return $('.CodeMirror-sizer');
- },
- enable: function () {
- codemirror.setOption('readOnly', false);
- codemirror.on('change', function () {
- $(document).trigger('markdownEditorChange');
- });
- },
- disable: function () {
- codemirror.setOption('readOnly', 'nocursor');
- codemirror.off('change', function () {
- $(document).trigger('markdownEditorChange');
- });
- },
- isCursorAtEnd: function () {
- return codemirror.getCursor('end').line > codemirror.lineCount() - 5;
- },
- value: function () {
- return codemirror.getValue();
- }
- });
- };
-
- Ghost.Editor = Ghost.Editor || {};
- Ghost.Editor.MarkdownEditor = MarkdownEditor;
-} ());
diff --git a/core/clientold/assets/lib/editor/markerManager.js b/core/clientold/assets/lib/editor/markerManager.js
deleted file mode 100644
index 1b3489d061..0000000000
--- a/core/clientold/assets/lib/editor/markerManager.js
+++ /dev/null
@@ -1,154 +0,0 @@
-// # Ghost Editor Marker Manager
-//
-// MarkerManager looks after the array of markers which are attached to image markdown in the editor.
-//
-// Marker Manager is told by the Upload Manager to add a marker to a line.
-// A marker takes the form of a 'magic id' which looks like:
-// {<1>}
-// It is appended to the start of the given line, and then defined as a CodeMirror 'TextMarker' widget which is
-// subsequently added to an array of markers to keep track of all markers in the editor.
-// The TextMarker is also set to 'collapsed' mode which means it does not show up in the display.
-// Currently, the markers can be seen if you copy and paste your content out of Ghost into a text editor.
-// The markers are stripped on save so should not appear in the DB
-
-
-/*global _, Ghost */
-
-(function () {
- 'use strict';
-
- var imageMarkdownRegex = /^(?:\{<(.*?)>\})?!(?:\[([^\n\]]*)\])(?:\(([^\n\]]*)\))?$/gim,
- markerRegex = /\{<([\w\W]*?)>\}/,
- MarkerManager;
-
- MarkerManager = function (editor) {
- var markers = {},
- uploadPrefix = 'image_upload',
- uploadId = 1,
- addMarker,
- removeMarker,
- markerRegexForId,
- stripMarkerFromLine,
- findAndStripMarker,
- checkMarkers,
- initMarkers;
-
- // the regex
- markerRegexForId = function (id) {
- id = id.replace('image_upload_', '');
- return new RegExp('\\{<' + id + '>\\}', 'gmi');
- };
-
- // Add a marker to the given line
- // Params:
- // line - CodeMirror LineHandle
- // ln - line number
- addMarker = function (line, ln) {
- var marker,
- magicId = '{<' + uploadId + '>}',
- newText = magicId + line.text;
-
- editor.replaceRange(
- newText,
- {line: ln, ch: 0},
- {line: ln, ch: newText.length}
- );
-
- marker = editor.markText(
- {line: ln, ch: 0},
- {line: ln, ch: (magicId.length)},
- {collapsed: true}
- );
-
- markers[uploadPrefix + '_' + uploadId] = marker;
- uploadId += 1;
- };
-
- // Remove a marker
- // Will be passed a LineHandle if we already know which line the marker is on
- removeMarker = function (id, marker, line) {
- delete markers[id];
- marker.clear();
-
- if (line) {
- stripMarkerFromLine(line);
- } else {
- findAndStripMarker(id);
- }
- };
-
- // Removes the marker on the given line if there is one
- stripMarkerFromLine = function (line) {
- var markerText = line.text.match(markerRegex),
- ln = editor.getLineNumber(line);
-
- if (markerText) {
- editor.replaceRange(
- '',
- {line: ln, ch: markerText.index},
- {line: ln, ch: markerText.index + markerText[0].length}
- );
- }
- };
-
- // Find a marker in the editor by id & remove it
- // Goes line by line to find the marker by it's text if we've lost track of the TextMarker
- findAndStripMarker = function (id) {
- editor.eachLine(function (line) {
- var markerText = markerRegexForId(id).exec(line.text),
- ln;
-
- if (markerText) {
- ln = editor.getLineNumber(line);
- editor.replaceRange(
- '',
- {line: ln, ch: markerText.index},
- {line: ln, ch: markerText.index + markerText[0].length}
- );
- }
- });
- };
-
- // Check each marker to see if it is still present in the editor and if it still corresponds to image markdown
- // If it is no longer a valid image, remove it
- checkMarkers = function () {
- _.each(markers, function (marker, id) {
- var line;
- marker = markers[id];
- if (marker.find()) {
- line = editor.getLineHandle(marker.find().from.line);
- if (!line.text.match(imageMarkdownRegex)) {
- removeMarker(id, marker, line);
- }
- } else {
- removeMarker(id, marker);
- }
- });
- };
-
- // Add markers to the line if it needs one
- initMarkers = function (line) {
- var isImage = line.text.match(imageMarkdownRegex),
- hasMarker = line.text.match(markerRegex);
-
- if (isImage && !hasMarker) {
- addMarker(line, editor.getLineNumber(line));
- }
- };
-
- // Initialise
- editor.eachLine(initMarkers);
-
- // Public API
- _.extend(this, {
- markers: markers,
- checkMarkers: checkMarkers,
- addMarker: addMarker,
- stripMarkerFromLine: stripMarkerFromLine,
- getMarkerRegexForId: markerRegexForId
- });
- };
-
- Ghost.Editor = Ghost.Editor || {};
- Ghost.Editor.MarkerManager = MarkerManager;
-}());
\ No newline at end of file
diff --git a/core/clientold/assets/lib/editor/mobileCodeMirror.js b/core/clientold/assets/lib/editor/mobileCodeMirror.js
deleted file mode 100644
index 7e2d3faad5..0000000000
--- a/core/clientold/assets/lib/editor/mobileCodeMirror.js
+++ /dev/null
@@ -1,112 +0,0 @@
-// Taken from js-bin with thanks to Remy Sharp
-// yeah, nasty, but it allows me to switch from a RTF to plain text if we're running a iOS
-
-/*global Ghost, $, _, DocumentTouch, CodeMirror*/
-(function () {
- Ghost.touchEditor = false;
-
- var noop = function () {},
- hasTouchScreen,
- smallScreen,
- TouchEditor,
- _oldCM,
- key;
-
- // Taken from "Responsive design & the Guardian" with thanks to Matt Andrews
- // Added !window._phantom so that the functional tests run as though this is not a touch screen.
- // In future we can do something more advanced here for testing both touch and non touch
- hasTouchScreen = function () {
- return !window._phantom &&
- (
- ('ontouchstart' in window) ||
- (window.DocumentTouch && document instanceof DocumentTouch)
- );
- };
-
- smallScreen = function () {
- if (window.matchMedia('(max-width: 1000px)').matches) {
- return true;
- }
-
- return false;
- };
-
- if (hasTouchScreen()) {
- $('body').addClass('touch-editor');
- Ghost.touchEditor = true;
-
- TouchEditor = function (el, options) {
- /*jshint unused:false*/
- this.textarea = el;
- this.win = { document : this.textarea };
- this.ready = true;
- this.wrapping = document.createElement('div');
-
- var textareaParent = this.textarea.parentNode;
- this.wrapping.appendChild(this.textarea);
- textareaParent.appendChild(this.wrapping);
-
- this.textarea.style.opacity = 1;
-
- $(this.textarea).blur(_.throttle(function () {
- $(document).trigger('markdownEditorChange', { panelId: el.id });
- }, 200));
-
- if (!smallScreen()) {
- $(this.textarea).on('change', _.throttle(function () {
- $(document).trigger('markdownEditorChange', { panelId: el.id });
- }, 200));
- }
- };
-
- TouchEditor.prototype = {
- setOption: function (type, handler) {
- if (type === 'onChange') {
- $(this.textarea).change(handler);
- }
- },
- eachLine: function () {
- return [];
- },
- getValue: function () {
- return this.textarea.value;
- },
- setValue: function (code) {
- this.textarea.value = code;
- },
- focus: noop,
- getCursor: function () {
- return { line: 0, ch: 0 };
- },
- setCursor: noop,
- currentLine: function () {
- return 0;
- },
- cursorPosition: function () {
- return { character: 0 };
- },
- addMarkdown: noop,
- nthLine: noop,
- refresh: noop,
- selectLines: noop,
- on: noop
- };
-
- _oldCM = CodeMirror;
-
- // CodeMirror = noop;
-
- for (key in _oldCM) {
- if (_oldCM.hasOwnProperty(key)) {
- CodeMirror[key] = noop;
- }
- }
-
- CodeMirror.fromTextArea = function (el, options) {
- return new TouchEditor(el, options);
- };
-
- CodeMirror.keyMap = { basic: {} };
-
- }
-}());
\ No newline at end of file
diff --git a/core/clientold/assets/lib/editor/scrollHandler.js b/core/clientold/assets/lib/editor/scrollHandler.js
deleted file mode 100644
index 05d6638cd6..0000000000
--- a/core/clientold/assets/lib/editor/scrollHandler.js
+++ /dev/null
@@ -1,47 +0,0 @@
-// # Ghost Editor Scroll Handler
-//
-// Scroll Handler does the (currently very simple / naive) job of syncing the right pane with the left pane
-// as the right pane scrolls
-
-/*global Ghost, _ */
-(function () {
- 'use strict';
-
- var ScrollHandler = function (markdown, preview) {
- var $markdownViewPort = markdown.scrollViewPort(),
- $previewViewPort = preview.scrollViewPort(),
- $markdownContent = markdown.scrollContent(),
- $previewContent = preview.scrollContent(),
- syncScroll;
-
- syncScroll = _.throttle(function () {
- // calc position
- var markdownHeight = $markdownContent.height() - $markdownViewPort.height(),
- previewHeight = $previewContent.height() - $previewViewPort.height(),
- ratio = previewHeight / markdownHeight,
- previewPosition = $markdownViewPort.scrollTop() * ratio;
-
- if (markdown.isCursorAtEnd()) {
- previewPosition = previewHeight + 30;
- }
-
- // apply new scroll
- $previewViewPort.scrollTop(previewPosition);
- }, 10);
-
- _.extend(this, {
- enable: function () { // Handle Scroll Events
- $markdownViewPort.on('scroll', syncScroll);
- $markdownViewPort.scrollClass({target: '.entry-markdown', offset: 10});
- $previewViewPort.scrollClass({target: '.entry-preview', offset: 10});
- },
- disable: function () {
- $markdownViewPort.off('scroll', syncScroll);
- }
- });
-
- };
-
- Ghost.Editor = Ghost.Editor || {};
- Ghost.Editor.ScrollHandler = ScrollHandler;
-} ());
\ No newline at end of file
diff --git a/core/clientold/assets/lib/editor/uploadManager.js b/core/clientold/assets/lib/editor/uploadManager.js
deleted file mode 100644
index 7bbee4e14c..0000000000
--- a/core/clientold/assets/lib/editor/uploadManager.js
+++ /dev/null
@@ -1,153 +0,0 @@
-// # Ghost Editor Upload Manager
-//
-// UploadManager ensures that markdown gets updated when images get uploaded via the Preview.
-//
-// The Ghost Editor has a particularly tricky problem to solve, in that it is possible to upload an image by
-// interacting with the preview. The process of uploading an image is handled by uploader.js, but there is still
-// a lot of work needed to ensure that uploaded files end up in the right place - that is that the image
-// path gets added to the correct piece of markdown in the editor.
-//
-// To solve this, Ghost adds a unique 'marker' to each piece of markdown which represents an image:
-// More detail about how the markers work can be find in markerManager.js
-//
-// UploadManager handles changes in the editor, looking for text which matches image markdown, and telling the marker
-// manager to add a marker. It also checks changed lines to see if they have a marker but are no longer an image.
-//
-// UploadManager's most important job is handling uploads such that when a successful upload completes, the correct
-// piece of image markdown is updated with the path.
-// This is done in part by ghostImagePreview.js, which takes the marker from the markdown and uses it to create an ID
-// on the dropzone. When an upload completes successfully from uploader.js, the event thrown contains reference to the
-// dropzone, from which uploadManager can pull the ID & then get the right marker from the Marker Manager.
-//
-// Without a doubt, the separation of concerns between the uploadManager, and the markerManager could be vastly
-// improved
-
-
-/*global $, _, Ghost */
-(function () {
- 'use strict';
-
- var imageMarkdownRegex = /^(?:\{<(.*?)>\})?!(?:\[([^\n\]]*)\])(?:\(([^\n\]]*)\))?$/gim,
- markerRegex = /\{<([\w\W]*?)>\}/,
- UploadManager;
-
- UploadManager = function (markdown) {
- var editor = markdown.codemirror,
- markerMgr = new Ghost.Editor.MarkerManager(editor),
- findLine,
- checkLine,
- value,
- handleUpload,
- handleChange;
-
- // Find the line with the marker which matches
- findLine = function (result_id) {
- // try to find the right line to replace
- if (markerMgr.markers.hasOwnProperty(result_id) && markerMgr.markers[result_id].find()) {
- return editor.getLineHandle(markerMgr.markers[result_id].find().from.line);
- }
-
- return false;
- };
-
- // Check the given line to see if it has an image, and if it correctly has a marker
- // In the special case of lines which were just pasted in, any markers are removed to prevent duplication
- checkLine = function (ln, mode) {
- var line = editor.getLineHandle(ln),
- isImage = line.text.match(imageMarkdownRegex),
- hasMarker;
-
- // We care if it is an image
- if (isImage) {
- hasMarker = line.text.match(markerRegex);
-
- if (hasMarker && (mode === 'paste' || mode === 'undo')) {
- // this could be a duplicate, and won't be a real marker
- markerMgr.stripMarkerFromLine(line);
- }
-
- if (!hasMarker) {
- markerMgr.addMarker(line, ln);
- }
- }
- // TODO: hasMarker but no image?
- };
-
- // Get the markdown with all the markers stripped
- value = function () {
- var value = editor.getValue();
-
- _.each(markerMgr.markers, function (marker, id) {
- /*jshint unused:false*/
- value = value.replace(markerMgr.getMarkerRegexForId(id), '');
- });
-
- return value;
- };
-
- // Match the uploaded file to a line in the editor, and update that line with a path reference
- // ensuring that everything ends up in the correct place and format.
- handleUpload = function (e, result_src) {
- var line = findLine($(e.currentTarget).attr('id')),
- lineNumber = editor.getLineNumber(line),
- match = line.text.match(/\([^\n]*\)?/),
- replacement = '(http://)';
-
- if (match) {
- // simple case, we have the parenthesis
- editor.setSelection(
- {line: lineNumber, ch: match.index + 1},
- {line: lineNumber, ch: match.index + match[0].length - 1}
- );
- } else {
- match = line.text.match(/\]/);
- if (match) {
- editor.replaceRange(
- replacement,
- {line: lineNumber, ch: match.index + 1},
- {line: lineNumber, ch: match.index + 1}
- );
- editor.setSelection(
- {line: lineNumber, ch: match.index + 2},
- {line: lineNumber, ch: match.index + replacement.length }
- );
- }
- }
- editor.replaceSelection(result_src);
- };
-
- // Change events from CodeMirror tell us which lines have changed.
- // Each changed line is then checked to see if a marker needs to be added or removed
- handleChange = function (cm, changeObj) {
- /*jshint unused:false*/
- var linesChanged = _.range(changeObj.from.line, changeObj.from.line + changeObj.text.length);
-
- _.each(linesChanged, function (ln) {
- checkLine(ln, changeObj.origin);
- });
-
- // Is this a line which may have had a marker on it?
- markerMgr.checkMarkers();
- };
-
- // Public API
- _.extend(this, {
- value: value,
- enable: function () {
- var filestorage = $('#entry-markdown-content').data('filestorage');
- $('.js-drop-zone').upload({editor: true, fileStorage: filestorage});
- $('.js-drop-zone').on('uploadstart', markdown.off);
- $('.js-drop-zone').on('uploadfailure', markdown.on);
- $('.js-drop-zone').on('uploadsuccess', markdown.on);
- $('.js-drop-zone').on('uploadsuccess', handleUpload);
- },
- disable: function () {
- $('.js-drop-zone').off('uploadsuccess', handleUpload);
- }
- });
-
- editor.on('change', handleChange);
- };
- Ghost.Editor = Ghost.Editor || {};
- Ghost.Editor.UploadManager = UploadManager;
-}());
\ No newline at end of file
diff --git a/core/clientold/assets/lib/jquery-utils.js b/core/clientold/assets/lib/jquery-utils.js
deleted file mode 100644
index 1c63de9af9..0000000000
--- a/core/clientold/assets/lib/jquery-utils.js
+++ /dev/null
@@ -1,175 +0,0 @@
-// # Ghost jQuery Utils
-
-/*global window, document, $ */
-
-(function () {
- "use strict";
-
- // ## UTILS
-
- /**
- * Allows to check contents of each element exactly
- * @param {Object} obj
- * @param {*} index
- * @param {*} meta
- * @param {*} stack
- * @returns {boolean}
- */
- $.expr[":"].containsExact = function (obj, index, meta, stack) {
- /*jshint unused:false*/
- return (obj.textContent || obj.innerText || $(obj).text() || "") === meta[3];
- };
-
- /**
- * Center an element to the window vertically and centrally
- * @returns {*}
- */
- $.fn.center = function (options) {
- var $window = $(window),
- config = $.extend({
- animate : true,
- successTrigger : 'centered'
- }, options);
-
- return this.each(function () {
- var $this = $(this);
- $this.css({
- 'position': 'absolute'
- });
- if (config.animate) {
- $this.animate({
- 'left': ($window.width() / 2) - $this.outerWidth() / 2 + 'px',
- 'top': ($window.height() / 2) - $this.outerHeight() / 2 + 'px'
- });
- } else {
- $this.css({
- 'left': ($window.width() / 2) - $this.outerWidth() / 2 + 'px',
- 'top': ($window.height() / 2) - $this.outerHeight() / 2 + 'px'
- });
- }
- $(window).trigger(config.successTrigger);
- });
- };
-
- // ## getTransformProperty
- // This returns the transition duration for an element, good for calling things after a transition has finished.
- // **Original**: [https://gist.github.com/mandelbro/4067903](https://gist.github.com/mandelbro/4067903)
- // **returns:** the elements transition duration
- $.fn.transitionDuration = function () {
- var $this = $(this);
-
- // check the main transition duration property
- if ($this.css('transition-duration')) {
- return Math.round(parseFloat(this.css('transition-duration')) * 1000);
- }
-
- // check the vendor transition duration properties
- if (this.css('-webkit-transition-duration')) {
- return Math.round(parseFloat(this.css('-webkit-transition-duration')) * 1000);
- }
-
- if (this.css('-ms-transition-duration')) {
- return Math.round(parseFloat(this.css('-ms-transition-duration')) * 1000);
- }
-
- if (this.css('-moz-transition-duration')) {
- return Math.round(parseFloat(this.css('-moz-transition-duration')) * 1000);
- }
-
- if (this.css('-o-transition-duration')) {
- return Math.round(parseFloat(this.css('-o-transition-duration')) * 1000);
- }
-
- // if we're here, then no transition duration was found, return 0
- return 0;
- };
-
- // ## scrollShadow
- // This adds a 'scroll' class to the targeted element when the element is scrolled
- // **target:** The element in which the class is applied. Defaults to scrolled element.
- // **class-name:** The class which is applied.
- // **offset:** How far the user has to scroll before the class is applied.
- $.fn.scrollClass = function (options) {
- var config = $.extend({
- 'target' : '',
- 'class-name' : 'scrolling',
- 'offset' : 1
- }, options);
-
- return this.each(function () {
- var $this = $(this),
- $target = $this;
- if (config.target) {
- $target = $(config.target);
- }
- $this.scroll(function () {
- if ($this.scrollTop() > config.offset) {
- $target.addClass(config['class-name']);
- } else {
- $target.removeClass(config['class-name']);
- }
- });
- });
- };
-
- $.fn.selectText = function () {
- var elem = this[0],
- range,
- selection;
- if (document.body.createTextRange) {
- range = document.body.createTextRange();
- range.moveToElementText(elem);
- range.select();
- } else if (window.getSelection) {
- selection = window.getSelection();
- range = document.createRange();
- range.selectNodeContents(elem);
- selection.removeAllRanges();
- selection.addRange(range);
- }
- };
-
- /**
- * Set interactions for all menus and overlays
- * This finds all visible 'hideClass' elements and hides them upon clicking away from the element itself.
- * A callback can be defined to customise the results. By default it will hide the element.
- * @param {Function} callback
- */
- $.fn.hideAway = function (callback) {
- var $self = $(this);
- $("body").on('click', function (event) {
- var $target = $(event.target),
- hideClass = $self.selector;
- if (!$target.parents().is(hideClass + ":visible") && !$target.is(hideClass + ":visible")) {
- if (callback) {
- callback($("body").find(hideClass + ":visible"));
- } else {
- $("body").find(hideClass + ":visible").fadeOut(150);
-
- // Toggle active classes on menu headers
- $("[data-toggle].active").removeClass("active");
- }
- }
- });
-
- return this;
- };
-
- // ## GLOBALS
-
- $('.overlay').hideAway();
-
- /**
- * Adds appropriate inflection for pluralizing the singular form of a word when appropriate.
- * This is an overly simplistic implementation that does not handle irregular plurals.
- * @param {Number} count
- * @param {String} singularWord
- * @returns {String}
- */
- $.pluralize = function inflect(count, singularWord) {
- var base = [count, ' ', singularWord];
-
- return (count === 1) ? base.join('') : base.concat('s').join('');
- };
-
-}());
diff --git a/core/clientold/assets/lib/uploader.js b/core/clientold/assets/lib/uploader.js
deleted file mode 100644
index 99e5acf68d..0000000000
--- a/core/clientold/assets/lib/uploader.js
+++ /dev/null
@@ -1,260 +0,0 @@
-/*global jQuery, Ghost */
-(function ($) {
- "use strict";
-
- var UploadUi;
-
-
- UploadUi = function ($dropzone, settings) {
- var $url = '
',
- $cancel = 'Delete',
- $progress = $('', {
- "class" : "js-upload-progress progress progress-success active",
- "role": "progressbar",
- "aria-valuemin": "0",
- "aria-valuemax": "100"
- }).append($("", {
- "class": "js-upload-progress-bar bar",
- "style": "width:0%"
- }));
-
- $.extend(this, {
- complete: function (result) {
- var self = this;
-
- function showImage(width, height) {
- $dropzone.find('img.js-upload-target').attr({"width": width, "height": height}).css({"display": "block"});
- $dropzone.find('.fileupload-loading').remove();
- $dropzone.css({"height": "auto"});
- $dropzone.delay(250).animate({opacity: 100}, 1000, function () {
- $('.js-button-accept').prop('disabled', false);
- self.init();
- });
- }
-
- function animateDropzone($img) {
- $dropzone.animate({opacity: 0}, 250, function () {
- $dropzone.removeClass('image-uploader').addClass('pre-image-uploader');
- $dropzone.css({minHeight: 0});
- self.removeExtras();
- $dropzone.animate({height: $img.height()}, 250, function () {
- showImage($img.width(), $img.height());
- });
- });
- }
-
- function preLoadImage() {
- var $img = $dropzone.find('img.js-upload-target')
- .attr({'src': '', "width": 'auto', "height": 'auto'});
-
- $progress.animate({"opacity": 0}, 250, function () {
- $dropzone.find('span.media').after('');
- if (!settings.editor) {$progress.find('.fileupload-loading').css({"top": "56px"}); }
- });
- $dropzone.trigger("uploadsuccess", [result]);
- $img.one('load', function () {
- animateDropzone($img);
- }).attr('src', result);
- }
- preLoadImage();
- },
-
- bindFileUpload: function () {
- var self = this;
-
- $dropzone.find('.js-fileupload').fileupload().fileupload("option", {
- url: Ghost.paths.subdir + '/ghost/upload/',
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- add: function (e, data) {
- /*jshint unused:false*/
- $('.js-button-accept').prop('disabled', true);
- $dropzone.find('.js-fileupload').removeClass('right');
- $dropzone.find('.js-url').remove();
- $progress.find('.js-upload-progress-bar').removeClass('fail');
- $dropzone.trigger('uploadstart', [$dropzone.attr('id')]);
- $dropzone.find('span.media, div.description, a.image-url, a.image-webcam')
- .animate({opacity: 0}, 250, function () {
- $dropzone.find('div.description').hide().css({"opacity": 100});
- if (settings.progressbar) {
- $dropzone.find('div.js-fail').after($progress);
- $progress.animate({opacity: 100}, 250);
- }
- data.submit();
- });
- },
- dropZone: settings.fileStorage ? $dropzone : null,
- progressall: function (e, data) {
- /*jshint unused:false*/
- var progress = parseInt(data.loaded / data.total * 100, 10);
- if (!settings.editor) {$progress.find('div.js-progress').css({"position": "absolute", "top": "40px"}); }
- if (settings.progressbar) {
- $dropzone.trigger("uploadprogress", [progress, data]);
- $progress.find('.js-upload-progress-bar').css('width', progress + '%');
- }
- },
- fail: function (e, data) {
- /*jshint unused:false*/
- $('.js-button-accept').prop('disabled', false);
- $dropzone.trigger("uploadfailure", [data.result]);
- $dropzone.find('.js-upload-progress-bar').addClass('fail');
- if (data.jqXHR.status === 413) {
- $dropzone.find('div.js-fail').text("The image you uploaded was larger than the maximum file size your server allows.");
- } else if (data.jqXHR.status === 415) {
- $dropzone.find('div.js-fail').text("The image type you uploaded is not supported. Please use .PNG, .JPG, .GIF, .SVG.");
- } else {
- $dropzone.find('div.js-fail').text("Something went wrong :(");
- }
- $dropzone.find('div.js-fail, button.js-fail').fadeIn(1500);
- $dropzone.find('button.js-fail').on('click', function () {
- $dropzone.css({minHeight: 0});
- $dropzone.find('div.description').show();
- self.removeExtras();
- self.init();
- });
- },
- done: function (e, data) {
- /*jshint unused:false*/
- self.complete(data.result);
- }
- });
- },
-
- buildExtras: function () {
- if (!$dropzone.find('span.media')[0]) {
- $dropzone.prepend('Image Upload');
- }
- if (!$dropzone.find('div.description')[0]) {
- $dropzone.append('Add image
');
- }
- if (!$dropzone.find('div.js-fail')[0]) {
- $dropzone.append('Something went wrong :(
');
- }
- if (!$dropzone.find('button.js-fail')[0]) {
- $dropzone.append('');
- }
- if (!$dropzone.find('a.image-url')[0]) {
- $dropzone.append('URL');
- }
-// if (!$dropzone.find('a.image-webcam')[0]) {
-// $dropzone.append('Webcam');
-// }
- },
-
- removeExtras: function () {
- $dropzone.find('span.media, div.js-upload-progress, a.image-url, a.image-upload, a.image-webcam, div.js-fail, button.js-fail, a.js-cancel').remove();
- },
-
- initWithDropzone: function () {
- var self = this;
- //This is the start point if no image exists
- $dropzone.find('img.js-upload-target').css({"display": "none"});
- $dropzone.removeClass('pre-image-uploader image-uploader-url').addClass('image-uploader');
- this.removeExtras();
- this.buildExtras();
- this.bindFileUpload();
- if (!settings.fileStorage) {
- self.initUrl();
- return;
- }
- $dropzone.find('a.image-url').on('click', function () {
- self.initUrl();
- });
- },
- initUrl: function () {
- var self = this, val;
- this.removeExtras();
- $dropzone.addClass('image-uploader-url').removeClass('pre-image-uploader');
- $dropzone.find('.js-fileupload').addClass('right');
- if (settings.fileStorage) {
- $dropzone.append($cancel);
- }
- $dropzone.find('.js-cancel').on('click', function () {
- $dropzone.find('.js-url').remove();
- $dropzone.find('.js-fileupload').removeClass('right');
- self.removeExtras();
- self.initWithDropzone();
- });
-
- $dropzone.find('div.description').before($url);
-
- if (settings.editor) {
- $dropzone.find('div.js-url').append('');
- }
-
- $dropzone.find('.js-button-accept').on('click', function () {
- val = $dropzone.find('.js-upload-url').val();
- $dropzone.find('div.description').hide();
- $dropzone.find('.js-fileupload').removeClass('right');
- $dropzone.find('.js-url').remove();
- if (val === "") {
- $dropzone.trigger("uploadsuccess", 'http://');
- self.initWithDropzone();
- } else {
- self.complete(val);
- }
- });
-
- // Only show the toggle icon if there is a dropzone mode to go back to
- if (settings.fileStorage !== false) {
- $dropzone.append('Upload');
- }
-
- $dropzone.find('a.image-upload').on('click', function () {
- $dropzone.find('.js-url').remove();
- $dropzone.find('.js-fileupload').removeClass('right');
- self.initWithDropzone();
- });
-
- },
- initWithImage: function () {
- var self = this;
- // This is the start point if an image already exists
- $dropzone.removeClass('image-uploader image-uploader-url').addClass('pre-image-uploader');
- $dropzone.find('div.description').hide();
- $dropzone.append($cancel);
- $dropzone.find('.js-cancel').on('click', function () {
- $dropzone.find('img.js-upload-target').attr({'src': ''});
- $dropzone.find('div.description').show();
- $dropzone.delay(2500).animate({opacity: 100}, 1000, function () {
- self.init();
- });
-
- $dropzone.trigger("uploadsuccess", 'http://');
- self.initWithDropzone();
- });
- },
-
- init: function () {
- // First check if field image is defined by checking for js-upload-target class
- if (!$dropzone.find('img.js-upload-target')[0]) {
- // This ensures there is an image we can hook into to display uploaded image
- $dropzone.prepend('');
- }
- $('.js-button-accept').prop('disabled', false);
- if ($dropzone.find('img.js-upload-target').attr('src') === '') {
- this.initWithDropzone();
- } else {
- this.initWithImage();
- }
- }
- });
- };
-
-
- $.fn.upload = function (options) {
- var settings = $.extend({
- progressbar: true,
- editor: false,
- fileStorage: true
- }, options);
- return this.each(function () {
- var $dropzone = $(this),
- ui;
-
- ui = new UploadUi($dropzone, settings);
- ui.init();
- });
- };
-}(jQuery));
diff --git a/core/clientold/assets/vendor/shortcuts.js b/core/clientold/assets/vendor/shortcuts.js
deleted file mode 100644
index 13105b9966..0000000000
--- a/core/clientold/assets/vendor/shortcuts.js
+++ /dev/null
@@ -1,224 +0,0 @@
-/**
- * http://www.openjs.com/scripts/events/keyboard_shortcuts/
- * Version : 2.01.B
- * By Binny V A
- * License : BSD
- */
-shortcut = {
- 'all_shortcuts':{},//All the shortcuts are stored in this array
- 'add': function(shortcut_combination,callback,opt) {
- //Provide a set of default options
- var default_options = {
- 'type':'keydown',
- 'propagate':false,
- 'disable_in_input':false,
- 'target':document,
- 'keycode':false
- }
- if(!opt) opt = default_options;
- else {
- for(var dfo in default_options) {
- if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
- }
- }
-
- var ele = opt.target;
- if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
- var ths = this;
- shortcut_combination = shortcut_combination.toLowerCase();
-
- //The function to be called at keypress
- var func = function(e) {
- e = e || window.event;
-
- if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
- var element;
- if(e.target) element=e.target;
- else if(e.srcElement) element=e.srcElement;
- if(element.nodeType==3) element=element.parentNode;
-
- if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
- }
-
- //Find Which key is pressed
- if (e.keyCode) code = e.keyCode;
- else if (e.which) code = e.which;
- else return;
- var character = String.fromCharCode(code).toLowerCase();
-
- if(code == 188) character=","; //If the user presses , when the type is onkeydown
- if(code == 190) character="."; //If the user presses , when the type is onkeydown
-
- var keys = shortcut_combination.split("+");
- //Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
- var kp = 0;
-
- //Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
- var shift_nums = {
- "`":"~",
- "1":"!",
- "2":"@",
- "3":"#",
- "4":"$",
- "5":"%",
- "6":"^",
- "7":"&",
- "8":"*",
- "9":"(",
- "0":")",
- "-":"_",
- "=":"+",
- ";":":",
- "'":"\"",
- ",":"<",
- ".":">",
- "/":"?",
- "\\":"|"
- }
- //Special Keys - and their codes
- var special_keys = {
- 'esc':27,
- 'escape':27,
- 'tab':9,
- 'space':32,
- 'return':13,
- 'enter':13,
- 'backspace':8,
-
- 'scrolllock':145,
- 'scroll_lock':145,
- 'scroll':145,
- 'capslock':20,
- 'caps_lock':20,
- 'caps':20,
- 'numlock':144,
- 'num_lock':144,
- 'num':144,
-
- 'pause':19,
- 'break':19,
-
- 'insert':45,
- 'home':36,
- 'delete':46,
- 'end':35,
-
- 'pageup':33,
- 'page_up':33,
- 'pu':33,
-
- 'pagedown':34,
- 'page_down':34,
- 'pd':34,
-
- 'left':37,
- 'up':38,
- 'right':39,
- 'down':40,
-
- 'f1':112,
- 'f2':113,
- 'f3':114,
- 'f4':115,
- 'f5':116,
- 'f6':117,
- 'f7':118,
- 'f8':119,
- 'f9':120,
- 'f10':121,
- 'f11':122,
- 'f12':123
- }
-
- var modifiers = {
- shift: { wanted:false, pressed:false},
- ctrl : { wanted:false, pressed:false},
- alt : { wanted:false, pressed:false},
- meta : { wanted:false, pressed:false} //Meta is Mac specific
- };
-
- if(e.ctrlKey) modifiers.ctrl.pressed = true;
- if(e.shiftKey) modifiers.shift.pressed = true;
- if(e.altKey) modifiers.alt.pressed = true;
- if(e.metaKey) modifiers.meta.pressed = true;
-
- for(var i=0; k=keys[i],i 1) { //If it is a special key
- if(special_keys[k] == code) kp++;
-
- } else if(opt['keycode']) {
- if(opt['keycode'] == code) kp++;
-
- } else { //The special keys did not match
- if(character == k) kp++;
- else {
- if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
- character = shift_nums[character];
- if(character == k) kp++;
- }
- }
- }
- }
-
- if(kp == keys.length &&
- modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
- modifiers.shift.pressed == modifiers.shift.wanted &&
- modifiers.alt.pressed == modifiers.alt.wanted &&
- modifiers.meta.pressed == modifiers.meta.wanted) {
- callback(e);
-
- if(!opt['propagate']) { //Stop the event
- //e.cancelBubble is supported by IE - this will kill the bubbling process.
- e.cancelBubble = true;
- e.returnValue = false;
-
- //e.stopPropagation works in Firefox.
- if (e.stopPropagation) {
- e.stopPropagation();
- e.preventDefault();
- }
- return false;
- }
- }
- }
- this.all_shortcuts[shortcut_combination] = {
- 'callback':func,
- 'target':ele,
- 'event': opt['type']
- };
- //Attach the function with the event
- if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
- else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
- else ele['on'+opt['type']] = func;
- },
-
- //Remove the shortcut - just specify the shortcut and I will remove the binding
- 'remove':function(shortcut_combination) {
- shortcut_combination = shortcut_combination.toLowerCase();
- var binding = this.all_shortcuts[shortcut_combination];
- delete(this.all_shortcuts[shortcut_combination])
- if(!binding) return;
- var type = binding['event'];
- var ele = binding['target'];
- var callback = binding['callback'];
-
- if(ele.detachEvent) ele.detachEvent('on'+type, callback);
- else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
- else ele['on'+type] = false;
- }
-};
diff --git a/core/clientold/assets/vendor/to-title-case.js b/core/clientold/assets/vendor/to-title-case.js
deleted file mode 100644
index cdb561e718..0000000000
--- a/core/clientold/assets/vendor/to-title-case.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * To Title Case 2.0.1 – http://individed.com/code/to-title-case/
- * Copyright © 2008–2012 David Gouch. Licensed under the MIT License.
- */
-
-String.prototype.toTitleCase = function () {
- var smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|vs?\.?|via)$/i;
-
- return this.replace(/([^\W_]+[^\s-]*) */g, function (match, p1, index, title) {
- if (index > 0 && index + p1.length !== title.length &&
- p1.search(smallWords) > -1 && title.charAt(index - 2) !== ":" &&
- title.charAt(index - 1).search(/[^\s-]/) < 0) {
- return match.toLowerCase();
- }
-
- if (p1.substr(1).search(/[A-Z]|\../) > -1) {
- return match;
- }
-
- return match.charAt(0).toUpperCase() + match.substr(1);
- });
-};
\ No newline at end of file
diff --git a/core/clientold/helpers/index.js b/core/clientold/helpers/index.js
deleted file mode 100644
index 462c4447e5..0000000000
--- a/core/clientold/helpers/index.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/*globals Handlebars, moment, Ghost */
-(function () {
- 'use strict';
- Handlebars.registerHelper('date', function (context, options) {
- if (!options && context.hasOwnProperty('hash')) {
- options = context;
- context = undefined;
-
- // set to published_at by default, if it's available
- // otherwise, this will print the current date
- if (this.published_at) {
- context = this.published_at;
- }
- }
-
- // ensure that context is undefined, not null, as that can cause errors
- context = context === null ? undefined : context;
-
- var f = options.hash.format || 'MMM Do, YYYY',
- timeago = options.hash.timeago,
- date;
-
-
- if (timeago) {
- date = moment(context).fromNow();
- } else {
- date = moment(context).format(f);
- }
- return date;
- });
-
- Handlebars.registerHelper('admin_url', function () {
- return Ghost.paths.subdir + '/ghost';
- });
-
- Handlebars.registerHelper('asset', function (context, options) {
- var output = '',
- isAdmin = options && options.hash && options.hash.ghost;
-
- output += Ghost.paths.subdir + '/';
-
- if (!context.match(/^shared/)) {
- if (isAdmin) {
- output += 'ghost/';
- } else {
- output += 'assets/';
- }
- }
-
- output += context;
- return new Handlebars.SafeString(output);
- });
-}());
diff --git a/core/clientold/init.js b/core/clientold/init.js
deleted file mode 100644
index 9f4bda35dc..0000000000
--- a/core/clientold/init.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*globals window, $, _, Backbone, validator */
-(function () {
- 'use strict';
-
- function ghostPaths() {
- var path = window.location.pathname,
- subdir = path.substr(0, path.search('/ghost/'));
-
- return {
- subdir: subdir,
- apiRoot: subdir + '/ghost/api/v0.1'
- };
- }
-
- var Ghost = {
- Layout : {},
- Views : {},
- Collections : {},
- Models : {},
-
- paths: ghostPaths(),
-
- // This is a helper object to denote legacy things in the
- // middle of being transitioned.
- temporary: {},
-
- currentView: null,
- router: null
- };
-
- _.extend(Ghost, Backbone.Events);
-
- Backbone.oldsync = Backbone.sync;
- // override original sync method to make header request contain csrf token
- Backbone.sync = function (method, model, options, error) {
- options.beforeSend = function (xhr) {
- xhr.setRequestHeader('X-CSRF-Token', $("meta[name='csrf-param']").attr('content'));
- };
- /* call the old sync method */
- return Backbone.oldsync(method, model, options, error);
- };
-
- Backbone.oldModelProtoUrl = Backbone.Model.prototype.url;
- //overwrite original url method to add slash to end of the url if needed.
- Backbone.Model.prototype.url = function () {
- var url = Backbone.oldModelProtoUrl.apply(this, arguments);
- return url + (url.charAt(url.length - 1) === '/' ? '' : '/');
- };
-
- Ghost.init = function () {
- Ghost.router = new Ghost.Router();
-
- // This is needed so Backbone recognizes elements already rendered server side
- // as valid views, and events are bound
- Ghost.notifications = new Ghost.Views.NotificationCollection({model: []});
-
- Backbone.history.start({
- pushState: true,
- hashChange: false,
- root: Ghost.paths.subdir + '/ghost'
- });
- };
-
- validator.handleErrors = function (errors) {
- Ghost.notifications.clearEverything();
- _.each(errors, function (errorObj) {
-
- Ghost.notifications.addItem({
- type: 'error',
- message: errorObj.message || errorObj,
- status: 'passive'
- });
-
- if (errorObj.hasOwnProperty('el')) {
- errorObj.el.addClass('input-error');
- }
- });
- };
-
- window.Ghost = Ghost;
-
- window.addEventListener("load", Ghost.init, false);
-}());
diff --git a/core/clientold/markdown-actions.js b/core/clientold/markdown-actions.js
deleted file mode 100644
index aedc2ef7c5..0000000000
--- a/core/clientold/markdown-actions.js
+++ /dev/null
@@ -1,149 +0,0 @@
-// # Surrounds given text with Markdown syntax
-
-/*global $, CodeMirror, Showdown, moment */
-(function () {
- 'use strict';
- var Markdown = {
- init : function (options, elem) {
- var self = this;
- self.elem = elem;
-
- self.style = (typeof options === 'string') ? options : options.style;
-
- self.options = $.extend({}, CodeMirror.prototype.addMarkdown.options, options);
-
- self.replace();
- },
- replace: function () {
- var text = this.elem.getSelection(), pass = true, cursor = this.elem.getCursor(), line = this.elem.getLine(cursor.line), md, word, letterCount, converter, textIndex, position;
- switch (this.style) {
- case 'h1':
- this.elem.setLine(cursor.line, '# ' + line);
- this.elem.setCursor(cursor.line, cursor.ch + 2);
- pass = false;
- break;
- case 'h2':
- this.elem.setLine(cursor.line, '## ' + line);
- this.elem.setCursor(cursor.line, cursor.ch + 3);
- pass = false;
- break;
- case 'h3':
- this.elem.setLine(cursor.line, '### ' + line);
- this.elem.setCursor(cursor.line, cursor.ch + 4);
- pass = false;
- break;
- case 'h4':
- this.elem.setLine(cursor.line, '#### ' + line);
- this.elem.setCursor(cursor.line, cursor.ch + 5);
- pass = false;
- break;
- case 'h5':
- this.elem.setLine(cursor.line, '##### ' + line);
- this.elem.setCursor(cursor.line, cursor.ch + 6);
- pass = false;
- break;
- case 'h6':
- this.elem.setLine(cursor.line, '###### ' + line);
- this.elem.setCursor(cursor.line, cursor.ch + 7);
- pass = false;
- break;
- case 'link':
- md = this.options.syntax.link.replace('$1', text);
- this.elem.replaceSelection(md, 'end');
- if (!text) {
- this.elem.setCursor(cursor.line, cursor.ch + 1);
- } else {
- textIndex = line.indexOf(text, cursor.ch - text.length);
- position = textIndex + md.length - 1;
- this.elem.setSelection({line: cursor.line, ch: position - 7}, {line: cursor.line, ch: position});
- }
- pass = false;
- break;
- case 'image':
- md = this.options.syntax.image.replace('$1', text);
- if (line !== '') {
- md = "\n\n" + md;
- }
- this.elem.replaceSelection(md, "end");
- cursor = this.elem.getCursor();
- this.elem.setSelection({line: cursor.line, ch: cursor.ch - 8}, {line: cursor.line, ch: cursor.ch - 1});
- pass = false;
- break;
- case 'uppercase':
- md = text.toLocaleUpperCase();
- break;
- case 'lowercase':
- md = text.toLocaleLowerCase();
- break;
- case 'titlecase':
- md = text.toTitleCase();
- break;
- case 'selectword':
- word = this.elem.getTokenAt(cursor);
- if (!/\w$/g.test(word.string)) {
- this.elem.setSelection({line: cursor.line, ch: word.start}, {line: cursor.line, ch: word.end - 1});
- } else {
- this.elem.setSelection({line: cursor.line, ch: word.start}, {line: cursor.line, ch: word.end});
- }
- break;
- case 'copyHTML':
- converter = new Showdown.converter();
- if (text) {
- md = converter.makeHtml(text);
- } else {
- md = converter.makeHtml(this.elem.getValue());
- }
-
- $(".modal-copyToHTML-content").text(md).selectText();
- pass = false;
- break;
- case 'list':
- md = text.replace(/^(\s*)(\w\W*)/gm, '$1* $2');
- this.elem.replaceSelection(md, 'end');
- pass = false;
- break;
- case 'currentDate':
- md = moment(new Date()).format('D MMMM YYYY');
- this.elem.replaceSelection(md, 'end');
- pass = false;
- break;
- case 'newLine':
- if (line !== "") {
- this.elem.setLine(cursor.line, line + "\n\n");
- }
- pass = false;
- break;
- default:
- if (this.options.syntax[this.style]) {
- md = this.options.syntax[this.style].replace('$1', text);
- }
- }
- if (pass && md) {
- this.elem.replaceSelection(md, 'end');
- if (!text) {
- letterCount = md.length;
- this.elem.setCursor({line: cursor.line, ch: cursor.ch - (letterCount / 2)});
- }
- }
- }
- };
-
- CodeMirror.prototype.addMarkdown = function (options) {
- var markdown = Object.create(Markdown);
- markdown.init(options, this);
- };
-
- CodeMirror.prototype.addMarkdown.options = {
- style: null,
- syntax: {
- bold: '**$1**',
- italic: '*$1*',
- strike: '~~$1~~',
- code: '`$1`',
- link: '[$1](http://)',
- image: '![$1](http://)',
- blockquote: '> $1'
- }
- };
-
-}());
diff --git a/core/clientold/mobile-interactions.js b/core/clientold/mobile-interactions.js
deleted file mode 100644
index 853c9e3328..0000000000
--- a/core/clientold/mobile-interactions.js
+++ /dev/null
@@ -1,62 +0,0 @@
-// # Ghost Mobile Interactions
-
-/*global window, document, $, FastClick */
-
-(function () {
- 'use strict';
-
- FastClick.attach(document.body);
-
- // ### general wrapper to handle conditional screen size actions
- function responsiveAction(event, mediaCondition, cb) {
- if (!window.matchMedia(mediaCondition).matches) {
- return;
- }
-
- event.preventDefault();
- event.stopPropagation();
- cb();
- }
-
- // ### Show content preview when swiping left on content list
- $('.manage').on('click', '.content-list ol li', function (event) {
- responsiveAction(event, '(max-width: 800px)', function () {
- $('.content-list').animate({right: '100%', left: '-100%', 'margin-right': '15px'}, 300);
- $('.content-preview').animate({right: '0', left: '0', 'margin-left': '0'}, 300);
- });
- });
-
- // ### Hide content preview
- $('.manage').on('click', '.content-preview .button-back', function (event) {
- responsiveAction(event, '(max-width: 800px)', function () {
- $('.content-list').animate({right: '0', left: '0', 'margin-right': '0'}, 300);
- $('.content-preview').animate({right: '-100%', left: '100%', 'margin-left': '15px'}, 300);
- });
- });
-
- // ### Show settings options page when swiping left on settings menu link
- $('.settings').on('click', '.settings-menu li', function (event) {
- responsiveAction(event, '(max-width: 800px)', function () {
- $('.settings-sidebar').animate({right: '100%', left: '-102%', 'margin-right': '15px'}, 300);
- $('.settings-content').animate({right: '0', left: '0', 'margin-left': '0'}, 300);
- $('.settings-content .button-back, .settings-content .button-save').css('display', 'inline-block');
- });
- });
-
- // ### Hide settings options page
- $('.settings').on('click', '.settings-content .button-back', function (event) {
- responsiveAction(event, '(max-width: 800px)', function () {
- $('.settings-sidebar').animate({right: '0', left: '0', 'margin-right': '0'}, 300);
- $('.settings-content').animate({right: '-100%', left: '100%', 'margin-left': '15'}, 300);
- $('.settings-content .button-back, .settings-content .button-save').css('display', 'none');
- });
- });
-
- // ### Toggle the sidebar menu
- $('[data-off-canvas]').on('click', function (event) {
- responsiveAction(event, '(max-width: 650px)', function () {
- $('body').toggleClass('off-canvas');
- });
- });
-
-}());
diff --git a/core/clientold/models/base.js b/core/clientold/models/base.js
deleted file mode 100644
index bd7c179e64..0000000000
--- a/core/clientold/models/base.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/*global Ghost, _, Backbone, NProgress */
-
-(function () {
- "use strict";
- NProgress.configure({ showSpinner: false });
-
- // Adds in a call to start a loading bar
- // This is sets up a success function which completes the loading bar
- function wrapSync(method, model, options) {
- if (options !== undefined && _.isObject(options)) {
- NProgress.start();
-
- /*jshint validthis:true */
- var self = this,
- oldSuccess = options.success;
- /*jshint validthis:false */
-
- options.success = function () {
- NProgress.done();
- return oldSuccess.apply(self, arguments);
- };
- }
-
- /*jshint validthis:true */
- return Backbone.sync.call(this, method, model, options);
- }
-
- Ghost.ProgressModel = Backbone.Model.extend({
- sync: wrapSync
- });
-
- Ghost.ProgressCollection = Backbone.Collection.extend({
- sync: wrapSync
- });
-}());
diff --git a/core/clientold/models/post.js b/core/clientold/models/post.js
deleted file mode 100644
index 7dda878c09..0000000000
--- a/core/clientold/models/post.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*global Ghost, _, Backbone, JSON */
-(function () {
- 'use strict';
-
- Ghost.Models.Post = Ghost.ProgressModel.extend({
-
- defaults: {
- status: 'draft'
- },
-
- blacklist: ['published', 'draft'],
-
- parse: function (resp) {
-
- if (resp.posts) {
- resp = resp.posts[0];
- }
- if (resp.status) {
- resp.published = resp.status === 'published';
- resp.draft = resp.status === 'draft';
- }
- if (resp.tags) {
- return resp;
- }
- return resp;
- },
-
- validate: function (attrs) {
- if (_.isEmpty(attrs.title)) {
- return 'You must specify a title for the post.';
- }
- },
-
- addTag: function (tagToAdd) {
- var tags = this.get('tags') || [];
- tags.push(tagToAdd);
- this.set('tags', tags);
- },
-
- removeTag: function (tagToRemove) {
- var tags = this.get('tags') || [];
- tags = _.reject(tags, function (tag) {
- return tag.id === tagToRemove.id || tag.name === tagToRemove.name;
- });
- this.set('tags', tags);
- },
- sync: function (method, model, options) {
- //wrap post in {posts: [{...}]}
- if (method === 'create' || method === 'update') {
- options.data = JSON.stringify({posts: [this.attributes]});
- options.contentType = 'application/json';
- options.url = model.url() + '?include=tags';
- }
-
- return Backbone.Model.prototype.sync.apply(this, arguments);
- }
- });
-
- Ghost.Collections.Posts = Backbone.Collection.extend({
- currentPage: 1,
- totalPages: 0,
- totalPosts: 0,
- nextPage: 0,
- prevPage: 0,
-
- url: Ghost.paths.apiRoot + '/posts/',
- model: Ghost.Models.Post,
-
- parse: function (resp) {
- if (_.isArray(resp.posts)) {
- this.limit = resp.meta.pagination.limit;
- this.currentPage = resp.meta.pagination.page;
- this.totalPages = resp.meta.pagination.pages;
- this.totalPosts = resp.meta.pagination.total;
- this.nextPage = resp.meta.pagination.next;
- this.prevPage = resp.meta.pagination.prev;
- return resp.posts;
- }
- return resp;
- }
- });
-
-}());
\ No newline at end of file
diff --git a/core/clientold/models/settings.js b/core/clientold/models/settings.js
deleted file mode 100644
index eeda242007..0000000000
--- a/core/clientold/models/settings.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/*global Backbone, Ghost, _ */
-(function () {
- 'use strict';
- //id:0 is used to issue PUT requests
- Ghost.Models.Settings = Ghost.ProgressModel.extend({
- url: Ghost.paths.apiRoot + '/settings/?type=blog,theme,app',
- id: '0',
-
- parse: function (response) {
- var result = _.reduce(response.settings, function (settings, setting) {
- settings[setting.key] = setting.value;
-
- return settings;
- }, {});
-
- return result;
- },
-
- sync: function (method, model, options) {
- var settings = _.map(this.attributes, function (value, key) {
- return { key: key, value: value };
- });
- //wrap settings in {settings: [{...}]}
- if (method === 'update') {
- options.data = JSON.stringify({settings: settings});
- options.contentType = 'application/json';
- }
-
- return Backbone.Model.prototype.sync.apply(this, arguments);
- }
- });
-
-}());
\ No newline at end of file
diff --git a/core/clientold/models/tag.js b/core/clientold/models/tag.js
deleted file mode 100644
index 9803905487..0000000000
--- a/core/clientold/models/tag.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/*global Ghost */
-(function () {
- 'use strict';
-
- Ghost.Collections.Tags = Ghost.ProgressCollection.extend({
- url: Ghost.paths.apiRoot + '/tags/',
-
- parse: function (resp) {
- return resp.tags;
- }
- });
-}());
diff --git a/core/clientold/models/themes.js b/core/clientold/models/themes.js
deleted file mode 100644
index 86f19c0c13..0000000000
--- a/core/clientold/models/themes.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/*global Ghost, Backbone */
-(function () {
- 'use strict';
-
- Ghost.Models.Themes = Backbone.Model.extend({
- url: Ghost.paths.apiRoot + '/themes/'
- });
-
-}());
diff --git a/core/clientold/models/uploadModal.js b/core/clientold/models/uploadModal.js
deleted file mode 100644
index 52fcec1a67..0000000000
--- a/core/clientold/models/uploadModal.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/*global Ghost, Backbone, $ */
-(function () {
- 'use strict';
- Ghost.Models.uploadModal = Backbone.Model.extend({
-
- options: {
- close: true,
- type: 'action',
- style: ["wide"],
- animation: 'fade',
- afterRender: function () {
- var filestorage = $('#' + this.options.model.id).data('filestorage');
- this.$('.js-drop-zone').upload({fileStorage: filestorage});
- },
- confirm: {
- reject: {
- func: function () { // The function called on rejection
- return true;
- },
- buttonClass: true,
- text: "Cancel" // The reject button text
- }
- }
- },
- content: {
- template: 'uploadImage'
- },
-
- initialize: function (options) {
- this.options.id = options.id;
- this.options.key = options.key;
- this.options.src = options.src;
- this.options.confirm.accept = options.accept;
- this.options.acceptEncoding = options.acceptEncoding || 'image/*';
- }
- });
-
-}());
diff --git a/core/clientold/models/user.js b/core/clientold/models/user.js
deleted file mode 100644
index a1c1927b6d..0000000000
--- a/core/clientold/models/user.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*global Ghost,Backbone */
-(function () {
- 'use strict';
-
- Ghost.Models.User = Ghost.ProgressModel.extend({
- url: Ghost.paths.apiRoot + '/users/me/',
-
- parse: function (resp) {
- // unwrap user from {users: [{...}]}
- if (resp.users) {
- resp = resp.users[0];
- }
-
- return resp;
- },
-
- sync: function (method, model, options) {
- // wrap user in {users: [{...}]}
- if (method === 'create' || method === 'update') {
- options.data = JSON.stringify({users: [this.attributes]});
- options.contentType = 'application/json';
- }
-
- return Backbone.Model.prototype.sync.apply(this, arguments);
- }
- });
-
-// Ghost.Collections.Users = Backbone.Collection.extend({
-// url: Ghost.paths.apiRoot + '/users/'
-// });
-
-}());
\ No newline at end of file
diff --git a/core/clientold/models/widget.js b/core/clientold/models/widget.js
deleted file mode 100644
index c9cc8f5c6c..0000000000
--- a/core/clientold/models/widget.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/*global Ghost */
-(function () {
- 'use strict';
-
- Ghost.Models.Widget = Ghost.ProgressModel.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 = Ghost.ProgressCollection.extend({
- // url: Ghost.paths.apiRoot + '/widgets/', // What will this be?
- model: Ghost.Models.Widget
- });
-
-}());
diff --git a/core/clientold/router.js b/core/clientold/router.js
deleted file mode 100644
index 26e2b5e591..0000000000
--- a/core/clientold/router.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/*global Ghost, Backbone, NProgress */
-(function () {
- "use strict";
-
- Ghost.Router = Backbone.Router.extend({
-
- routes: {
- '' : 'blog',
- 'content/' : 'blog',
- 'settings(/:pane)/' : 'settings',
- 'editor(/:id)/' : 'editor',
- 'debug/' : 'debug',
- 'register/' : 'register',
- 'signup/' : 'signup',
- 'signin/' : 'login',
- 'forgotten/' : 'forgotten',
- 'reset/:token/' : 'reset'
- },
-
- signup: function () {
- Ghost.currentView = new Ghost.Views.Signup({ el: '.js-signup-box' });
- },
-
- login: function () {
- Ghost.currentView = new Ghost.Views.Login({ el: '.js-login-box' });
- },
-
- forgotten: function () {
- Ghost.currentView = new Ghost.Views.Forgotten({ el: '.js-forgotten-box' });
- },
-
- reset: function (token) {
- Ghost.currentView = new Ghost.Views.ResetPassword({ el: '.js-reset-box', token: token });
- },
-
- blog: function () {
- var posts = new Ghost.Collections.Posts();
- NProgress.start();
- posts.fetch({ data: { status: 'all', staticPages: 'all', include: 'author'} }).then(function () {
- Ghost.currentView = new Ghost.Views.Blog({ el: '#main', collection: posts });
- NProgress.done();
- });
- },
-
- settings: function (pane) {
- if (!pane) {
- // Redirect to settings/general if no pane supplied
- this.navigate('/settings/general/', {
- trigger: true,
- replace: true
- });
- return;
- }
-
- // only update the currentView if we don't already have a Settings view
- if (!Ghost.currentView || !(Ghost.currentView instanceof Ghost.Views.Settings)) {
- Ghost.currentView = new Ghost.Views.Settings({ el: '#main', pane: pane });
- }
- },
-
- editor: function (id) {
- var post = new Ghost.Models.Post();
- post.urlRoot = Ghost.paths.apiRoot + '/posts';
- if (id) {
- post.id = id;
- post.fetch({ data: {status: 'all', include: 'tags'}}).then(function () {
- Ghost.currentView = new Ghost.Views.Editor({ el: '#main', model: post });
- });
- } else {
- Ghost.currentView = new Ghost.Views.Editor({ el: '#main', model: post });
- }
- },
-
- debug: function () {
- Ghost.currentView = new Ghost.Views.Debug({ el: "#main" });
- }
- });
-}());
\ No newline at end of file
diff --git a/core/clientold/toggle.js b/core/clientold/toggle.js
deleted file mode 100644
index 4efc7554eb..0000000000
--- a/core/clientold/toggle.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// # Toggle Support
-
-/*global document, $, Ghost */
-(function () {
- 'use strict';
-
- Ghost.temporary.hideToggles = function () {
- $('[data-toggle]').each(function () {
- var toggle = $(this).data('toggle');
- $(this).parent().children(toggle + ':visible').fadeOut(150);
- });
-
- // Toggle active classes on menu headers
- $('[data-toggle].active').removeClass('active');
- };
-
- Ghost.temporary.initToggles = function ($el) {
-
- $el.find('[data-toggle]').each(function () {
- var toggle = $(this).data('toggle');
- $(this).parent().children(toggle).hide();
- });
-
- $el.find('[data-toggle]').on('click', function (e) {
- e.preventDefault();
- e.stopPropagation();
- var $this = $(this),
- toggle = $this.data('toggle'),
- isAlreadyActive = $this.is('.active');
-
- // Close all the other open toggle menus
- Ghost.temporary.hideToggles();
-
- if (!isAlreadyActive) {
- $this.toggleClass('active');
- $(this).parent().children(toggle).toggleClass('open').fadeToggle(150);
- }
- });
-
- };
-
-
- $(document).ready(function () {
-
- // ## Toggle Up In Your Grill
- // Allows for toggling via data-attributes.
- // ### Usage
- //
- Ghost.temporary.initToggles($(document));
- });
-
-}());
diff --git a/core/clientold/tpl/forgotten.hbs b/core/clientold/tpl/forgotten.hbs
deleted file mode 100644
index 64f1508199..0000000000
--- a/core/clientold/tpl/forgotten.hbs
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/core/clientold/tpl/list-item.hbs b/core/clientold/tpl/list-item.hbs
deleted file mode 100644
index 304dbf8741..0000000000
--- a/core/clientold/tpl/list-item.hbs
+++ /dev/null
@@ -1,18 +0,0 @@
-
- {{{title}}}
-
-
- {{#if published}}
- {{#if page}}
- Page
- {{else}}
-
- {{/if}}
- {{else}}
- Draft
- {{/if}}
-
-
-
diff --git a/core/clientold/tpl/login.hbs b/core/clientold/tpl/login.hbs
deleted file mode 100644
index 3293741ea4..0000000000
--- a/core/clientold/tpl/login.hbs
+++ /dev/null
@@ -1,12 +0,0 @@
-
diff --git a/core/clientold/tpl/modal.hbs b/core/clientold/tpl/modal.hbs
deleted file mode 100644
index 7aefeb59fb..0000000000
--- a/core/clientold/tpl/modal.hbs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
- {{#if content.title}}{{/if}}
- {{#if options.close}}Close{{/if}}
-
- {{#if options.confirm}}
-
- {{/if}}
-
-
\ No newline at end of file
diff --git a/core/clientold/tpl/modals/blank.hbs b/core/clientold/tpl/modals/blank.hbs
deleted file mode 100644
index 9c6faeada0..0000000000
--- a/core/clientold/tpl/modals/blank.hbs
+++ /dev/null
@@ -1 +0,0 @@
-{{{content.text}}}
\ No newline at end of file
diff --git a/core/clientold/tpl/modals/copyToHTML.hbs b/core/clientold/tpl/modals/copyToHTML.hbs
deleted file mode 100644
index fc08540921..0000000000
--- a/core/clientold/tpl/modals/copyToHTML.hbs
+++ /dev/null
@@ -1,4 +0,0 @@
-Press Ctrl / Cmd + C to copy the following HTML.
-
-
-
\ No newline at end of file
diff --git a/core/clientold/tpl/modals/markdown.hbs b/core/clientold/tpl/modals/markdown.hbs
deleted file mode 100644
index 1d6383ea14..0000000000
--- a/core/clientold/tpl/modals/markdown.hbs
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
- Result |
- Markdown |
- Shortcut |
-
-
-
-
- Bold |
- **text** |
- Ctrl / Cmd + B |
-
-
- Emphasize |
- *text* |
- Ctrl / Cmd + I |
-
-
- Strike-through |
- ~~text~~ |
- Ctrl + Alt + U |
-
-
- Link |
- [title](http://) |
- Ctrl + Shift + L |
-
-
- Image |
- ![alt](http://) |
- Ctrl + Shift + I |
-
-
- List |
- * item |
- Ctrl + L |
-
-
- Blockquote |
- > quote |
- Ctrl + Q |
-
-
- H1 |
- # Heading |
- Ctrl + Alt + 1 |
-
-
- H2 |
- ## Heading |
- Ctrl + Alt + 2 |
-
-
- H3 |
- ### Heading |
- Ctrl + Alt + 3 |
-
-
- Inline Code |
- `code` |
- Cmd + K / Ctrl + Shift + K |
-
-
-
- For further Markdown syntax reference: Markdown Documentation
-
\ No newline at end of file
diff --git a/core/clientold/tpl/modals/uploadImage.hbs b/core/clientold/tpl/modals/uploadImage.hbs
deleted file mode 100644
index 54ff7a5cd2..0000000000
--- a/core/clientold/tpl/modals/uploadImage.hbs
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/core/clientold/tpl/notification.hbs b/core/clientold/tpl/notification.hbs
deleted file mode 100644
index af2733dac8..0000000000
--- a/core/clientold/tpl/notification.hbs
+++ /dev/null
@@ -1,4 +0,0 @@
-
- {{{message}}}
- Close
-
diff --git a/core/clientold/tpl/preview.hbs b/core/clientold/tpl/preview.hbs
deleted file mode 100644
index 430193dff8..0000000000
--- a/core/clientold/tpl/preview.hbs
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
- {{{title}}}
{{{html}}}
-
-{{#unless title}}
-
-
-
You Haven't Written Any Posts Yet!
-
-
-
-{{/unless}}
diff --git a/core/clientold/tpl/reset.hbs b/core/clientold/tpl/reset.hbs
deleted file mode 100644
index 242426b4c6..0000000000
--- a/core/clientold/tpl/reset.hbs
+++ /dev/null
@@ -1,9 +0,0 @@
-
diff --git a/core/clientold/tpl/settings/apps.hbs b/core/clientold/tpl/settings/apps.hbs
deleted file mode 100644
index e584081688..0000000000
--- a/core/clientold/tpl/settings/apps.hbs
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
- {{#each availableApps}}
- -
- {{#if package}}{{package.name}} - {{package.version}}{{else}}{{name}} - package.json missing :({{/if}}
-
-
- {{/each}}
-
-
\ No newline at end of file
diff --git a/core/clientold/tpl/settings/general.hbs b/core/clientold/tpl/settings/general.hbs
deleted file mode 100644
index 96a2eaf336..0000000000
--- a/core/clientold/tpl/settings/general.hbs
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
diff --git a/core/clientold/tpl/settings/sidebar.hbs b/core/clientold/tpl/settings/sidebar.hbs
deleted file mode 100644
index ad8295818b..0000000000
--- a/core/clientold/tpl/settings/sidebar.hbs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
\ No newline at end of file
diff --git a/core/clientold/tpl/settings/user-profile.hbs b/core/clientold/tpl/settings/user-profile.hbs
deleted file mode 100644
index 16af3ff369..0000000000
--- a/core/clientold/tpl/settings/user-profile.hbs
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
- Your Profile
-
-
-
-
diff --git a/core/clientold/tpl/signup.hbs b/core/clientold/tpl/signup.hbs
deleted file mode 100644
index 2543c663bf..0000000000
--- a/core/clientold/tpl/signup.hbs
+++ /dev/null
@@ -1,12 +0,0 @@
-
diff --git a/core/clientold/views/base.js b/core/clientold/views/base.js
deleted file mode 100644
index e7a19cb99b..0000000000
--- a/core/clientold/views/base.js
+++ /dev/null
@@ -1,407 +0,0 @@
-/*global window, document, setTimeout, Ghost, $, _, Backbone, JST, shortcut */
-(function () {
- "use strict";
-
- Ghost.TemplateView = Backbone.View.extend({
- templateName: "widget",
-
- template: function (data) {
- return JST[this.templateName](data);
- },
-
- templateData: function () {
- if (this.model) {
- return this.model.toJSON();
- }
-
- if (this.collection) {
- return this.collection.toJSON();
- }
-
- return {};
- },
-
- render: function () {
- if (_.isFunction(this.beforeRender)) {
- this.beforeRender();
- }
-
- this.$el.html(this.template(this.templateData()));
-
- if (_.isFunction(this.afterRender)) {
- this.afterRender();
- }
-
- return this;
- }
- });
-
- Ghost.View = Ghost.TemplateView.extend({
-
- // Adds a subview to the current view, which will
- // ensure its removal when this view is removed,
- // or when view.removeSubviews is called
- addSubview: function (view) {
- if (!(view instanceof Backbone.View)) {
- throw new Error("Subview must be a Backbone.View");
- }
- this.subviews = this.subviews || [];
- this.subviews.push(view);
- return view;
- },
-
- // Removes any subviews associated with this view
- // by `addSubview`, which will in-turn remove any
- // children of those views, and so on.
- removeSubviews: function () {
- var children = this.subviews;
-
- if (!children) {
- return this;
- }
-
- _(children).invoke("remove");
-
- this.subviews = [];
- return this;
- },
-
- // Extends the view's remove, by calling `removeSubviews`
- // if any subviews exist.
- remove: function () {
- if (this.subviews) {
- this.removeSubviews();
- }
- return Backbone.View.prototype.remove.apply(this, arguments);
- }
- });
-
- Ghost.Views.Utils = {
-
- // Used in API request fail handlers to parse a standard api error
- // response json for the message to display
- getRequestErrorMessage: function (request) {
- var message,
- msgDetail;
-
- // Can't really continue without a request
- if (!request) {
- return null;
- }
-
- // Seems like a sensible default
- message = request.statusText;
-
- // If a non 200 response
- if (request.status !== 200) {
- try {
- // Try to parse out the error, or default to "Unknown"
- if (request.responseJSON.errors && _.isArray(request.responseJSON.errors)) {
- message = '';
- _.each(request.responseJSON.errors, function (errorItem) {
- message += '
' + errorItem.message;
- });
- } else {
- message = request.responseJSON.error || "Unknown Error";
- }
- } catch (e) {
- msgDetail = request.status ? request.status + " - " + request.statusText : "Server was not available";
- message = "The server returned an error (" + msgDetail + ").";
- }
- }
-
- return message;
- },
-
- // Getting URL vars
- getUrlVariables: function () {
- var vars = [],
- hash,
- hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'),
- i;
-
- for (i = 0; i < hashes.length; i += 1) {
- hash = hashes[i].split('=');
- vars.push(hash[0]);
- vars[hash[0]] = hash[1];
- }
- return vars;
- }
- };
-
- /**
- * This is the view to generate the markup for the individual
- * notification. Will be included into #notifications.
- *
- * States can be
- * - persistent
- * - passive
- *
- * Types can be
- * - error
- * - success
- * - alert
- * - (empty)
- *
- */
- Ghost.Views.Notification = Ghost.View.extend({
- templateName: 'notification',
- className: 'js-bb-notification',
- template: function (data) {
- return JST[this.templateName](data);
- },
- render: function () {
- var html = this.template(this.model);
- this.$el.html(html);
- return this;
- }
- });
-
- /**
- * This handles Notification groups
- */
- Ghost.Views.NotificationCollection = Ghost.View.extend({
- el: '#notifications',
- initialize: function () {
- var self = this;
- this.render();
- Ghost.on('urlchange', function () {
- self.clearEverything();
- });
- shortcut.add("ESC", function () {
- // Make sure there isn't currently an open modal, as the escape key should close that first.
- // This is a temporary solution to enable closing extra-long notifications, and should be refactored
- // into something more robust in future
- if ($('.js-modal').length < 1) {
- self.clearEverything();
- }
- });
- },
- events: {
- 'animationend .js-notification': 'removeItem',
- 'webkitAnimationEnd .js-notification': 'removeItem',
- 'oanimationend .js-notification': 'removeItem',
- 'MSAnimationEnd .js-notification': 'removeItem',
- 'click .js-notification.notification-passive .close': 'closePassive',
- 'click .js-notification.notification-persistent .close': 'closePersistent'
- },
- render: function () {
- _.each(this.model, function (item) {
- this.renderItem(item);
- }, this);
- },
- renderItem: function (item) {
- var itemView = new Ghost.Views.Notification({ model: item }),
- height,
- $notification = $(itemView.render().el);
-
- this.$el.append($notification);
- height = $notification.hide().outerHeight(true);
- $notification.animate({height: height}, 250, function () {
- $(this)
- .css({height: "auto"})
- .fadeIn(250);
- });
- },
- addItem: function (item) {
- this.model.push(item);
- this.renderItem(item);
- },
- clearEverything: function () {
- this.$el.find('.js-notification.notification-passive').parent().remove();
- },
- removeItem: function (e) {
- e.preventDefault();
- var self = e.currentTarget,
- bbSelf = this;
- if (self.className.indexOf('notification-persistent') !== -1) {
- $.ajax({
- type: "DELETE",
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- url: Ghost.paths.apiRoot + '/notifications/' + $(self).find('.close').data('id')
- }).done(function (result) {
- /*jshint unused:false*/
- bbSelf.$el.slideUp(250, function () {
- $(this).show().css({height: "auto"});
- $(self).remove();
- });
- });
- } else {
- $(self).slideUp(250, function () {
- $(this)
- .show()
- .css({height: "auto"})
- .parent()
- .remove();
- });
- }
- },
- closePassive: function (e) {
- $(e.currentTarget)
- .parent()
- .fadeOut(250)
- .slideUp(250, function () {
- $(this).remove();
- });
- },
- closePersistent: function (e) {
- var self = e.currentTarget,
- bbSelf = this;
- $.ajax({
- type: "DELETE",
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- url: Ghost.paths.apiRoot + '/notifications/' + $(self).data('id')
- }).done(function (result) {
- /*jshint unused:false*/
- var height = bbSelf.$('.js-notification').outerHeight(true),
- $parent = $(self).parent();
- bbSelf.$el.css({height: height});
-
- if ($parent.parent().hasClass('js-bb-notification')) {
- $parent.parent().fadeOut(200, function () {
- $(this).remove();
- bbSelf.$el.slideUp(250, function () {
- $(this).show().css({height: "auto"});
- });
- });
- } else {
- $parent.fadeOut(200, function () {
- $(this).remove();
- bbSelf.$el.slideUp(250, function () {
- $(this).show().css({height: "auto"});
- });
- });
- }
- });
- }
- });
-
- // ## Modals
- Ghost.Views.Modal = Ghost.View.extend({
- el: '#modal-container',
- templateName: 'modal',
- className: 'js-bb-modal',
- // Render and manages modal dismissal
- initialize: function () {
- this.render();
- var self = this;
- if (this.model.options.close) {
- shortcut.add("ESC", function () {
- self.removeElement();
- });
- $(document).on('click', '.modal-background', function () {
- self.removeElement();
- });
- } else {
- shortcut.remove("ESC");
- $(document).off('click', '.modal-background');
- }
-
- if (this.model.options.confirm) {
- // Initiate functions for buttons here so models don't get tied up.
- this.acceptModal = function () {
- this.model.options.confirm.accept.func.call(this);
- self.removeElement();
- };
- this.rejectModal = function () {
- this.model.options.confirm.reject.func.call(this);
- self.removeElement();
- };
- }
- },
- templateData: function () {
- return this.model;
- },
- events: {
- 'click .close': 'removeElement',
- 'click .js-button-accept': 'acceptModal',
- 'click .js-button-reject': 'rejectModal'
- },
- afterRender: function () {
- this.$el.fadeIn(50);
- $(".modal-background").show(10, function () {
- $(this).addClass("in");
- });
- if (this.model.options.confirm) {
- this.$('.close').remove();
- }
- this.$(".modal-body").html(this.addSubview(new Ghost.Views.Modal.ContentView({model: this.model})).render().el);
-
-// if (document.body.style.webkitFilter !== undefined) { // Detect webkit filters
-// $("body").addClass("blur"); // Removed due to poor performance in Chrome
-// }
-
- if (_.isFunction(this.model.options.afterRender)) {
- this.model.options.afterRender.call(this);
- }
- if (this.model.options.animation) {
- this.animate(this.$el.children(".js-modal"));
- }
- },
- // #### remove
- // Removes Backbone attachments from modals
- remove: function () {
- this.undelegateEvents();
- this.$el.empty();
- this.stopListening();
- return this;
- },
- // #### removeElement
- // Visually removes the modal from the user interface
- removeElement: function (e) {
- if (e) {
- e.preventDefault();
- e.stopPropagation();
- }
-
- var self = this,
- $jsModal = $('.js-modal'),
- removeModalDelay = $jsModal.transitionDuration(),
- removeBackgroundDelay = self.$el.transitionDuration();
-
- $jsModal.removeClass('in');
-
- if (!this.model.options.animation) {
- removeModalDelay = removeBackgroundDelay = 0;
- }
-
- setTimeout(function () {
-
- if (document.body.style.filter !== undefined) {
- $("body").removeClass("blur");
- }
- $(".modal-background").removeClass('in');
-
- setTimeout(function () {
- self.remove();
- self.$el.hide();
- $(".modal-background").hide();
- }, removeBackgroundDelay);
- }, removeModalDelay);
-
- },
- // #### animate
- // Animates the animation
- animate: function (target) {
- setTimeout(function () {
- target.addClass('in');
- }, target.transitionDuration());
- }
- });
-
- // ## Modal Content
- Ghost.Views.Modal.ContentView = Ghost.View.extend({
-
- template: function (data) {
- return JST['modals/' + this.model.content.template](data);
- },
- templateData: function () {
- return this.model;
- }
-
- });
-}());
diff --git a/core/clientold/views/blog.js b/core/clientold/views/blog.js
deleted file mode 100644
index a3d62a6935..0000000000
--- a/core/clientold/views/blog.js
+++ /dev/null
@@ -1,284 +0,0 @@
-/*global window, Ghost, $, _, Backbone, NProgress */
-(function () {
- "use strict";
-
- var ContentList,
- ContentItem,
- PreviewContainer;
-
- // Base view
- // ----------
- Ghost.Views.Blog = Ghost.View.extend({
- initialize: function (options) {
- /*jshint unused:false*/
- var self = this,
- finishProgress = function () {
- NProgress.done();
- };
-
- // Basic collection request/sync flow progress bar handlers
- this.listenTo(this.collection, 'request', function () {
- NProgress.start();
- });
- this.listenTo(this.collection, 'sync', finishProgress);
-
- // A special case because models that are destroyed are removed from the
- // collection before the sync event fires and bubbles up
- this.listenTo(this.collection, 'destroy', function (model) {
- self.listenToOnce(model, 'sync', finishProgress);
- });
-
- this.addSubview(new PreviewContainer({ el: '.js-content-preview', collection: this.collection })).render();
- this.addSubview(new ContentList({ el: '.js-content-list', collection: this.collection })).render();
- }
- });
-
-
- // Content list (sidebar)
- // -----------------------
- ContentList = Ghost.View.extend({
-
- isLoading: false,
-
- events: {
- 'click .content-list-content' : 'scrollHandler'
- },
-
- initialize: function () {
- this.$('.content-list-content').scrollClass({target: '.content-list', offset: 10});
- this.listenTo(this.collection, 'remove', this.showNext);
- this.listenTo(this.collection, 'add', this.renderPost);
- // Can't use backbone event bind (see: http://stackoverflow.com/questions/13480843/backbone-scroll-event-not-firing)
- this.$('.content-list-content').scroll($.proxy(this.checkScroll, this));
- },
-
- showNext: function () {
- if (this.isLoading) { return; }
-
- if (!this.collection.length) {
- return Backbone.trigger('blog:activeItem', null);
- }
-
- var id = this.collection.at(0) ? this.collection.at(0).id : false;
- if (id) {
- Backbone.trigger('blog:activeItem', id);
- }
- },
-
- reportLoadError: function (response) {
- var message = 'A problem was encountered while loading more posts';
-
- if (response) {
- // Get message from response
- message += '; ' + Ghost.Views.Utils.getRequestErrorMessage(response);
- } else {
- message += '.';
- }
-
- Ghost.notifications.addItem({
- type: 'error',
- message: message,
- status: 'passive'
- });
- },
-
- checkScroll: function (event) {
- var self = this,
- element = event.target,
- triggerPoint = 100;
-
- // If we haven't passed our threshold, exit
- if (this.isLoading || (element.scrollTop + element.clientHeight + triggerPoint <= element.scrollHeight)) {
- return;
- }
-
- // If we've loaded the max number of pages, exit
- if (this.collection.currentPage >= this.collection.totalPages) {
- return;
- }
-
- // Load moar posts!
- this.isLoading = true;
- this.collection.fetch({
- update: true,
- remove: false,
- data: {
- status: 'all',
- page: (self.collection.currentPage + 1),
- staticPages: 'all'
- }
- }).then(function onSuccess(response) {
- /*jshint unused:false*/
- self.render();
- self.isLoading = false;
- }, function onError(e) {
- self.reportLoadError(e);
- });
- },
-
- renderPost: function (model) {
- this.$('ol').append(this.addSubview(new ContentItem({model: model})).render().el);
- },
-
- render: function () {
- var $list = this.$('ol');
-
- // Clear out any pre-existing subviews.
- this.removeSubviews();
-
- this.collection.each(function (model) {
- $list.append(this.addSubview(new ContentItem({model: model})).render().el);
- }, this);
- this.showNext();
- }
-
- });
-
- // Content Item
- // -----------------------
- ContentItem = Ghost.View.extend({
-
- tagName: 'li',
-
- events: {
- 'click a': 'setActiveItem'
- },
-
- active: false,
-
- initialize: function () {
- this.listenTo(Backbone, 'blog:activeItem', this.checkActive);
- this.listenTo(this.model, 'change:page change:featured', this.render);
- this.listenTo(this.model, 'destroy', this.removeItem);
- },
-
- removeItem: function () {
- var self = this;
- $.when(this.$el.slideUp()).then(function () {
- self.remove();
- });
- },
-
- // If the current item isn't active, we trigger the event to
- // notify a change in which item we're viewing.
- setActiveItem: function (e) {
- e.preventDefault();
- if (this.active !== true) {
- Backbone.trigger('blog:activeItem', this.model.id);
- this.render();
- }
- },
-
- // Checks whether this item is active and doesn't match the current id.
- checkActive: function (id) {
- if (this.model.id !== id) {
- if (this.active) {
- this.active = false;
- this.$el.removeClass('active');
- this.render();
- }
- } else {
- this.active = true;
- this.$el.addClass('active');
- }
- },
-
- showPreview: function (e) {
- var item = $(e.currentTarget);
- this.$('.content-list-content li').removeClass('active');
- item.addClass('active');
- Backbone.trigger('blog:activeItem', item.data('id'));
- },
-
- templateName: "list-item",
-
- templateData: function () {
- return _.extend({active: this.active}, this.model.toJSON());
- }
- });
-
- // Content preview
- // ----------------
- PreviewContainer = Ghost.View.extend({
-
- activeId: null,
-
- events: {
- 'click .post-controls .post-edit' : 'editPost',
- 'click .featured' : 'toggleFeatured',
- 'click .unfeatured' : 'toggleFeatured'
- },
-
- initialize: function () {
- this.listenTo(Backbone, 'blog:activeItem', this.setActivePreview);
- },
-
- setActivePreview: function (id) {
- if (this.activeId !== id) {
- this.activeId = id;
- this.render();
- }
- },
-
- editPost: function (e) {
- e.preventDefault();
- // for now this will disable "open in new tab", but when we have a Router implemented
- // it can go back to being a normal link to '#/ghost/editor/X'
- window.location = Ghost.paths.subdir + '/ghost/editor/' + this.model.get('id') + '/';
- },
-
- toggleFeatured: function (e) {
- e.preventDefault();
- var self = this,
- featured = !self.model.get('featured'),
- featuredEl = $(e.currentTarget),
- model = this.collection.get(this.activeId);
-
- model.save({
- featured: featured
- }, {
- success : function () {
- featuredEl.removeClass("featured unfeatured").addClass(featured ? "featured" : "unfeatured");
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'success',
- message: "Post successfully marked as " + (featured ? "featured" : "not featured") + ".",
- status: 'passive'
- });
- },
- error : function (model, xhr) {
- /*jshint unused:false*/
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- }
- });
- },
-
- templateName: "preview",
-
- render: function () {
- var self = this;
-
- this.model = this.collection.get(this.activeId);
- this.$el.html(this.template(this.templateData()));
-
- this.$('.content-preview-content').scrollClass({target: '.content-preview', offset: 10});
- this.$('.wrapper').on('click', 'a', function (e) {
- $(e.currentTarget).attr('target', '_blank');
- });
-
- if (this.model !== undefined) {
- this.model.fetch({ data: { status: 'all', include: 'tags,author' } }).then(function () {
- self.addSubview(new Ghost.View.PostSettings({ el: $('.post-controls'), model: self.model })).render();
- });
- }
-
- Ghost.temporary.initToggles(this.$el);
- return this;
- }
- });
-
-}());
diff --git a/core/clientold/views/debug.js b/core/clientold/views/debug.js
deleted file mode 100644
index bf4f63fbed..0000000000
--- a/core/clientold/views/debug.js
+++ /dev/null
@@ -1,189 +0,0 @@
-/*global Ghost, $ */
-(function () {
- "use strict";
-
- Ghost.Views.Debug = Ghost.View.extend({
- events: {
- "click .settings-menu a": "handleMenuClick",
- "click #startupload": "handleUploadClick",
- "click .js-delete": "handleDeleteClick",
- "click #sendtestmail": "handleSendTestMailClick"
- },
-
- initialize: function () {
- var view = this;
-
- this.uploadButton = this.$el.find('#startupload');
-
- // Disable import button and initizalize BlueImp file upload
- this.uploadButton.prop('disabled', 'disabled');
- $('#importfile').fileupload({
- url: Ghost.paths.apiRoot + '/db/',
- limitMultiFileUploads: 1,
- replaceFileInput: false,
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- dataType: 'json',
- add: function (e, data) {
- /*jshint unused:false*/
-
- // Bind the upload data to the view, so it is
- // available to the click handler, and enable the
- // upload button.
- view.fileUploadData = data;
- data.context = view.uploadButton.removeProp('disabled');
- },
- done: function (e, data) {
- /*jshint unused:false*/
- $('#startupload').text('Import');
- if (!data.result) {
- throw new Error('No response received from server.');
- }
- if (!data.result.message) {
- throw new Error('Unknown error');
- }
-
- Ghost.notifications.addItem({
- type: 'success',
- message: data.result.message,
- status: 'passive'
- });
- },
- error: function (response) {
- $('#startupload').text('Import');
- var responseJSON = response.responseJSON,
- message = responseJSON && responseJSON.errors[0].message ? responseJSON.errors[0].message : 'unknown';
- Ghost.notifications.addItem({
- type: 'error',
- message: ['A problem was encountered while importing new content to your blog. Error: ', message].join(''),
- status: 'passive'
- });
- }
-
- });
-
- },
-
- handleMenuClick: function (ev) {
- ev.preventDefault();
-
- var $target = $(ev.currentTarget);
-
- // Hide the current content
- this.$(".settings-content").hide();
-
- // Show the clicked content
- this.$("#debug-" + $target.attr("class")).show();
-
- return false;
- },
-
- handleUploadClick: function (ev) {
- ev.preventDefault();
-
- if (!this.uploadButton.prop('disabled')) {
- this.fileUploadData.context = this.uploadButton.text('Importing');
- this.fileUploadData.submit();
- }
-
- // Prevent double post by disabling the button.
- this.uploadButton.prop('disabled', 'disabled');
- },
-
- handleDeleteClick: function (ev) {
- ev.preventDefault();
- this.addSubview(new Ghost.Views.Modal({
- model: {
- options: {
- close: true,
- confirm: {
- accept: {
- func: function () {
- $.ajax({
- url: Ghost.paths.apiRoot + '/db/',
- type: 'DELETE',
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- success: function onSuccess(response) {
- if (!response) {
- throw new Error('No response received from server.');
- }
- if (!response.message) {
- throw new Error(response.detail || 'Unknown error');
- }
-
- Ghost.notifications.addItem({
- type: 'success',
- message: response.message,
- status: 'passive'
- });
-
- },
- error: function onError(response) {
- var responseText = JSON.parse(response.responseText),
- message = responseText && responseText.errors[0].message ? responseText.errors[0].message : 'unknown';
- Ghost.notifications.addItem({
- type: 'error',
- message: ['A problem was encountered while deleting content from your blog. Error: ', message].join(''),
- status: 'passive'
- });
-
- }
- });
- },
- text: "Delete",
- buttonClass: "button-delete"
- },
- reject: {
- func: function () {
- return true;
- },
- text: "Cancel",
- buttonClass: "button"
- }
- },
- type: "action",
- style: ["wide", "centered"],
- animation: 'fade'
- },
- content: {
- template: 'blank',
- title: 'Would you really like to delete all content from your blog?',
- text: 'This is permanent! No backups, no restores, no magic undo button.
We warned you, ok?
'
- }
- }
- }));
- },
-
- handleSendTestMailClick: function (ev) {
- ev.preventDefault();
-
- $.ajax({
- url: Ghost.paths.apiRoot + '/mail/test/',
- type: 'POST',
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- success: function onSuccess(response) {
- Ghost.notifications.addItem({
- type: 'success',
- message: ['Check your email for the test message: ', response.message].join(''),
- status: 'passive'
- });
- },
- error: function onError(response) {
- var responseText = JSON.parse(response.responseText),
- message = responseText && responseText.errors[0].message ? responseText.errors[0].message : 'unknown';
- Ghost.notifications.addItem({
- type: 'error',
- message: ['A problem was encountered while sending the test email: ', message].join(''),
- status: 'passive'
- });
-
- }
- });
- },
- });
-}());
diff --git a/core/clientold/views/editor-actions-widget.js b/core/clientold/views/editor-actions-widget.js
deleted file mode 100644
index 38fa4a27ef..0000000000
--- a/core/clientold/views/editor-actions-widget.js
+++ /dev/null
@@ -1,255 +0,0 @@
-// The Save / Publish button
-
-/*global $, _, Ghost, shortcut */
-
-(function () {
- 'use strict';
-
- // The Publish, Queue, Publish Now buttons
- // ----------------------------------------
- Ghost.View.EditorActionsWidget = Ghost.View.extend({
-
- events: {
- 'click [data-set-status]': 'handleStatus',
- 'click .js-publish-button': 'handlePostButton'
- },
-
- statusMap: null,
-
- createStatusMap: {
- 'draft': 'Save Draft',
- 'published': 'Publish Now'
- },
-
- updateStatusMap: {
- 'draft': 'Unpublish',
- 'published': 'Update Post'
- },
-
- //TODO: This has to be moved to the I18n localization file.
- //This structure is supposed to be close to the i18n-localization which will be used soon.
- messageMap: {
- errors: {
- post: {
- published: {
- 'published': 'Your post could not be updated.',
- 'draft': 'Your post could not be saved as a draft.'
- },
- draft: {
- 'published': 'Your post could not be published.',
- 'draft': 'Your post could not be saved as a draft.'
- }
-
- }
- },
-
- success: {
- post: {
- published: {
- 'published': 'Your post has been updated.',
- 'draft': 'Your post has been saved as a draft.'
- },
- draft: {
- 'published': 'Your post has been published.',
- 'draft': 'Your post has been saved as a draft.'
- }
- }
- }
- },
-
- initialize: function () {
- var self = this;
-
- // Toggle publish
- shortcut.add('Ctrl+Alt+P', function () {
- self.toggleStatus();
- });
- shortcut.add('Ctrl+S', function () {
- self.updatePost();
- });
- shortcut.add('Meta+S', function () {
- self.updatePost();
- });
- this.listenTo(this.model, 'change:status', this.render);
- },
-
- toggleStatus: function () {
- var self = this,
- keys = Object.keys(this.statusMap),
- model = self.model,
- prevStatus = model.get('status'),
- currentIndex = keys.indexOf(prevStatus),
- newIndex,
- status;
-
- newIndex = currentIndex + 1 > keys.length - 1 ? 0 : currentIndex + 1;
- status = keys[newIndex];
-
- this.setActiveStatus(keys[newIndex], this.statusMap[status], prevStatus);
-
- this.savePost({
- status: keys[newIndex]
- }).then(function () {
- self.reportSaveSuccess(status, prevStatus);
- }, function (xhr) {
- // Show a notification about the error
- self.reportSaveError(xhr, model, status, prevStatus);
- });
- },
-
- setActiveStatus: function (newStatus, displayText, currentStatus) {
- var isPublishing = (newStatus === 'published' && currentStatus !== 'published'),
- isUnpublishing = (newStatus === 'draft' && currentStatus === 'published'),
- // Controls when background of button has the splitbutton-delete/button-delete classes applied
- isImportantStatus = (isPublishing || isUnpublishing);
-
- $('.js-publish-splitbutton')
- .removeClass(isImportantStatus ? 'splitbutton-save' : 'splitbutton-delete')
- .addClass(isImportantStatus ? 'splitbutton-delete' : 'splitbutton-save');
-
- // Set the publish button's action and proper coloring
- $('.js-publish-button')
- .attr('data-status', newStatus)
- .text(displayText)
- .removeClass(isImportantStatus ? 'button-save' : 'button-delete')
- .addClass(isImportantStatus ? 'button-delete' : 'button-save');
-
- // Remove the animated popup arrow
- $('.js-publish-splitbutton > a')
- .removeClass('active');
-
- // Set the active action in the popup
- $('.js-publish-splitbutton .editor-options li')
- .removeClass('active')
- .filter(['li[data-set-status="', newStatus, '"]'].join(''))
- .addClass('active');
- },
-
- handleStatus: function (e) {
- if (e) { e.preventDefault(); }
- var status = $(e.currentTarget).attr('data-set-status'),
- currentStatus = this.model.get('status');
-
- this.setActiveStatus(status, this.statusMap[status], currentStatus);
-
- // Dismiss the popup menu
- $('body').find('.overlay:visible').fadeOut();
- },
-
- handlePostButton: function (e) {
- if (e) { e.preventDefault(); }
- var status = $(e.currentTarget).attr('data-status');
-
- this.updatePost(status);
- },
-
- updatePost: function (status) {
- var self = this,
- model = this.model,
- prevStatus = model.get('status');
-
- // Default to same status if not passed in
- status = status || prevStatus;
-
- model.trigger('willSave');
-
- this.savePost({
- status: status
- }).then(function () {
- self.reportSaveSuccess(status, prevStatus);
- // Refresh publish button and all relevant controls with updated status.
- self.render();
- }, function (xhr) {
- // Set the model status back to previous
- model.set({ status: prevStatus });
- // Set appropriate button status
- self.setActiveStatus(status, self.statusMap[status], prevStatus);
- // Show a notification about the error
- self.reportSaveError(xhr, model, status, prevStatus);
- });
- },
-
- savePost: function (data) {
- var publishButton = $('.js-publish-button'),
- saved,
- enablePublish = function (deferred) {
- deferred.always(function () {
- publishButton.prop('disabled', false);
- });
- return deferred;
- };
-
- publishButton.prop('disabled', true);
-
- _.each(this.model.blacklist, function (item) {
- this.model.unset(item);
- }, this);
-
- saved = this.model.save(_.extend({
- title: this.options.$title.val(),
- markdown: this.options.editor.value()
- }, data));
-
- // TODO: Take this out if #2489 gets merged in Backbone. Or patch Backbone
- // ourselves for more consistent promises.
- if (saved) {
- return enablePublish(saved);
- }
-
- return enablePublish($.Deferred().reject());
- },
-
- reportSaveSuccess: function (status, prevStatus) {
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'success',
- message: this.messageMap.success.post[prevStatus][status],
- status: 'passive'
- });
- this.options.editor.setDirty(false);
- },
-
- reportSaveError: function (response, model, status, prevStatus) {
- var message = this.messageMap.errors.post[prevStatus][status];
-
- if (response) {
- // Get message from response
- message += ' ' + Ghost.Views.Utils.getRequestErrorMessage(response);
- } else if (model.validationError) {
- // Grab a validation error
- message += ' ' + model.validationError;
- }
-
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'error',
- message: message,
- status: 'passive'
- });
- },
-
- setStatusLabels: function (statusMap) {
- _.each(statusMap, function (label, status) {
- $('li[data-set-status="' + status + '"] > a').text(label);
- });
- },
-
- render: function () {
- var status = this.model.get('status');
-
- // Assume that we're creating a new post
- if (status !== 'published') {
- this.statusMap = this.createStatusMap;
- } else {
- this.statusMap = this.updateStatusMap;
- }
-
- // Populate the publish menu with the appropriate verbiage
- this.setStatusLabels(this.statusMap);
-
- // Default the selected publish option to the current status of the post.
- this.setActiveStatus(status, this.statusMap[status], status);
- }
-
- });
-}());
\ No newline at end of file
diff --git a/core/clientold/views/editor-tag-widget.js b/core/clientold/views/editor-tag-widget.js
deleted file mode 100644
index a3a89ed829..0000000000
--- a/core/clientold/views/editor-tag-widget.js
+++ /dev/null
@@ -1,303 +0,0 @@
-// The Tag UI area associated with a post
-
-/*global window, document, setTimeout, $, _, Ghost */
-
-(function () {
- "use strict";
-
- Ghost.View.EditorTagWidget = Ghost.View.extend({
-
- events: {
- 'keyup [data-input-behaviour="tag"]': 'handleKeyup',
- 'keydown [data-input-behaviour="tag"]': 'handleKeydown',
- 'keypress [data-input-behaviour="tag"]': 'handleKeypress',
- 'click ul.suggestions li': 'handleSuggestionClick',
- 'click .tags .tag': 'handleTagClick',
- 'click .tag-label': 'mobileTags'
- },
-
- keys: {
- UP: 38,
- DOWN: 40,
- ESC: 27,
- ENTER: 13,
- BACKSPACE: 8
- },
-
- initialize: function () {
- var self = this,
- tagCollection = new Ghost.Collections.Tags();
-
- tagCollection.fetch().then(function () {
- self.allGhostTags = tagCollection.toJSON();
- });
-
- this.listenTo(this.model, 'willSave', this.completeCurrentTag, this);
- },
-
- render: function () {
- var tags = this.model.get('tags'),
- $tags = $('.tags'),
- tagOffset,
- self = this;
-
- $tags.empty();
-
- if (tags) {
- _.forEach(tags, function (tag) {
- var $tag = $('' + _.escape(tag.name) + '');
- $tags.append($tag);
- $("[data-tag-id=" + tag.id + "]")[0].scrollIntoView(true);
- });
- }
-
- this.$suggestions = $("ul.suggestions").hide(); // Initialise suggestions overlay
-
- if ($tags.length) {
- tagOffset = $('.tag-input').offset().left;
- $('.tag-blocks').css({'left': tagOffset + 'px'});
- }
-
- $(window).on('resize', self.resize).trigger('resize');
-
- $('.tag-label').on('touchstart', function () {
- $(this).addClass('touch');
- });
-
- return this;
- },
-
- mobileTags: function () {
- var mq = window.matchMedia("(max-width: 400px)"),
- publishBar = $("#publish-bar");
- if (mq.matches) {
-
- if (publishBar.hasClass("extended-tags")) {
- publishBar.css("top", "auto").animate({"height": "40px"}, 300, "swing", function () {
- $(this).removeClass("extended-tags");
- $(".tag-input").blur();
- });
- } else {
- publishBar.animate({"top": 0, "height": $(window).height()}, 300, "swing", function () {
- $(this).addClass("extended-tags");
- $(".tag-input").focus();
- });
- }
-
- $(".tag-input").one("blur", function () {
-
- if (publishBar.hasClass("extended-tags") && !$(':hover').last().hasClass("tag")) {
- publishBar.css("top", "auto").animate({"height": "40px"}, 300, "swing", function () {
- $(this).removeClass("extended-tags");
- $(document.activeElement).blur();
- document.documentElement.style.display = "none";
- setTimeout(function () { document.documentElement.style.display = 'block'; }, 0);
- });
- }
- });
-
- window.scrollTo(0, 1);
- }
- },
-
- showSuggestions: function ($target, _searchTerm) {
- var searchTerm = _searchTerm.toLowerCase(),
- matchingTags = this.findMatchingTags(searchTerm),
- styles = {
- left: $target.position().left
- },
- // Limit the suggestions number
- maxSuggestions = 5,
- // Escape regex special characters
- escapedTerm = searchTerm.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&'),
- regexTerm = escapedTerm.replace(/(\s+)/g, "(<[^>]+>)*$1(<[^>]+>)*"),
- regexPattern = new RegExp("(" + regexTerm + ")", "i");
-
- this.$suggestions.css(styles);
- this.$suggestions.html("");
-
- matchingTags = _.first(matchingTags, maxSuggestions);
- if (matchingTags.length > 0) {
- this.$suggestions.show();
- }
- _.each(matchingTags, function (matchingTag) {
- var highlightedName,
- suggestionHTML;
-
- highlightedName = matchingTag.name.replace(regexPattern, function (match, p1) {
- return "" + _.escape(p1) + "";
- });
- /*jslint regexp: true */ // - would like to remove this
- highlightedName = highlightedName.replace(/([^<>]*)((<[^>]+>)+)([^<>]*<\/mark>)/, function (match, p1, p2, p3, p4) {
- return _.escape(p1) + '' + _.escape(p2) + '' + _.escape(p4);
- });
-
- suggestionHTML = "" + highlightedName + "";
- this.$suggestions.append(suggestionHTML);
- }, this);
- },
-
- handleKeyup: function (e) {
- var $target = $(e.currentTarget),
- searchTerm = $.trim($target.val());
-
- if (e.keyCode === this.keys.UP) {
- e.preventDefault();
- if (this.$suggestions.is(":visible")) {
- if (this.$suggestions.children(".selected").length === 0) {
- this.$suggestions.find("li:last-child").addClass('selected');
- } else {
- this.$suggestions.children(".selected").removeClass('selected').prev().addClass('selected');
- }
- }
- } else if (e.keyCode === this.keys.DOWN) {
- e.preventDefault();
- if (this.$suggestions.is(":visible")) {
- if (this.$suggestions.children(".selected").length === 0) {
- this.$suggestions.find("li:first-child").addClass('selected');
- } else {
- this.$suggestions.children(".selected").removeClass('selected').next().addClass('selected');
- }
- }
- } else if (e.keyCode === this.keys.ESC) {
- this.$suggestions.hide();
- } else {
- if (searchTerm) {
- this.showSuggestions($target, searchTerm);
- } else {
- this.$suggestions.hide();
- }
- }
-
- if (e.keyCode === this.keys.UP || e.keyCode === this.keys.DOWN) {
- return false;
- }
- },
-
- handleKeydown: function (e) {
- var $target = $(e.currentTarget),
- lastBlock,
- tag;
- // Delete character tiggers on Keydown, so needed to check on that event rather than Keyup.
- if (e.keyCode === this.keys.BACKSPACE && !$target.val()) {
- lastBlock = this.$('.tags').find('.tag').last();
- lastBlock.remove();
- tag = {id: lastBlock.data('tag-id'), name: lastBlock.text()};
- this.model.removeTag(tag);
- }
- },
-
- handleKeypress: function (e) {
- var $target = $(e.currentTarget),
- searchTerm = $.trim($target.val()),
- tag,
- $selectedSuggestion,
- isComma = ",".localeCompare(String.fromCharCode(e.keyCode || e.charCode)) === 0,
- hasAlreadyBeenAdded;
-
- // use localeCompare in case of international keyboard layout
- if ((e.keyCode === this.keys.ENTER || isComma) && searchTerm) {
- // Submit tag using enter or comma key
- e.preventDefault();
-
- $selectedSuggestion = this.$suggestions.children(".selected");
- if (this.$suggestions.is(":visible") && $selectedSuggestion.length !== 0) {
- tag = {id: $selectedSuggestion.data('tag-id'), name: _.unescape($selectedSuggestion.data('tag-name'))};
- hasAlreadyBeenAdded = this.hasTagBeenAdded(tag.name);
- if (!hasAlreadyBeenAdded) {
- this.addTag(tag);
- }
- } else {
- if (isComma) {
- // Remove comma from string if comma is used to submit.
- searchTerm = searchTerm.replace(/,/g, "");
- }
-
- hasAlreadyBeenAdded = this.hasTagBeenAdded(searchTerm);
- if (!hasAlreadyBeenAdded) {
- this.addTag({id: null, name: searchTerm});
- }
- }
- $target.val('').focus();
- searchTerm = ""; // Used to reset search term
- this.$suggestions.hide();
- }
- },
-
- completeCurrentTag: function () {
- var $target = this.$('.tag-input'),
- tagName = $target.val(),
- hasAlreadyBeenAdded;
-
- hasAlreadyBeenAdded = this.hasTagBeenAdded(tagName);
-
- if (tagName.length > 0 && !hasAlreadyBeenAdded) {
- this.addTag({id: null, name: tagName});
- }
- },
-
- handleSuggestionClick: function (e) {
- var $target = $(e.currentTarget);
- if (e) { e.preventDefault(); }
- this.addTag({id: $target.data('tag-id'), name: _.unescape($target.data('tag-name'))});
- },
-
- handleTagClick: function (e) {
- var $tag = $(e.currentTarget),
- tag = {id: $tag.data('tag-id'), name: $tag.text()};
- $tag.remove();
- window.scrollTo(0, 1);
- this.model.removeTag(tag);
- },
-
- resize: _.throttle(function () {
- var $tags = $('.tags');
- if ($(window).width() > 400) {
- $tags.css("max-width", $("#entry-tags").width() - 320);
- } else {
- $tags.css("max-width", "inherit");
- }
- }, 50),
-
- findMatchingTags: function (searchTerm) {
- var matchingTagModels,
- self = this;
-
- if (!this.allGhostTags) {
- return [];
- }
-
- searchTerm = searchTerm.toUpperCase();
- matchingTagModels = _.filter(this.allGhostTags, function (tag) {
- var tagNameMatches,
- hasAlreadyBeenAdded;
-
- tagNameMatches = tag.name.toUpperCase().indexOf(searchTerm) !== -1;
-
- hasAlreadyBeenAdded = self.hasTagBeenAdded(tag.name);
-
- return tagNameMatches && !hasAlreadyBeenAdded;
- });
-
- return matchingTagModels;
- },
-
- addTag: function (tag) {
- var $tag = $('' + _.escape(tag.name) + '');
- this.$('.tags').append($tag);
- $(".tag").last()[0].scrollIntoView(true);
- window.scrollTo(0, 1);
- this.model.addTag(tag);
-
- this.$('.tag-input').val('').focus();
- this.$suggestions.hide();
- },
-
- hasTagBeenAdded: function (tagName) {
- return _.some(this.model.get('tags'), function (usedTag) {
- return tagName.toUpperCase() === usedTag.name.toUpperCase();
- });
- }
- });
-
-}());
diff --git a/core/clientold/views/editor.js b/core/clientold/views/editor.js
deleted file mode 100644
index bf8a8b98d9..0000000000
--- a/core/clientold/views/editor.js
+++ /dev/null
@@ -1,158 +0,0 @@
-// # Article Editor
-
-/*global document, setTimeout, navigator, $, Backbone, Ghost, shortcut */
-(function () {
- 'use strict';
-
- var PublishBar;
-
- // The publish bar associated with a post, which has the TagWidget and
- // Save button and options and such.
- // ----------------------------------------
- PublishBar = Ghost.View.extend({
-
- initialize: function () {
-
- this.addSubview(new Ghost.View.EditorTagWidget(
- {el: this.$('#entry-tags'), model: this.model}
- )).render();
- this.addSubview(new Ghost.View.PostSettings(
- {el: $('#entry-controls'), model: this.model}
- )).render();
-
- // Pass the Actions widget references to the title and editor so that it can get
- // the values that need to be saved
- this.addSubview(new Ghost.View.EditorActionsWidget(
- {
- el: this.$('#entry-actions'),
- model: this.model,
- $title: this.options.$title,
- editor: this.options.editor
- }
- )).render();
-
- },
-
- render: function () { return this; }
- });
-
-
- // The entire /editor page's route
- // ----------------------------------------
- Ghost.Views.Editor = Ghost.View.extend({
-
- events: {
- 'click .markdown-help': 'showHelp',
- 'blur #entry-title': 'trimTitle',
- 'orientationchange': 'orientationChange'
- },
-
- initialize: function () {
- this.$title = this.$('#entry-title');
- this.$editor = this.$('#entry-markdown');
-
- this.$title.val(this.model.get('title')).focus();
- this.$editor.text(this.model.get('markdown'));
-
- // Create a new editor
- this.editor = new Ghost.Editor.Main();
-
- // Add the container view for the Publish Bar
- // Passing reference to the title and editor
- this.addSubview(new PublishBar(
- {el: '#publish-bar', model: this.model, $title: this.$title, editor: this.editor}
- )).render();
-
- this.listenTo(this.model, 'change:title', this.renderTitle);
- this.listenTo(this.model, 'change:id', this.handleIdChange);
-
- this.bindShortcuts();
-
- $('.entry-markdown header, .entry-preview header').on('click', function (e) {
- $('.entry-markdown, .entry-preview').removeClass('active');
- $(e.currentTarget).closest('section').addClass('active');
- });
- },
-
- bindShortcuts: function () {
- var self = this;
-
- // Zen writing mode shortcut - full editor view
- shortcut.add('Alt+Shift+Z', function () {
- $('body').toggleClass('zen');
- });
-
- // HTML copy & paste
- shortcut.add('Ctrl+Alt+C', function () {
- self.showHTML();
- });
- },
-
- trimTitle: function () {
- var rawTitle = this.$title.val(),
- trimmedTitle = $.trim(rawTitle);
-
- if (rawTitle !== trimmedTitle) {
- this.$title.val(trimmedTitle);
- }
-
- // Trigger title change for post-settings.js
- this.model.set('title', trimmedTitle);
- },
-
- renderTitle: function () {
- this.$title.val(this.model.get('title'));
- },
-
- handleIdChange: function (m) {
- // This is a special case for browsers which fire an unload event when using navigate. The id change
- // happens before the save success and can cause the unload alert to appear incorrectly on first save
- // The id only changes in the event that the save has been successful, so this workaround is safes
- this.editor.setDirty(false);
- Backbone.history.navigate('/editor/' + m.id + '/');
- },
-
- // This is a hack to remove iOS6 white space on orientation change bug
- // See: http://cl.ly/RGx9
- orientationChange: function () {
- if (/iPhone/.test(navigator.userAgent) && !/Opera Mini/.test(navigator.userAgent)) {
- var focusedElement = document.activeElement,
- s = document.documentElement.style;
- focusedElement.blur();
- s.display = 'none';
- setTimeout(function () { s.display = 'block'; focusedElement.focus(); }, 0);
- }
- },
-
- showEditorModal: function (content) {
- this.addSubview(new Ghost.Views.Modal({
- model: {
- options: {
- close: true,
- style: ['wide'],
- animation: 'fade'
- },
- content: content
- }
- }));
- },
-
- showHelp: function () {
- var content = {
- template: 'markdown',
- title: 'Markdown Help'
- };
- this.showEditorModal(content);
- },
-
- showHTML: function () {
- var content = {
- template: 'copyToHTML',
- title: 'Copied HTML'
- };
- this.showEditorModal(content);
- },
-
- render: function () { return this; }
- });
-}());
\ No newline at end of file
diff --git a/core/clientold/views/login.js b/core/clientold/views/login.js
deleted file mode 100644
index 24e5f9cc77..0000000000
--- a/core/clientold/views/login.js
+++ /dev/null
@@ -1,276 +0,0 @@
-/*global window, Ghost, $, validator */
-(function () {
- "use strict";
-
- Ghost.Views.Login = Ghost.View.extend({
-
- initialize: function () {
- this.render();
- },
-
- templateName: "login",
-
- events: {
- 'submit #login': 'submitHandler'
- },
-
- afterRender: function () {
- var self = this;
- this.$el.css({"opacity": 0}).animate({"opacity": 1}, 500, function () {
- self.$("[name='email']").focus();
- });
- },
-
- submitHandler: function (event) {
- event.preventDefault();
- var email = this.$el.find('.email').val(),
- password = this.$el.find('.password').val(),
- redirect = Ghost.Views.Utils.getUrlVariables().r,
- validationErrors = [];
-
- if (!validator.isEmail(email)) {
- validationErrors.push("Invalid Email");
- }
-
- if (!validator.isLength(password, 0)) {
- validationErrors.push("Please enter a password");
- }
-
- if (validationErrors.length) {
- validator.handleErrors(validationErrors);
- } else {
- $.ajax({
- url: Ghost.paths.subdir + '/ghost/signin/',
- type: 'POST',
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- data: {
- email: email,
- password: password,
- redirect: redirect
- },
- success: function (msg) {
- window.location.href = msg.redirect;
- },
- error: function (xhr) {
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- }
- });
- }
- }
- });
-
- Ghost.Views.Signup = Ghost.View.extend({
-
- initialize: function () {
- this.submitted = "no";
- this.render();
- },
-
- templateName: "signup",
-
- events: {
- 'submit #signup': 'submitHandler'
- },
-
- afterRender: function () {
- var self = this;
-
- this.$el
- .css({"opacity": 0})
- .animate({"opacity": 1}, 500, function () {
- self.$("[name='name']").focus();
- });
- },
-
- submitHandler: function (event) {
- event.preventDefault();
- var name = this.$('.name').val(),
- email = this.$('.email').val(),
- password = this.$('.password').val(),
- validationErrors = [],
- self = this;
-
- if (!validator.isLength(name, 1)) {
- validationErrors.push("Please enter a name.");
- }
-
- if (!validator.isEmail(email)) {
- validationErrors.push("Please enter a correct email address.");
- }
-
- if (!validator.isLength(password, 0)) {
- validationErrors.push("Please enter a password");
- }
-
- if (!validator.equals(this.submitted, "no")) {
- validationErrors.push("Ghost is signing you up. Please wait...");
- }
-
- if (validationErrors.length) {
- validator.handleErrors(validationErrors);
- } else {
- this.submitted = "yes";
- $.ajax({
- url: Ghost.paths.subdir + '/ghost/signup/',
- type: 'POST',
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- data: {
- name: name,
- email: email,
- password: password
- },
- success: function (msg) {
- window.location.href = msg.redirect;
- },
- error: function (xhr) {
- self.submitted = "no";
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- }
- });
- }
- }
- });
-
- Ghost.Views.Forgotten = Ghost.View.extend({
-
- initialize: function () {
- this.render();
- },
-
- templateName: "forgotten",
-
- events: {
- 'submit #forgotten': 'submitHandler'
- },
-
- afterRender: function () {
- var self = this;
- this.$el.css({"opacity": 0}).animate({"opacity": 1}, 500, function () {
- self.$("[name='email']").focus();
- });
- },
-
- submitHandler: function (event) {
- event.preventDefault();
-
- var email = this.$el.find('.email').val(),
- validationErrors = [];
-
- if (!validator.isEmail(email)) {
- validationErrors.push("Please enter a correct email address.");
- }
-
- if (validationErrors.length) {
- validator.handleErrors(validationErrors);
- } else {
- $.ajax({
- url: Ghost.paths.subdir + '/ghost/forgotten/',
- type: 'POST',
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- data: {
- email: email
- },
- success: function (msg) {
-
- window.location.href = msg.redirect;
- },
- error: function (xhr) {
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- }
- });
- }
- }
- });
-
- Ghost.Views.ResetPassword = Ghost.View.extend({
- templateName: 'reset',
-
- events: {
- 'submit #reset': 'submitHandler'
- },
-
- initialize: function (attrs) {
- attrs = attrs || {};
-
- this.token = attrs.token;
-
- this.render();
- },
-
- afterRender: function () {
- var self = this;
- this.$el.css({"opacity": 0}).animate({"opacity": 1}, 500, function () {
- self.$("[name='newpassword']").focus();
- });
- },
-
- submitHandler: function (ev) {
- ev.preventDefault();
-
- var self = this,
- newPassword = this.$('input[name="newpassword"]').val(),
- ne2Password = this.$('input[name="ne2password"]').val();
-
- if (newPassword !== ne2Password) {
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'error',
- message: "Your passwords do not match.",
- status: 'passive'
- });
-
- return;
- }
-
- this.$('input, button').prop('disabled', true);
-
- $.ajax({
- url: Ghost.paths.subdir + '/ghost/reset/' + this.token + '/',
- type: 'POST',
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- data: {
- newpassword: newPassword,
- ne2password: ne2Password
- },
- success: function (msg) {
- window.location.href = msg.redirect;
- },
- error: function (xhr) {
- self.$('input, button').prop('disabled', false);
-
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- }
- });
-
- return false;
- }
- });
-}());
diff --git a/core/clientold/views/post-settings.js b/core/clientold/views/post-settings.js
deleted file mode 100644
index 7cd6130ab8..0000000000
--- a/core/clientold/views/post-settings.js
+++ /dev/null
@@ -1,359 +0,0 @@
-// The Post Settings Menu available in the content preview screen, as well as the post editor.
-
-/*global window, $, _, Ghost, moment */
-
-(function () {
- "use strict";
-
- var parseDateFormats = ["DD MMM YY HH:mm", "DD MMM YYYY HH:mm", "DD/MM/YY HH:mm", "DD/MM/YYYY HH:mm",
- "DD-MM-YY HH:mm", "DD-MM-YYYY HH:mm", "YYYY-MM-DD HH:mm"],
- displayDateFormat = 'DD MMM YY @ HH:mm';
-
- Ghost.View.PostSettings = Ghost.View.extend({
-
- events: {
- 'blur .post-setting-slug' : 'editSlug',
- 'click .post-setting-slug' : 'selectSlug',
- 'blur .post-setting-date' : 'editDate',
- 'click .post-setting-static-page' : 'toggleStaticPage',
- 'click .delete' : 'deletePost'
- },
-
- initialize: function () {
- if (this.model) {
- // These three items can be updated outside of the post settings menu, so have to be listened to.
- this.listenTo(this.model, 'change:id', this.render);
- this.listenTo(this.model, 'change:title', this.updateSlugPlaceholder);
- this.listenTo(this.model, 'change:published_at', this.updatePublishedDate);
- }
- },
-
- render: function () {
- var slug = this.model ? this.model.get('slug') : '',
- pubDate = this.model ? this.model.get('published_at') : 'Not Published',
- $pubDateEl = this.$('.post-setting-date'),
- $postSettingSlugEl = this.$('.post-setting-slug');
-
- $postSettingSlugEl.val(slug);
-
- // Update page status test if already a page.
- if (this.model && this.model.get('page')) {
- $('.post-setting-static-page').prop('checked', this.model.get('page'));
- }
-
- // Insert the published date, and make it editable if it exists.
- if (this.model && this.model.get('published_at')) {
- pubDate = moment(pubDate).format(displayDateFormat);
- $pubDateEl.attr('placeholder', '');
- } else {
- $pubDateEl.attr('placeholder', moment().format(displayDateFormat));
- }
-
- if (this.model && this.model.get('id')) {
- this.$('.post-setting-page').removeClass('hidden');
- this.$('.delete').removeClass('hidden');
- }
-
- // Apply different style for model's that aren't
- // yet persisted to the server.
- // Mostly we're hiding the delete post UI
- if (this.model.id === undefined) {
- this.$el.addClass('unsaved');
- } else {
- this.$el.removeClass('unsaved');
- }
-
- $pubDateEl.val(pubDate);
- },
-
- // Requests a new slug when the title was changed
- updateSlugPlaceholder: function () {
- var title = this.model.get('title'),
- $postSettingSlugEl = this.$('.post-setting-slug');
-
- // If there's a title present we want to
- // validate it against existing slugs in the db
- // and then update the placeholder value.
- if (title) {
- $.ajax({
- url: Ghost.paths.apiRoot + '/slugs/post/' + encodeURIComponent(title) + '/',
- success: function (result) {
- $postSettingSlugEl.attr('placeholder', result);
- }
- });
- } else {
- // If there's no title set placeholder to blank
- // and don't make an ajax request to server
- // for a proper slug (as there won't be any).
- $postSettingSlugEl.attr('placeholder', '');
- return;
- }
- },
-
- selectSlug: function (e) {
- e.currentTarget.select();
- },
-
- editSlug: _.debounce(function (e) {
- e.preventDefault();
- var self = this,
- slug = self.model.get('slug'),
- slugEl = e.currentTarget,
- newSlug = slugEl.value,
- placeholder = slugEl.placeholder;
-
- newSlug = (_.isEmpty(newSlug) && placeholder) ? placeholder : newSlug;
-
- // If the model doesn't currently
- // exist on the server (aka has no id)
- // then just update the model's value
- if (self.model.id === undefined) {
- this.model.set({
- slug: newSlug
- });
- return;
- }
-
- // Ignore unchanged slugs
- if (slug === newSlug) {
- slugEl.value = slug === undefined ? '' : slug;
- return;
- }
-
- this.model.save({
- slug: newSlug
- }, {
- success : function (model, response, options) {
- /*jshint unused:false*/
- // Repopulate slug in case it changed on the server (e.g. 'new-slug-2')
- slugEl.value = model.get('slug');
- Ghost.notifications.addItem({
- type: 'success',
- message: "Permalink successfully changed to " + model.get('slug') + '.',
- status: 'passive'
- });
- },
- error : function (model, xhr) {
- /*jshint unused:false*/
- slugEl.value = model.previous('slug');
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- }
- });
- }, 500),
-
-
- updatePublishedDate: function () {
- var pubDate = this.model.get('published_at') ? moment(this.model.get('published_at'))
- .format(displayDateFormat) : '',
- $pubDateEl = this.$('.post-setting-date');
-
- // Only change the date if it's different
- if (pubDate && $pubDateEl.val() !== pubDate) {
- $pubDateEl.val(pubDate);
- }
- },
-
- editDate: _.debounce(function (e) {
- e.preventDefault();
- var self = this,
- errMessage = '',
- pubDate = self.model.get('published_at') ? moment(self.model.get('published_at'))
- .format(displayDateFormat) : '',
- pubDateEl = e.currentTarget,
- newPubDate = pubDateEl.value,
- pubDateMoment,
- newPubDateMoment;
-
- // if there is no new pub date do nothing
- if (!newPubDate) {
- return;
- }
-
- // Check for missing time stamp on new data
- // If no time specified, add a 12:00
- if (newPubDate && !newPubDate.slice(-5).match(/\d+:\d\d/)) {
- newPubDate += " 12:00";
- }
-
- newPubDateMoment = moment(newPubDate, parseDateFormats);
-
- // If there was a published date already set
- if (pubDate) {
- // Check for missing time stamp on current model
- // If no time specified, add a 12:00
- if (!pubDate.slice(-5).match(/\d+:\d\d/)) {
- pubDate += " 12:00";
- }
-
- pubDateMoment = moment(pubDate, parseDateFormats);
-
- // Ensure the published date has changed
- if (newPubDate.length === 0 || pubDateMoment.isSame(newPubDateMoment)) {
- // If it wasn't, reset it and return
- pubDateEl.value = pubDateMoment.format(displayDateFormat);
- return;
- }
- }
-
- // Validate new Published date
- if (!newPubDateMoment.isValid()) {
- errMessage = 'Published Date must be a valid date with format: DD MMM YY @ HH:mm (e.g. 6 Dec 14 @ 15:00)';
- }
-
- if (newPubDateMoment.diff(new Date(), 'h') > 0) {
- errMessage = 'Published Date cannot currently be in the future.';
- }
-
- if (errMessage.length) {
- // Show error message
- Ghost.notifications.addItem({
- type: 'error',
- message: errMessage,
- status: 'passive'
- });
-
- // Reset back to original value and return
- pubDateEl.value = pubDateMoment ? pubDateMoment.format(displayDateFormat) : '';
- return;
- }
-
- // If the model doesn't currently
- // exist on the server (aka has no id)
- // then just update the model's value
- if (self.model.id === undefined) {
- this.model.set({
- published_at: newPubDateMoment.toDate()
- });
- return;
- }
-
- // Save new 'Published' date
- this.model.save({
- published_at: newPubDateMoment.toDate()
- }, {
- success : function (model) {
- pubDateEl.value = moment(model.get('published_at')).format(displayDateFormat);
- Ghost.notifications.addItem({
- type: 'success',
- message: 'Publish date successfully changed to ' + pubDateEl.value + '.',
- status: 'passive'
- });
- },
- error : function (model, xhr) {
- /*jshint unused:false*/
- // Reset back to original value
- pubDateEl.value = pubDateMoment ? pubDateMoment.format(displayDateFormat) : '';
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- }
- });
-
- }, 500),
-
- toggleStaticPage: _.debounce(function (e) {
- var pageEl = $(e.currentTarget),
- page = pageEl.prop('checked');
-
- // Don't try to save
- // if the model doesn't currently
- // exist on the server
- if (this.model.id === undefined) {
- this.model.set({
- page: page
- });
- return;
- }
-
- this.model.save({
- page: page
- }, {
- success : function (model, response, options) {
- /*jshint unused:false*/
- pageEl.prop('checked', page);
- Ghost.notifications.addItem({
- type: 'success',
- message: "Successfully converted " + (page ? "to static page" : "to post") + '.',
- status: 'passive'
- });
- },
- error : function (model, xhr) {
- /*jshint unused:false*/
- pageEl.prop('checked', model.previous('page'));
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- }
- });
- }, 500),
-
- deletePost: function (e) {
- e.preventDefault();
- var self = this;
- // You can't delete a post
- // that hasn't yet been saved
- if (this.model.id === undefined) {
- return;
- }
- this.addSubview(new Ghost.Views.Modal({
- model: {
- options: {
- close: false,
- confirm: {
- accept: {
- func: function () {
- self.model.destroy({
- wait: true
- }).then(function () {
- // Redirect to content screen if deleting post from editor.
- if (window.location.pathname.indexOf('editor') > -1) {
- window.location = Ghost.paths.subdir + '/ghost/content/';
- }
- Ghost.notifications.addItem({
- type: 'success',
- message: 'Your post has been deleted.',
- status: 'passive'
- });
- }, function () {
- Ghost.notifications.addItem({
- type: 'error',
- message: 'Your post could not be deleted. Please try again.',
- status: 'passive'
- });
- });
- },
- text: "Delete",
- buttonClass: "button-delete"
- },
- reject: {
- func: function () {
- return true;
- },
- text: "Cancel",
- buttonClass: "button"
- }
- },
- type: "action",
- style: ["wide", "centered"],
- animation: 'fade'
- },
- content: {
- template: 'blank',
- title: 'Are you sure you want to delete this post?',
- text: 'This is permanent! No backups, no restores, no magic undo button.
We warned you, ok?
'
- }
- }
- }));
- }
-
- });
-
-}());
diff --git a/core/clientold/views/settings.js b/core/clientold/views/settings.js
deleted file mode 100644
index e4c190f2dd..0000000000
--- a/core/clientold/views/settings.js
+++ /dev/null
@@ -1,521 +0,0 @@
-/*global document, Ghost, $, _, Countable, validator */
-(function () {
- "use strict";
-
- var Settings = {};
-
- // Base view
- // ----------
- Ghost.Views.Settings = Ghost.View.extend({
- initialize: function (options) {
- $(".settings-content").removeClass('active');
-
- this.sidebar = new Settings.Sidebar({
- el: '.settings-sidebar',
- pane: options.pane,
- model: this.model
- });
-
- this.addSubview(this.sidebar);
-
- this.listenTo(Ghost.router, 'route:settings', this.changePane);
- },
-
- changePane: function (pane) {
- if (!pane) {
- // Can happen when trying to load /settings with no pane specified
- // let the router navigate itself to /settings/general
- return;
- }
-
- this.sidebar.showContent(pane);
- }
- });
-
- // Sidebar (tabs)
- // ---------------
- Settings.Sidebar = Ghost.View.extend({
- initialize: function (options) {
- this.render();
- this.menu = this.$('.settings-menu');
- // Hides apps UI unless config.js says otherwise
- // This will stay until apps UI is ready to ship
- if ($(this.el).attr('data-apps') !== "true") {
- this.menu.find('.apps').hide();
- }
- this.showContent(options.pane);
- },
-
- models: {},
-
- events: {
- 'click .settings-menu li' : 'switchPane'
- },
-
- switchPane: function (e) {
- e.preventDefault();
- var item = $(e.currentTarget),
- id = item.find('a').attr('href').substring(1);
-
- this.showContent(id);
- },
-
- showContent: function (id) {
- var self = this,
- model;
-
- Ghost.router.navigate('/settings/' + id + '/');
- Ghost.trigger('urlchange');
- if (this.pane && id === this.pane.id) {
- return;
- }
- _.result(this.pane, 'destroy');
- this.setActive(id);
- this.pane = new Settings[id]({ el: '.settings-content'});
-
- if (!this.models.hasOwnProperty(this.pane.options.modelType)) {
- model = this.models[this.pane.options.modelType] = new Ghost.Models[this.pane.options.modelType]();
- model.fetch().then(function () {
- self.renderPane(model);
- });
- } else {
- model = this.models[this.pane.options.modelType];
- self.renderPane(model);
- }
- },
-
- renderPane: function (model) {
- this.pane.model = model;
- this.pane.render();
- },
-
- setActive: function (id) {
- this.menu.find('li').removeClass('active');
- this.menu.find('a[href=#' + id + ']').parent().addClass('active');
- },
-
- templateName: 'settings/sidebar'
- });
-
- // Content panes
- // --------------
- Settings.Pane = Ghost.View.extend({
- options: {
- modelType: 'Settings'
- },
- destroy: function () {
- this.$el.removeClass('active');
- this.undelegateEvents();
- },
- render: function () {
- this.$el.hide();
- Ghost.View.prototype.render.call(this);
- this.$el.fadeIn(300);
- },
- afterRender: function () {
- this.$el.attr('id', this.id);
- this.$el.addClass('active');
- },
- saveSuccess: function (model, response, options) {
- /*jshint unused:false*/
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'success',
- message: 'Saved',
- status: 'passive'
- });
- },
- saveError: function (model, xhr) {
- /*jshint unused:false*/
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- },
- validationError: function (message) {
- Ghost.notifications.clearEverything();
- Ghost.notifications.addItem({
- type: 'error',
- message: message,
- status: 'passive'
- });
- }
- });
-
- // ### General settings
- Settings.general = Settings.Pane.extend({
- id: "general",
-
- events: {
- 'click .button-save': 'saveSettings',
- 'click .js-modal-logo': 'showLogo',
- 'click .js-modal-cover': 'showCover'
- },
-
- saveSettings: function () {
- var self = this,
- title = this.$('#blog-title').val(),
- description = this.$('#blog-description').val(),
- email = this.$('#email-address').val(),
- postsPerPage = this.$('#postsPerPage').val(),
- permalinks = this.$('#permalinks').is(':checked') ? '/:year/:month/:day/:slug/' : '/:slug/',
- validationErrors = [];
-
- if (!validator.isLength(title, 0, 150)) {
- validationErrors.push({message: "Title is too long", el: $('#blog-title')});
- }
-
- if (!validator.isLength(description, 0, 200)) {
- validationErrors.push({message: "Description is too long", el: $('#blog-description')});
- }
-
- if (!validator.isEmail(email) || !validator.isLength(email, 0, 254)) {
- validationErrors.push({message: "Please supply a valid email address", el: $('#email-address')});
- }
-
- if (!validator.isInt(postsPerPage) || postsPerPage > 1000) {
- validationErrors.push({message: "Please use a number less than 1000", el: $('postsPerPage')});
- }
-
- if (!validator.isInt(postsPerPage) || postsPerPage < 0) {
- validationErrors.push({message: "Please use a number greater than 0", el: $('postsPerPage')});
- }
-
-
- if (validationErrors.length) {
- validator.handleErrors(validationErrors);
- } else {
- this.model.save({
- title: title,
- description: description,
- email: email,
- postsPerPage: postsPerPage,
- activeTheme: this.$('#activeTheme').val(),
- permalinks: permalinks
- }, {
- success: this.saveSuccess,
- error: this.saveError
- }).then(function () { self.render(); });
- }
- },
- showLogo: function (e) {
- e.preventDefault();
- var settings = this.model.toJSON();
- this.showUpload('logo', settings.logo);
- },
- showCover: function (e) {
- e.preventDefault();
- var settings = this.model.toJSON();
- this.showUpload('cover', settings.cover);
- },
- showUpload: function (key, src) {
- var self = this,
- upload = new Ghost.Models.uploadModal({'key': key, 'src': src, 'id': this.id, 'accept': {
- func: function () { // The function called on acceptance
- var data = {};
- if (this.$('.js-upload-url').val()) {
- data[key] = this.$('.js-upload-url').val();
- } else {
- data[key] = this.$('.js-upload-target').attr('src');
- }
- self.model.set(data);
- self.saveSettings();
- return true;
- },
- buttonClass: "button-save right",
- text: "Save" // The accept button text
- }});
-
- this.addSubview(new Ghost.Views.Modal({
- model: upload
- }));
- },
- templateName: 'settings/general',
-
- afterRender: function () {
- var self = this;
-
- this.$('#permalinks').prop('checked', this.model.get('permalinks') !== '/:slug/');
- this.$('.js-drop-zone').upload();
-
- Countable.live(document.getElementById('blog-description'), function (counter) {
- var descriptionContainer = self.$('.description-container .word-count');
- if (counter.all > 180) {
- descriptionContainer.css({color: "#e25440"});
- } else {
- descriptionContainer.css({color: "#9E9D95"});
- }
-
- descriptionContainer.text(200 - counter.all);
-
- });
-
- Settings.Pane.prototype.afterRender.call(this);
- }
- });
-
- // ### User profile
- Settings.user = Settings.Pane.extend({
- templateName: 'settings/user-profile',
-
- id: 'user',
-
- options: {
- modelType: 'User'
- },
-
- events: {
- 'click .button-save': 'saveUser',
- 'click .button-change-password': 'changePassword',
- 'click .js-modal-cover': 'showCover',
- 'click .js-modal-image': 'showImage',
- 'keyup .user-profile': 'handleEnterKeyOnForm'
- },
- showCover: function (e) {
- e.preventDefault();
- var user = this.model.toJSON();
- this.showUpload('cover', user.cover);
- },
- showImage: function (e) {
- e.preventDefault();
- var user = this.model.toJSON();
- this.showUpload('image', user.image);
- },
- showUpload: function (key, src) {
- var self = this, upload = new Ghost.Models.uploadModal({'key': key, 'src': src, 'id': this.id, 'accept': {
- func: function () { // The function called on acceptance
- var data = {};
- if (this.$('.js-upload-url').val()) {
- data[key] = this.$('.js-upload-url').val();
- } else {
- data[key] = this.$('.js-upload-target').attr('src');
- }
- self.model.set(data);
- self.saveUser(data);
- return true;
- },
- buttonClass: "button-save right",
- text: "Save" // The accept button text
- }});
-
- this.addSubview(new Ghost.Views.Modal({
- model: upload
- }));
- },
-
- handleEnterKeyOnForm: function (ev) {
- // Don't worry about it unless it's an enter key
- if (ev.which !== 13) {
- return;
- }
-
- var $target = $(ev.target);
-
- if ($target.is("textarea")) {
- // Allow enter key on user bio text area.
- return;
- }
-
- if ($target.is('input[type=password]')) {
- // Change password if on a password input
- return this.changePassword(ev);
- }
-
- // Simulate clicking save otherwise
- ev.preventDefault();
-
- this.saveUser(ev);
-
- return false;
- },
-
- saveUser: function () {
- var self = this,
- userName = this.$('#user-name').val(),
- userEmail = this.$('#user-email').val(),
- userLocation = this.$('#user-location').val(),
- userWebsite = this.$('#user-website').val(),
- userBio = this.$('#user-bio').val(),
- validationErrors = [];
-
- if (!validator.isLength(userName, 0, 150)) {
- validationErrors.push({message: "Name is too long", el: $('#user-name')});
- }
-
- if (!validator.isLength(userBio, 0, 200)) {
- validationErrors.push({message: "Bio is too long", el: $('#user-bio')});
- }
-
- if (!validator.isEmail(userEmail)) {
- validationErrors.push({message: "Please supply a valid email address", el: $('#user-email')});
- }
-
- if (!validator.isLength(userLocation, 0, 150)) {
- validationErrors.push({message: "Location is too long", el: $('#user-location')});
- }
-
- if (userWebsite.length) {
- if (!validator.isURL(userWebsite) || !validator.isLength(userWebsite, 0, 2000)) {
- validationErrors.push({message: "Please use a valid url", el: $('#user-website')});
- }
- }
-
- if (validationErrors.length) {
- validator.handleErrors(validationErrors);
- } else {
-
- this.model.save({
- 'name': userName,
- 'email': userEmail,
- 'location': userLocation,
- 'website': userWebsite,
- 'bio': userBio
- }, {
- success: this.saveSuccess,
- error: this.saveError
- }).then(function () {
- self.render();
- });
- }
- },
-
- changePassword: function (event) {
- event.preventDefault();
- var self = this,
- oldPassword = this.$('#user-password-old').val(),
- newPassword = this.$('#user-password-new').val(),
- ne2Password = this.$('#user-new-password-verification').val(),
- validationErrors = [];
-
- if (!validator.equals(newPassword, ne2Password)) {
- validationErrors.push("Your new passwords do not match");
- }
-
- if (!validator.isLength(newPassword, 8)) {
- validationErrors.push("Your password is not long enough. It must be at least 8 characters long.");
- }
-
- if (validationErrors.length) {
- validator.handleErrors(validationErrors);
- } else {
- $.ajax({
- url: Ghost.paths.subdir + '/ghost/api/v0.1/users/password/',
- type: 'PUT',
- headers: {
- 'X-CSRF-Token': $("meta[name='csrf-param']").attr('content')
- },
- data: {password: [{
- oldPassword: oldPassword,
- newPassword: newPassword,
- ne2Password: ne2Password
- }]},
- success: function (msg) {
- Ghost.notifications.addItem({
- type: 'success',
- message: msg.password[0].message,
- status: 'passive',
- id: 'success-98'
- });
- self.$('#user-password-old, #user-password-new, #user-new-password-verification').val('');
- },
- error: function (xhr) {
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- }
- }).then(function () {
- self.render();
- });
- }
- },
-
- afterRender: function () {
- var self = this;
-
- Countable.live(document.getElementById('user-bio'), function (counter) {
- var bioContainer = self.$('.bio-container .word-count');
- if (counter.all > 180) {
- bioContainer.css({color: "#e25440"});
- } else {
- bioContainer.css({color: "#9E9D95"});
- }
-
- bioContainer.text(200 - counter.all);
-
- });
-
- Settings.Pane.prototype.afterRender.call(this);
- }
- });
-
- // ### Apps page
- Settings.apps = Settings.Pane.extend({
- id: "apps",
-
- events: {
- 'click .js-button-activate': 'activateApp',
- 'click .js-button-deactivate': 'deactivateApp'
- },
-
- beforeRender: function () {
- this.availableApps = this.model.toJSON().availableApps;
- },
-
- activateApp: function (event) {
- var button = $(event.currentTarget);
-
- button.removeClass('button-add').addClass('button js-button-active').text('Working');
-
- this.saveStates();
- },
-
- deactivateApp: function (event) {
- var button = $(event.currentTarget);
-
- button.removeClass('button-delete js-button-active').addClass('button').text('Working');
-
- this.saveStates();
- },
-
- saveStates: function () {
- var activeButtons = this.$el.find('.js-apps .js-button-active'),
- toSave = [],
- self = this;
-
- _.each(activeButtons, function (app) {
- toSave.push($(app).data('app'));
- });
-
- this.model.save({
- activeApps: JSON.stringify(toSave)
- }, {
- success: this.saveSuccess,
- error: this.saveError
- }).then(function () { self.render(); });
- },
-
- saveSuccess: function () {
- Ghost.notifications.addItem({
- type: 'success',
- message: 'Active applications updated.',
- status: 'passive',
- id: 'success-1100'
- });
- },
-
- saveError: function (xhr) {
- Ghost.notifications.addItem({
- type: 'error',
- message: Ghost.Views.Utils.getRequestErrorMessage(xhr),
- status: 'passive'
- });
- },
-
- templateName: 'settings/apps'
- });
-
-}());
diff --git a/core/server/api/authentication.js b/core/server/api/authentication.js
index 0ece83856a..a08d067589 100644
--- a/core/server/api/authentication.js
+++ b/core/server/api/authentication.js
@@ -38,7 +38,7 @@ authentication = {
return dataProvider.User.generateResetToken(email, expires, dbHash).then(function (resetToken) {
var baseUrl = config().forceAdminSSL ? (config().urlSSL || config().url) : config().url,
siteLink = '' + baseUrl + '',
- resetUrl = baseUrl.replace(/\/$/, '') + '/ghost/ember/reset/' + resetToken + '/',
+ resetUrl = baseUrl.replace(/\/$/, '') + '/ghost/reset/' + resetToken + '/',
resetLink = '' + resetUrl + '',
payload = {
mail: [{
diff --git a/core/server/controllers/admin.js b/core/server/controllers/admin.js
index cf698be9ff..72a05719de 100644
--- a/core/server/controllers/admin.js
+++ b/core/server/controllers/admin.js
@@ -6,65 +6,25 @@ var config = require('../config'),
errors = require('../errors'),
storage = require('../storage'),
updateCheck = require('../update-check'),
- adminNavbar,
- adminControllers,
- loginSecurity = [];
-
-adminNavbar = {
- content: {
- name: 'Content',
- navClass: 'content',
- key: 'admin.navbar.content',
- path: '/'
- },
- add: {
- name: 'New Post',
- navClass: 'editor',
- key: 'admin.navbar.editor',
- path: '/editor/'
- },
- settings: {
- name: 'Settings',
- navClass: 'settings',
- key: 'admin.navbar.settings',
- path: '/settings/'
- }
-};
-
-
-// TODO: make this a util or helper
-function setSelected(list, name) {
- _.each(list, function (item, key) {
- item.selected = key === name;
- });
- return list;
-}
+ adminControllers;
adminControllers = {
+ // Route: index
+ // Path: /ghost/
+ // Method: GET
'index': function (req, res) {
-
/*jslint unparam:true*/
var userData,
- // config we need on the frontend
+ // config we need on the frontend
frontConfig = {
apps: config().apps,
fileStorage: config().fileStorage
};
- res.render('default-ember', {
- user: userData,
- config: JSON.stringify(frontConfig)
- });
- },
- // Route: index
- // Path: /ghost/
- // Method: GET
- 'indexold': function (req, res) {
- /*jslint unparam:true*/
function renderIndex() {
- res.render('content', {
- bodyClass: 'manage',
- adminNav: setSelected(adminNavbar, 'content')
+ res.render('default', {
+ user: userData,
+ config: JSON.stringify(frontConfig)
});
}
@@ -74,90 +34,6 @@ adminControllers = {
// an error here should just get logged
).otherwise(errors.logError);
},
- 'content': function (req, res) {
- /*jslint unparam:true*/
- res.render('content', {
- bodyClass: 'manage',
- adminNav: setSelected(adminNavbar, 'content')
- });
- },
- // Route: editor
- // Path: /ghost/editor(/:id)?(/:action)?/
- // Method: GET
- 'editor': function (req, res) {
- if (req.params.id && req.params.action) {
- if (req.params.action !== 'view') {
- return errors.error404(req, res);
- }
-
- api.posts.read({ id: req.params.id }).then(function (result) {
- return config.urlForPost(api.settings, result.posts[0]).then(function (url) {
- return res.redirect(url);
- });
- }, function () {
- return errors.error404(req, res);
- });
-
- } else if (req.params.id !== undefined) {
- res.render('editor', {
- bodyClass: 'editor',
- adminNav: setSelected(adminNavbar, 'content')
- });
- } else {
- res.render('editor', {
- bodyClass: 'editor',
- adminNav: setSelected(adminNavbar, 'add')
- });
- }
- },
- // Route: settings
- // path: /ghost/settings/(*/)?
- // Method: GET
- 'settings': function (req, res, next) {
- // TODO: Centralise list/enumeration of settings panes, so we don't run into trouble in future.
- var allowedSections = ['', 'general', 'user', 'apps'],
- section = req.url.replace(/(^\/ghost\/settings[\/]*|\/$)/ig, '');
-
- if (allowedSections.indexOf(section) < 0) {
- return next();
- }
-
- res.render('settings', {
- bodyClass: 'settings',
- adminNav: setSelected(adminNavbar, 'settings')
- });
- },
- // Route: debug
- // path: /ghost/debug/
- // Method: GET
- 'debug': {
- index: function (req, res) {
- /*jslint unparam:true*/
- res.render('debug', {
- bodyClass: 'settings',
- adminNav: setSelected(adminNavbar, 'settings')
- });
- },
- // frontend route for downloading a file
- exportContent: function (req, res) {
- api.db.exportContent({context: {user: req.user.id}}).then(function (exportData) {
- // send a file to the client
- res.set('Content-Disposition', 'attachment; filename="GhostData.json"');
- res.json(exportData);
- }).otherwise(function (err) {
- var notification = {
- type: 'error',
- message: 'Your export file could not be generated. Error: ' + err.message
- };
-
- errors.logError(err, 'admin.js', "Your export file could not be generated.");
-
- return api.notifications.add({ notifications: [notification]}).then(function () {
- res.redirect(config().paths.subdir + '/ghost/debug');
- });
- });
- }
- },
// Route: upload
// Path: /ghost/upload/
// Method: POST
@@ -181,122 +57,9 @@ adminControllers = {
return res.send(500, e.message);
});
},
- // Route: signout
- // Path: /ghost/signout/
- // Method: GET
- 'signout': function (req, res) {
- var notification = {
- type: 'success',
- message: 'You were successfully signed out',
- status: 'passive'
- };
-
- return api.notifications.add({ notifications: [notification] }).then(function () {
- res.redirect(config().paths.subdir + '/ghost/signin/');
- });
- },
- // Route: doSignout
- // Path: /ghost/signout/
- // Method: POST
- 'doSignout': function (req, res) {
- req.session.destroy();
-
- var statusCode,
- redirectUrl,
- errorMessage,
- notification = {
- type: 'success',
- message: 'You were successfully signed out.',
- status: 'passive'
- };
-
- if (_.isUndefined(req.session)) {
- statusCode = 200;
- redirectUrl = config().paths.subdir + '/ghost/signin/';
- } else {
- notification.type = 'error';
- notification.message = 'Unable to sign out.';
-
- statusCode = 500;
- errorMessage = 'There was a problem logging out. Please try again.';
- }
-
- return api.notifications.add({ notifications: [notification] }).then(function () {
- res.json(statusCode, {error: errorMessage, redirect: redirectUrl});
- });
-
- },
- // Route: signin
- // Path: /ghost/signin/
- // Method: GET
- 'signin': function (req, res) {
- /*jslint unparam:true*/
- res.render('login', {
- bodyClass: 'ghost-login',
- hideNavbar: true,
- adminNav: setSelected(adminNavbar, 'login')
- });
- },
- // Route: doSignin
- // Path: /ghost/signin/
- // Method: POST
- 'doSignin': function (req, res) {
- var currentTime = process.hrtime()[0],
- remoteAddress = req.connection.remoteAddress,
- denied = '';
- loginSecurity = _.filter(loginSecurity, function (ipTime) {
- return (ipTime.time + 2 > currentTime);
- });
- denied = _.find(loginSecurity, function (ipTime) {
- return (ipTime.ip === remoteAddress);
- });
-
- if (!denied) {
- loginSecurity.push({ip: remoteAddress, time: currentTime});
- api.users.check({email: req.body.email, pw: req.body.password}).then(function (user) {
- // Carry over the csrf secret
- var existingSecret = req.session.csrfSecret;
-
- req.session.regenerate(function (err) {
- if (!err) {
- req.session.csrfSecret = existingSecret;
-
- req.session.user = user.id;
- req.session.userData = user.attributes;
-
- var redirect = config().paths.subdir + '/ghost/';
- if (req.body.redirect) {
- redirect += decodeURIComponent(req.body.redirect);
- }
- // If this IP address successfully logs in we
- // can remove it from the array of failed login attempts.
- loginSecurity = _.reject(loginSecurity, function (ipTime) {
- return ipTime.ip === remoteAddress;
- });
- res.json(200, {redirect: redirect, userData: req.session.userData});
- }
- });
- }, function (error) {
- res.json(401, {error: error.message});
- });
- } else {
- res.json(401, {error: 'Slow down, there are way too many login attempts!'});
- }
- },
- // Route: signup
- // Path: /ghost/signup/
- // Method: GET
- 'signup': function (req, res) {
- /*jslint unparam:true*/
- res.render('signup', {
- bodyClass: 'ghost-signup',
- hideNavbar: true,
- adminNav: setSelected(adminNavbar, 'login')
- });
- },
// Route: doSignup
- // Path: /ghost/signup/
+ // Path: /ghost/setup/
// Method: POST
'doSignup': function (req, res) {
var name = req.body.name,
@@ -308,7 +71,7 @@ adminControllers = {
email: email,
password: password
}];
-
+
api.users.register({users: users}).then(function () {
var settings = [];
@@ -356,117 +119,6 @@ adminControllers = {
}).otherwise(function (error) {
res.json(401, {error: error.message});
});
- },
- // Route: forgotten
- // Path: /ghost/forgotten/
- // Method: GET
- 'forgotten': function (req, res) {
- /*jslint unparam:true*/
- res.render('forgotten', {
- bodyClass: 'ghost-forgotten',
- hideNavbar: true,
- adminNav: setSelected(adminNavbar, 'login')
- });
- },
- // TODO: remove when old admin is removed, functionality lives now in api/authentication
- // Route: doForgotten
- // Path: /ghost/forgotten/
- // Method: POST
- 'doForgotten': function (req, res) {
- var email = req.body.email;
-
- api.users.generateResetToken(email).then(function (token) {
- var baseUrl = config().forceAdminSSL ? (config().urlSSL || config().url) : config().url,
- siteLink = '' + baseUrl + '',
- resetUrl = baseUrl.replace(/\/$/, '') + '/ghost/reset/' + token + '/',
- resetLink = '' + resetUrl + '',
- payload = {
- mail: [{
- message: {
- to: email,
- subject: 'Reset Password',
- html: 'Hello!
' +
- 'A request has been made to reset the password on the site ' + siteLink + '.
' +
- 'Please follow the link below to reset your password:
' + resetLink + '
' +
- 'Ghost
'
- },
- options: {}
- }]
- };
-
- return api.mail.send(payload);
- }).then(function success() {
- // TODO: note that this function takes a response as an
- // argument but jshint complains of it not being used
- var notification = {
- type: 'success',
- message: 'Check your email for further instructions',
- status: 'passive'
- };
-
- return api.notifications.add({ notifications: [notification] }).then(function () {
- res.json(200, {redirect: config().paths.subdir + '/ghost/signin/'});
- });
-
- }, function failure(error) {
- // TODO: This is kind of sketchy, depends on magic string error.message from Bookshelf.
- if (error && error.message === 'EmptyResponse') {
- error.message = "Invalid email address";
- }
-
- res.json(401, {error: error.message});
- });
- },
- // Route: reset
- // Path: /ghost/reset/:token
- // Method: GET
- 'reset': function (req, res) {
- // Validate the request token
- var token = req.params.token;
-
- api.users.validateToken(token).then(function () {
- // Render the reset form
- res.render('reset', {
- bodyClass: 'ghost-reset',
- hideNavbar: true,
- adminNav: setSelected(adminNavbar, 'reset')
- });
- }).otherwise(function (err) {
- // Redirect to forgotten if invalid token
- var notification = {
- type: 'error',
- message: 'Invalid or expired token'
- };
-
- errors.logError(err, 'admin.js', "Please check the provided token for validity and expiration.");
-
- return api.notifications.add({ notifications: [notification] }).then(function () {
- res.redirect(config().paths.subdir + '/ghost/forgotten');
- });
- });
- },
- // TODO: remove when old admin is removed, functionality lives now in api/authentication
- // Route: doReset
- // Path: /ghost/reset/:token
- // Method: POST
- 'doReset': function (req, res) {
- var token = req.params.token,
- newPassword = req.param('newpassword'),
- ne2Password = req.param('ne2password');
-
- api.users.resetPassword(token, newPassword, ne2Password).then(function () {
- var notification = {
- type: 'success',
- message: 'Password changed successfully.',
- status: 'passive'
- };
-
- return api.notifications.add({ notifications: [notification] }).then(function () {
- res.json(200, {redirect: config().paths.subdir + '/ghost/signin/'});
- });
- }).otherwise(function (err) {
- res.json(401, {error: err.message});
- });
}
};
diff --git a/core/server/helpers/index.js b/core/server/helpers/index.js
index 5895adfae9..62e1dfe87b 100644
--- a/core/server/helpers/index.js
+++ b/core/server/helpers/index.js
@@ -163,21 +163,17 @@ coreHelpers.url = function (options) {
// *Usage example:*
// `{{asset "css/screen.css"}}`
// `{{asset "css/screen.css" ghost="true"}}`
-// `{{asset "css/screen.css" ember="true"}}`
// Returns the path to the specified asset. The ghost
// flag outputs the asset path for the Ghost admin
coreHelpers.asset = function (context, options) {
var output = '',
- isAdmin = options && options.hash && options.hash.ghost,
- isEmberAdmin = options && options.hash && options.hash.ember;
+ isAdmin = options && options.hash && options.hash.ghost;
output += config().paths.subdir + '/';
if (!context.match(/^favicon\.ico$/) && !context.match(/^shared/) && !context.match(/^asset/)) {
if (isAdmin) {
output += 'ghost/';
- } else if (isEmberAdmin) {
- output += 'ghost/ember/';
} else {
output += 'assets/';
}
@@ -350,19 +346,6 @@ coreHelpers.apps = function (context, options) {
};
coreHelpers.ghost_script_tags = function () {
- var scriptList = isProduction ? scriptFiles.production : scriptFiles.development;
-
- scriptList = _.map(scriptList, function (fileName) {
- return scriptTemplate({
- source: config().paths.subdir + '/ghost/scripts/' + fileName,
- version: coreHelpers.assetHash
- });
- });
-
- return scriptList.join('');
-};
-
-coreHelpers.ember_script_tags = function () {
var scriptList = scriptFiles.ember;
scriptList = _.map(scriptList, function (fileName) {
@@ -825,18 +808,12 @@ registerHelpers = function (adminHbs, assetHash) {
// Register admin helpers
- registerAdminHelper('asset', coreHelpers.asset);
-
registerAdminHelper('ghost_script_tags', coreHelpers.ghost_script_tags);
- registerAdminHelper('ember_script_tags', coreHelpers.ember_script_tags);
-
- registerAdminHelper('file_storage', coreHelpers.file_storage);
-
- registerAdminHelper('apps', coreHelpers.apps);
-
- registerAdminHelper('admin_url', coreHelpers.admin_url);
+ registerAdminHelper('asset', coreHelpers.asset);
+ // TODO: Make sure this works #3160
+ // we probably don't need this code for it, but it needs to work still
registerAsyncAdminHelper('update_notification', coreHelpers.update_notification);
};
diff --git a/core/server/index.js b/core/server/index.js
index 6a1adbd2d6..43c4a565c8 100644
--- a/core/server/index.js
+++ b/core/server/index.js
@@ -77,8 +77,7 @@ function builtFilesExist() {
var deferreds = [],
location = config().paths.builtScriptPath,
- fileNames = process.env.NODE_ENV === 'production' ?
- helpers.scriptFiles.production : helpers.scriptFiles.development;
+ fileNames = helpers.scriptFiles.ember;
function checkExist(fileName) {
var deferred = when.defer(),
@@ -256,7 +255,7 @@ function init(server) {
server.set('view engine', 'hbs');
// Create a hbs instance for admin and init view engine
- server.set('admin view engine', adminHbs.express3({partialsDir: config().paths.adminViews + 'partials'}));
+ server.set('admin view engine', adminHbs.express3({}));
// Load helpers
helpers.loadCoreHelpers(adminHbs, assetHash);
diff --git a/core/server/middleware/index.js b/core/server/middleware/index.js
index 8f646c54a1..4d63460ac4 100644
--- a/core/server/middleware/index.js
+++ b/core/server/middleware/index.js
@@ -136,28 +136,13 @@ function updateActiveTheme(req, res, next) {
});
}
-// Redirect to signup if no user exists
-// TODO Remove this when
-function redirectToSignup(req, res, next) {
- /*jslint unparam:true*/
-
- api.users.doesUserExist().then(function (exists) {
- if (!exists) {
- return res.redirect(config().paths.subdir + '/ghost/signup/');
- }
- next();
- }).otherwise(function (err) {
- return next(new Error(err));
- });
-}
-
// Redirect to setup if no user exists
function redirectToSetup(req, res, next) {
/*jslint unparam:true*/
api.users.doesUserExist().then(function (exists) {
- if (!exists && !req.path.match(/\/ghost\/ember\/setup\//)) {
- return res.redirect(config().paths.subdir + '/ghost/ember/setup/');
+ if (!exists && !req.path.match(/\/ghost\/setup\//)) {
+ return res.redirect(config().paths.subdir + '/ghost/setup/');
}
next();
}).otherwise(function (err) {
@@ -278,8 +263,7 @@ module.exports = function (server) {
expressServer.use(decideContext);
// Admin only config
- expressServer.use(subdir + '/ghost', middleware.whenEnabled('admin', express['static'](path.join(corePath, '/clientold/assets'), {maxAge: ONE_YEAR_MS})));
- expressServer.use(subdir + '/ghost/ember', middleware.whenEnabled('admin', express['static'](path.join(corePath, '/client/assets'), {maxAge: ONE_YEAR_MS})));
+ expressServer.use(subdir + '/ghost', middleware.whenEnabled('admin', express['static'](path.join(corePath, '/client/assets'), {maxAge: ONE_YEAR_MS})));
// Force SSL
// NOTE: Importantly this is _after_ the check above for admin-theme static resources,
@@ -339,5 +323,4 @@ module.exports = function (server) {
// Export middleware functions directly
module.exports.middleware = middleware;
// Expose middleware functions in this file as well
-module.exports.middleware.redirectToSignup = redirectToSignup;
module.exports.middleware.redirectToSetup = redirectToSetup;
diff --git a/core/server/middleware/middleware.js b/core/server/middleware/middleware.js
index ea110192d5..96db709163 100644
--- a/core/server/middleware/middleware.js
+++ b/core/server/middleware/middleware.js
@@ -37,12 +37,7 @@ var middleware = {
// exceptions for signin, signout, signup, forgotten, reset only
// api and frontend use different authentication mechanisms atm
authenticate: function (req, res, next) {
- var noAuthNeeded = [
- '/ghost/signin/', '/ghost/signout/', '/ghost/signup/',
- '/ghost/forgotten/', '/ghost/reset/', '/ghost/ember/',
- '/ghost/setup/'
- ],
- path,
+ var path,
subPath;
// SubPath is the url path starting after any default subdirectories
@@ -80,46 +75,10 @@ var middleware = {
}
)(req, res, next);
}
- if (noAuthNeeded.indexOf(subPath) < 0 && subPath.indexOf('/ghost/api/') !== 0) {
- return middleware.auth(req, res, next);
- }
}
next();
},
- // ### Auth Middleware
- // Authenticate a request by redirecting to login if not logged in.
- // We strip /ghost/ out of the redirect parameter for neatness
- auth: function (req, res, next) {
- if (!req.user) {
- var subPath = req.path.substring(config().paths.subdir.length),
- reqPath = subPath.replace(/^\/ghost\/?/gi, ''),
- redirect = '';
-
- if (reqPath !== '') {
- redirect = '?r=' + encodeURIComponent(reqPath);
- }
-
- if (subPath.indexOf('/ember') > -1) {
- return res.redirect(config().paths.subdir + '/ghost/ember/signin/');
- }
-
- return res.redirect(config().paths.subdir + '/ghost/signin/' + redirect);
- }
- next();
- },
-
- // ## AuthApi Middleware
- // Authenticate a request to the API by responding with a 401 and json error details
- authAPI: function (req, res, next) {
- if (!req.user) {
- res.json(401, { error: 'Please sign in' });
- return;
- }
-
- next();
- },
-
// Check if we're logged in, and if so, redirect people back to dashboard
// Login and signup forms in particular
redirectToDashboard: function (req, res, next) {
diff --git a/core/server/routes/admin.js b/core/server/routes/admin.js
index a11532be03..63597e8741 100644
--- a/core/server/routes/admin.js
+++ b/core/server/routes/admin.js
@@ -11,65 +11,38 @@ adminRoutes = function (middleware) {
var router = express.Router(),
subdir = config().paths.subdir;
- // Have ember route look for hits first
- // to prevent conflicts with pre-existing routes
- router.get('/ghost/ember/*', middleware.redirectToSetup, admin.index);
-
// ### Admin routes
- router.get('/logout/', function redirect(req, res) {
+ router.get('^/logout/', function redirect(req, res) {
/*jslint unparam:true*/
res.set({'Cache-Control': 'public, max-age=' + ONE_YEAR_S});
res.redirect(301, subdir + '/ghost/signout/');
});
- router.get('/signout/', function redirect(req, res) {
+ router.get('^/signout/', function redirect(req, res) {
/*jslint unparam:true*/
res.set({'Cache-Control': 'public, max-age=' + ONE_YEAR_S});
res.redirect(301, subdir + '/ghost/signout/');
});
- router.get('/signin/', function redirect(req, res) {
+ router.get('^/signin/', function redirect(req, res) {
/*jslint unparam:true*/
res.set({'Cache-Control': 'public, max-age=' + ONE_YEAR_S});
res.redirect(301, subdir + '/ghost/signin/');
});
- router.get('/signup/', function redirect(req, res) {
+ router.get('^/signup/', function redirect(req, res) {
/*jslint unparam:true*/
res.set({'Cache-Control': 'public, max-age=' + ONE_YEAR_S});
res.redirect(301, subdir + '/ghost/signup/');
});
router.post('/ghost/setup/', admin.doSignup);
- router.get('/ghost/signout/', admin.signout);
- router.post('/ghost/signout/', admin.doSignout);
- router.get('/ghost/signin/', middleware.redirectToSignup, middleware.redirectToDashboard, admin.signin);
- router.post('/ghost/signin/', admin.doSignin);
- router.get('/ghost/signup/', middleware.redirectToDashboard, admin.signup);
- router.post('/ghost/signup/', admin.doSignup);
- router.get('/ghost/forgotten/', middleware.redirectToDashboard, admin.forgotten);
- router.post('/ghost/forgotten/', admin.doForgotten);
- router.get('/ghost/reset/:token', admin.reset);
- router.post('/ghost/reset/:token', admin.doReset);
-
- router.get('/ghost/editor/:id/:action', admin.editor);
- router.get('/ghost/editor/:id/', admin.editor);
- router.get('/ghost/editor/', admin.editor);
- router.get('/ghost/content/', admin.content);
- router.get('/ghost/settings*', admin.settings);
- router.get('/ghost/debug/', admin.debug.index);
-
- router.get('/ghost/export/', admin.debug.exportContent);
-
router.post('/ghost/upload/', middleware.busboy, admin.upload);
// redirect to /ghost and let that do the authentication to prevent redirects to /ghost//admin etc.
- router.get(/\/((ghost-admin|admin|wp-admin|dashboard|signin)\/?)$/, function (req, res) {
+ router.get(/^\/((ghost-admin|admin|wp-admin|dashboard|signin)\/?)$/, function (req, res) {
/*jslint unparam:true*/
res.redirect(subdir + '/ghost/');
});
- router.get(/\/ghost$/, function (req, res) {
- /*jslint unparam:true*/
- res.redirect(subdir + '/ghost/');
- });
- router.get('/ghost/', admin.indexold);
+
+ router.get('/ghost/*', middleware.redirectToSetup, admin.index);
return router;
};
diff --git a/core/server/views/content.hbs b/core/server/views/content.hbs
deleted file mode 100644
index 2b08442bd3..0000000000
--- a/core/server/views/content.hbs
+++ /dev/null
@@ -1,17 +0,0 @@
-{{!< default}}
-
diff --git a/core/server/views/debug.hbs b/core/server/views/debug.hbs
deleted file mode 100644
index 4f6fd490c9..0000000000
--- a/core/server/views/debug.hbs
+++ /dev/null
@@ -1,58 +0,0 @@
-{{!< default}}
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/server/views/default-ember.hbs b/core/server/views/default-ember.hbs
deleted file mode 100644
index 80be672e9c..0000000000
--- a/core/server/views/default-ember.hbs
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
- Ghost Admin
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{{ember_script_tags}}}
-
-
-
-
diff --git a/core/server/views/default.hbs b/core/server/views/default.hbs
index 24428df33f..ce6ff4e68b 100644
--- a/core/server/views/default.hbs
+++ b/core/server/views/default.hbs
@@ -33,23 +33,15 @@
- {{#unless hideNavbar}}
- {{> navbar}}
- {{/unless}}
-
-
- {{update_notification}}
-
-
-
- {{{body}}}
-
-
-
-
{{{ghost_script_tags}}}
+
+