diff --git a/.gitignore b/.gitignore
index c3499f13f7..f5e5f3d209 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,6 @@ CHANGELOG.md
/core/test/functional/*.png
config.js
+
+# Built asset files
+/core/built
\ No newline at end of file
diff --git a/Gruntfile.js b/Gruntfile.js
index 146465aaa7..d68e9d4741 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -40,6 +40,16 @@ var path = require('path'),
files: ['<%= paths.adminAssets %>/sass/**/*'],
tasks: ['sass:admin']
},
+ concat: {
+ files: [
+ 'core/client/*.js',
+ 'core/client/helpers/*.js',
+ 'core/client/models/*.js',
+ 'core/client/tpl/*.js',
+ 'core/client/views/*.js'
+ ],
+ tasks: ['concat']
+ },
livereload: {
files: [
// Theme CSS
@@ -49,11 +59,7 @@ var path = require('path'),
// Admin CSS
'<%= paths.adminAssets %>/css/*.css',
// Admin JS
- 'core/client/*.js',
- 'core/client/helpers/*.js',
- 'core/client/models/*.js',
- 'core/client/tpl/*.js',
- 'core/client/views/*.js'
+ 'core/built/scripts/*.js'
],
options: {
livereload: true
@@ -343,6 +349,122 @@ var path = require('path'),
tagMessage: '<%= buildType %> Release %VERSION%',
pushTo: "origin build"
}
+ },
+
+ concat: {
+ dev: {
+ files: {
+ "core/built/scripts/vendor.js": [
+ "core/shared/vendor/jquery/jquery.js",
+ "core/shared/vendor/jquery/jquery-ui-1.10.3.custom.min.js",
+ "core/client/assets/lib/jquery-utils.js",
+ "core/client/assets/lib/uploader.js",
+ "core/shared/vendor/underscore.js",
+ "core/shared/vendor/backbone/backbone.js",
+ "core/shared/vendor/handlebars/handlebars-runtime.js",
+ "core/shared/vendor/moment.js",
+
+ "core/client/assets/vendor/icheck/jquery.icheck.min.js",
+
+ "core/shared/vendor/jquery/jquery.ui.widget.js",
+ "core/shared/vendor/jquery/jquery.iframe-transport.js",
+ "core/shared/vendor/jquery/jquery.fileupload.js",
+
+ "core/client/assets/vendor/codemirror/codemirror.js",
+ "core/client/assets/vendor/codemirror/addon/mode/overlay.js",
+ "core/client/assets/vendor/codemirror/mode/markdown/markdown.js",
+ "core/client/assets/vendor/codemirror/mode/gfm/gfm.js",
+ "core/client/assets/vendor/showdown/showdown.js",
+ "core/client/assets/vendor/showdown/extensions/ghostdown.js",
+ "core/shared/vendor/showdown/extensions/github.js",
+ "core/client/assets/vendor/shortcuts.js",
+ "core/client/assets/vendor/validator-client.js",
+ "core/client/assets/vendor/countable.js",
+ "core/client/assets/vendor/to-title-case.js",
+ "core/client/assets/vendor/packery.pkgd.min.js",
+ "core/client/assets/vendor/jquery.hammer.min.js"
+ ],
+
+ "core/built/scripts/helpers.js": [
+ "core/client/init.js",
+
+ "core/client/mobile-interactions.js",
+ "core/client/toggle.js",
+ "core/client/markdown-actions.js",
+ "core/client/helpers/index.js"
+ ],
+
+ "core/built/scripts/templates.js": [
+ "core/client/tpl/hbs-tpl.js"
+ ],
+
+ "core/built/scripts/models.js": [
+ "core/client/models/**/*.js"
+ ],
+
+ "core/built/scripts/views.js": [
+ "core/client/views/**/*.js",
+ "core/client/router.js"
+ ]
+ }
+ },
+ prod: {
+ files: {
+ "core/built/scripts/ghost.js": [
+ "core/shared/vendor/jquery/jquery.js",
+ "core/shared/vendor/jquery/jquery-ui-1.10.3.custom.min.js",
+ "core/client/assets/lib/jquery-utils.js",
+ "core/client/assets/lib/uploader.js",
+ "core/shared/vendor/underscore.js",
+ "core/shared/vendor/backbone/backbone.js",
+ "core/shared/vendor/handlebars/handlebars-runtime.js",
+ "core/shared/vendor/moment.js",
+
+ "core/client/assets/vendor/icheck/jquery.icheck.min.js",
+
+ "core/shared/vendor/jquery/jquery.ui.widget.js",
+ "core/shared/vendor/jquery/jquery.iframe-transport.js",
+ "core/shared/vendor/jquery/jquery.fileupload.js",
+
+ "core/client/assets/vendor/codemirror/codemirror.js",
+ "core/client/assets/vendor/codemirror/addon/mode/overlay.js",
+ "core/client/assets/vendor/codemirror/mode/markdown/markdown.js",
+ "core/client/assets/vendor/codemirror/mode/gfm/gfm.js",
+ "core/client/assets/vendor/showdown/showdown.js",
+ "core/client/assets/vendor/showdown/extensions/ghostdown.js",
+ "core/shared/vendor/showdown/extensions/github.js",
+ "core/client/assets/vendor/shortcuts.js",
+ "core/client/assets/vendor/validator-client.js",
+ "core/client/assets/vendor/countable.js",
+ "core/client/assets/vendor/to-title-case.js",
+ "core/client/assets/vendor/packery.pkgd.min.js",
+ "core/client/assets/vendor/jquery.hammer.min.js",
+
+ "core/client/init.js",
+
+ "core/client/mobile-interactions.js",
+ "core/client/toggle.js",
+ "core/client/markdown-actions.js",
+ "core/client/helpers/index.js",
+
+ "core/client/tpl/hbs-tpl.js",
+
+ "core/client/models/**/*.js",
+
+ "core/client/views/**/*.js",
+
+ "core/client/router.js"
+ ]
+ }
+ }
+ },
+
+ uglify: {
+ prod: {
+ files: {
+ "core/built/scripts/ghost.min.js": "core/built/scripts/ghost.js"
+ }
+ }
}
};
@@ -654,6 +776,8 @@ var path = require('path'),
"shell:bourbon",
"sass:admin",
"handlebars",
+ "concat",
+ "uglify",
"bump:build",
"updateCurrentPackageInfo",
"changelog",
@@ -666,6 +790,8 @@ var path = require('path'),
"shell:bourbon",
"sass:admin",
"handlebars",
+ "concat",
+ "uglify",
"bump:build",
"updateCurrentPackageInfo",
"changelog",
@@ -677,6 +803,8 @@ var path = require('path'),
"shell:bourbon",
"sass:admin",
"handlebars",
+ "concat",
+ "uglify",
"changelog",
"copy:build",
"compress:build"
@@ -692,7 +820,7 @@ var path = require('path'),
// Prepare the project for development
// TODO: Git submodule init/update (https://github.com/jaubourg/grunt-update-submodules)?
- grunt.registerTask("init", ["shell:bourbon", "sass:admin", 'handlebars']);
+ grunt.registerTask("init", ["shell:bourbon", "default"]);
// Run unit tests
grunt.registerTask("test-unit", ['setTestEnv', 'loadConfig', "mochacli:all"]);
@@ -706,8 +834,10 @@ var path = require('path'),
// Generate Docs
grunt.registerTask("docs", ["groc"]);
+ // TODO: Production build task that minifies with uglify:prod
+
// When you just say "grunt"
- grunt.registerTask("default", ['sass:admin', 'handlebars']);
+ grunt.registerTask("default", ['sass:admin', 'handlebars', 'concat']);
};
module.exports = configureGrunt;
diff --git a/core/client/assets/vendor/shortcuts.js b/core/client/assets/vendor/shortcuts.js
index 1704005d82..32ce8a5331 100644
--- a/core/client/assets/vendor/shortcuts.js
+++ b/core/client/assets/vendor/shortcuts.js
@@ -220,4 +220,4 @@ shortcut = {
else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
else ele['on'+type] = false;
}
-}
\ No newline at end of file
+};
\ No newline at end of file
diff --git a/core/ghost.js b/core/ghost.js
index d37364e67f..2f014b194e 100644
--- a/core/ghost.js
+++ b/core/ghost.js
@@ -2,7 +2,7 @@
// Defines core methods required to build the application
// Module dependencies
-var config = require('./../config'),
+var config = require('../config'),
when = require('when'),
express = require('express'),
errors = require('./server/errorHandling'),
@@ -340,11 +340,28 @@ Ghost.prototype.initPlugins = function (pluginsToLoad) {
// Initialise Theme or admin
Ghost.prototype.initTheme = function (app) {
var self = this,
- hbsOptions;
+ oneYear = 31536000000;
+
+ app.set('view engine', 'hbs');
+ // return the correct mime type for woff files
+ express['static'].mime.define({'application/font-woff': ['woff']});
+
+ // Serve the assets of the current theme
+ app.use(express['static'](self.paths().activeTheme));
+
+ // Serve shared assets and images
+ app.use('/shared', express['static'](path.join(__dirname, '/shared')));
+ app.use('/content/images', express['static'](path.join(__dirname, '/../content/images')));
+
+ // Serve our built scripts; can't use /scripts here because themes already are
+ app.use("/built/scripts", express['static'](path.join(__dirname, '/built/scripts'), {
+ // Put a maxAge of one year on built scripts
+ maxAge: oneYear
+ }));
+
return function initTheme(req, res, next) {
- app.set('view engine', 'hbs');
- // return the correct mime type for woff files
- express['static'].mime.define({'application/font-woff': ['woff']});
+
+ var hbsOptions;
if (!res.isAdmin) {
@@ -369,9 +386,7 @@ Ghost.prototype.initTheme = function (app) {
app.use('/public', express['static'](path.join(__dirname, '/client/assets')));
app.use('/public', express['static'](path.join(__dirname, '/client')));
}
- app.use(express['static'](self.paths().activeTheme));
- app.use('/shared', express['static'](path.join(__dirname, '/shared')));
- app.use('/content/images', express['static'](path.join(__dirname, '/../content/images')));
+
next();
};
};
diff --git a/core/server/helpers/index.js b/core/server/helpers/index.js
index e3de0b76a3..dcd6d1f173 100644
--- a/core/server/helpers/index.js
+++ b/core/server/helpers/index.js
@@ -3,13 +3,17 @@ var _ = require('underscore'),
downsize = require('downsize'),
when = require('when'),
hbs = require('express-hbs'),
+ packageInfo = require('../../../package.json'),
errors = require('../errorHandling'),
models = require('../models'),
coreHelpers;
coreHelpers = function (ghost) {
- var paginationHelper;
+ var paginationHelper,
+ scriptTemplate = _.template(""),
+ isProduction = process.env.NODE_ENV === "production",
+ version = encodeURIComponent(packageInfo.version);
/**
* [ description]
@@ -317,6 +321,32 @@ coreHelpers = function (ghost) {
return ret;
});
+ // A helper for inserting the javascript tags with version hashes
+ ghost.registerThemeHelper("ghostScriptTags", function () {
+ var scriptFiles = [];
+
+ if (isProduction) {
+ scriptFiles.push("ghost.min.js");
+ } else {
+ scriptFiles = [
+ "vendor.js",
+ "helpers.js",
+ "templates.js",
+ "models.js",
+ "views.js"
+ ];
+ }
+
+ scriptFiles = _.map(scriptFiles, function (fileName) {
+ return scriptTemplate({
+ name: fileName,
+ version: version
+ });
+ });
+
+ return scriptFiles.join("");
+ });
+
// ## Template driven helpers
// Template driven helpers require that their template is loaded before they can be registered.
diff --git a/core/server/views/default.hbs b/core/server/views/default.hbs
index 446bb643fd..bccf5638ac 100644
--- a/core/server/views/default.hbs
+++ b/core/server/views/default.hbs
@@ -36,63 +36,10 @@
-
-
-
-
-
-
-
+ {{{ghostScriptTags}}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{{block "bodyScripts"}}}
+
diff --git a/core/test/unit/admin_spec.js b/core/test/unit/admin_spec.js
index 5a82fd832c..e0de048fdb 100644
--- a/core/test/unit/admin_spec.js
+++ b/core/test/unit/admin_spec.js
@@ -90,7 +90,8 @@ describe('Admin Controller', function() {
});
it('should send correct path to image when today is in Sep 2013', function(done) {
- clock = sinon.useFakeTimers(1378585460681); // Sat Sep 07 2013 21:24:20 GMT+0100 (BST)
+ // Sat Sep 07 2013 21:24
+ clock = sinon.useFakeTimers(new Date(2013, 8, 7, 21, 24).getTime());
sinon.stub(res, 'send', function(data) {
data.should.equal('/content/images/2013/Sep/IMAGE.jpg');
return done();
@@ -100,7 +101,8 @@ describe('Admin Controller', function() {
});
it('should send correct path to image when today is in Jan 2014', function(done) {
- clock = sinon.useFakeTimers(1388707200000); // Wed Jan 03 2014 00:00:00 GMT+0000 (GMT)
+ // Jan 1 2014 12:00
+ clock = sinon.useFakeTimers(new Date(2014, 0, 1, 12).getTime());
sinon.stub(res, 'send', function(data) {
data.should.equal('/content/images/2014/Jan/IMAGE.jpg');
return done();
@@ -110,7 +112,8 @@ describe('Admin Controller', function() {
});
it('can upload two different images with the same name without overwriting the first', function(done) {
- clock = sinon.useFakeTimers(1378634224614); // Sun Sep 08 2013 10:57:04 GMT+0100 (BST)
+ // Sun Sep 08 2013 10:57
+ clock = sinon.useFakeTimers(new Date(2013, 8, 8, 10, 57).getTime());
fs.exists.withArgs('content/images/2013/Sep/IMAGE.jpg').yields(true);
fs.exists.withArgs('content/images/2013/Sep/IMAGE-1.jpg').yields(false);
@@ -123,7 +126,8 @@ describe('Admin Controller', function() {
});
it('can upload five different images with the same name without overwriting the first', function(done) {
- clock = sinon.useFakeTimers(1378634224614); // Sun Sep 08 2013 10:57:04 GMT+0100 (BST)
+ // Sun Sep 08 2013 10:57
+ clock = sinon.useFakeTimers(new Date(2013, 8, 8, 10, 57).getTime());
fs.exists.withArgs('content/images/2013/Sep/IMAGE.jpg').yields(true);
fs.exists.withArgs('content/images/2013/Sep/IMAGE-1.jpg').yields(true);
fs.exists.withArgs('content/images/2013/Sep/IMAGE-2.jpg').yields(true);
diff --git a/package.json b/package.json
index 1690fb5aab..95e116dc97 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,8 @@
"grunt-bump": "~0.0.11",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-compress": "~0.5.2",
+ "grunt-contrib-concat": "~0.3.0",
+ "grunt-contrib-uglify": "~0.2.4",
"grunt-groc": "~0.3.0",
"grunt-mocha-cli": "~1.0.6",
"grunt-express-server": "~0.4.2",