diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 000000000..644b0a917 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,86 @@ +{ + // JSHint Default Configuration File (as on JSHint website) + // See http://jshint.com/docs/ for more details + + "maxerr" : 50, // {int} Maximum error before stopping + + // Enforcing + "bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.) + "camelcase" : false, // true: Identifiers must be in camelCase + "curly" : true, // true: Require {} for every new block or scope + "eqeqeq" : true, // true: Require triple equals (===) for comparison + "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() + "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` + "indent" : 2, // {int} Number of spaces to use for indentation + "latedef" : false, // true: Require variables/functions to be defined before being used + "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` + "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` + "noempty" : true, // true: Prohibit use of empty blocks + "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) + "plusplus" : false, // true: Prohibit use of `++` & `--` + "quotmark" : false, // Quotation mark consistency: + // false : do nothing (default) + // true : ensure whatever is used is consistent + // "single" : require single quotes + // "double" : require double quotes + "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) + "unused" : true, // true: Require all defined variables be used + "strict" : true, // true: Requires all functions run in ES5 Strict Mode + "trailing" : false, // true: Prohibit trailing whitespaces + "maxparams" : false, // {int} Max number of formal params allowed per function + "maxdepth" : false, // {int} Max depth of nested blocks (within functions) + "maxstatements" : false, // {int} Max number statements per function + "maxcomplexity" : false, // {int} Max cyclomatic complexity per function + "maxlen" : false, // {int} Max number of characters per line + + // Relaxing + "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) + "boss" : false, // true: Tolerate assignments where comparisons would be expected + "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. + "eqnull" : false, // true: Tolerate use of `== null` + "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) + "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) + "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) + // (ex: `for each`, multiple try/catch, function expression…) + "evil" : false, // true: Tolerate use of `eval` and `new Function()` + "expr" : false, // true: Tolerate `ExpressionStatement` as Programs + "funcscope" : false, // true: Tolerate defining variables inside control statements" + "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') + "iterator" : false, // true: Tolerate using the `__iterator__` property + "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block + "laxbreak" : false, // true: Tolerate possibly unsafe line breakings + "laxcomma" : false, // true: Tolerate comma-first style coding + "loopfunc" : false, // true: Tolerate functions being defined in loops + "multistr" : false, // true: Tolerate multi-line strings + "proto" : false, // true: Tolerate using the `__proto__` property + "scripturl" : false, // true: Tolerate script-targeted URLs + "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment + "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` + "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation + "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` + "validthis" : false, // true: Tolerate using this in a non-constructor function + + // Environments + "browser" : true, // Web Browser (window, document, etc) + "couch" : false, // CouchDB + "devel" : true, // Development/debugging (alert, confirm, etc) + "dojo" : false, // Dojo Toolkit + "jquery" : false, // jQuery + "mootools" : false, // MooTools + "node" : false, // Node.js + "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) + "prototypejs" : false, // Prototype and Scriptaculous + "rhino" : false, // Rhino + "worker" : false, // Web Workers + "wsh" : false, // Windows Scripting Host + "yui" : false, // Yahoo User Interface + + // Legacy + "nomen" : false, // true: Prohibit dangling `_` in variables + "onevar" : false, // true: Allow only one `var` statement per function + "passfail" : false, // true: Stop on first error + "white" : false, // true: Check against strict whitespace and indentation rules + + // Custom Globals + "globals" : ["require", "module"] // additional predefined global variables +} diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 000000000..e3189a0a2 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,41 @@ +module.exports = function (grunt) { + + 'use strict'; + + var pkg = require("./package.json"); + + pkg.timestamp = new Date().getTime(); + + grunt.initConfig({ + pkg: pkg, + clean: require('./tasks/clean.js'), + copy: require('./tasks/copy.js'), + cssmin: require('./tasks/cssmin.js'), + htmlmin: require('./tasks/htmlmin.js'), + jshint: require('./tasks/jshint.js'), + uglify: require('./tasks/uglify.js') + }); + + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-contrib-cssmin'); + grunt.loadNpmTasks('grunt-contrib-htmlmin'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + + // called without any further parameter: 'grunt' + grunt.registerTask('default', ['jshint']); + // build: 'grunt build' + grunt.registerTask('build', [ + //check jss + 'jshint', + //clean old build + 'clean', + //copy root and img files + 'copy', + //minify html/js/css + 'htmlmin', + 'uglify', + 'cssmin' + ]); +}; diff --git a/libs/flag.js b/libs/flag.js index a38db014f..142cc85b9 100644 --- a/libs/flag.js +++ b/libs/flag.js @@ -69,7 +69,7 @@ function getThreshold (model, content, author, callback) { var threshold = thresholds[model.modelName] * (author.role < 4 ? 2 : 1); // Calculate karma and add it to the threshold - getKarma(author, maxKarma, function (karma) { + getKarma(author, maxKarma, function (karma) { return callback(threshold + karma); }); } @@ -91,17 +91,17 @@ function saveContent (model, content, author, flags, callback) { exports.saveContent = saveContent; function flag (model, content, user, author, callback) { - var flag = new Flag({ + var fl = new Flag({ 'model': model.modelName, '_contentId': content._id, '_userId': user._id }); - flag.save(function (err, flag) { + fl.save(function (err, flag) { if (!content.flags) { content.flags = 0; } if (!content.flagged) { content.flagged = false; } - saveContent(model, content, author, user.role < 4 ? 2 : 1, callback) + saveContent(model, content, author, user.role < 4 ? 2 : 1, callback); }); } diff --git a/libs/helpers.js b/libs/helpers.js index 74fffa18f..c15e3081d 100644 --- a/libs/helpers.js +++ b/libs/helpers.js @@ -29,9 +29,11 @@ exports.formatDate = function (date) { } if (difference > week) { - ret = date.getDate() + ' ' - + months[date.getMonth()] + ' ' - + date.getFullYear(); + ret = date.getDate() + + ' ' + + months[date.getMonth()] + + ' ' + + date.getFullYear(); } else if (difference > day) { days = Math.round(difference / day); if (days <= 1) { @@ -48,7 +50,7 @@ exports.formatDate = function (date) { } return ret; -} +}; // Create an object with no properties exports.nil = function (obj) { @@ -59,7 +61,7 @@ exports.nil = function (obj) { exports.forIn(obj, function (val, key) { nilObj[key] = val; }); - + return nilObj; }; diff --git a/libs/modelsList.js b/libs/modelsList.js index bf93a5e5c..d359874c6 100644 --- a/libs/modelsList.js +++ b/libs/modelsList.js @@ -10,7 +10,7 @@ var formatDate = require('./helpers').formatDate; var listSize = 10; // /scriptlist/size/:size/sort/:orderBy/dir/:direction/page/:page -// Get a list of scripts and build the options object +// Get a list of scripts and build the options object // for the corresponding Mustache partial template exports.listScripts = function (query, params, baseUrl, callback) { @@ -41,7 +41,7 @@ exports.listScripts = function (query, params, baseUrl, callback) { var name = null; scriptsList.scripts = []; scriptsList.headings = []; - scriptsList.hasAuthor = params[4] ? + scriptsList.hasAuthor = params[4] ? params[4].indexOf('author') === -1 : true; scripts.forEach(function (script) { @@ -52,7 +52,7 @@ exports.listScripts = function (query, params, baseUrl, callback) { editUrl.shift(); - scriptsList.scripts.push({ + scriptsList.scripts.push({ name: script.name, author: script.author, description: script.meta.description || '', @@ -72,7 +72,7 @@ exports.listScripts = function (query, params, baseUrl, callback) { heading = headings[name]; if (orderBy === name) { - heading.direction = '/dir/' + + heading.direction = '/dir/' + (direction === 'asc' ? 'desc' : 'asc'); } else { heading.direction = ''; @@ -82,14 +82,14 @@ exports.listScripts = function (query, params, baseUrl, callback) { scriptsList.headings.push(heading); }*/ - scriptsList.baseUrl = baseUrl + (query.isLib === true ? + scriptsList.baseUrl = baseUrl + (query.isLib === true ? '/liblist' : '/scriptlist'); callback(scriptsList); }); }; // /userlist/size/:size/sort/:orderBy/dir/:direction/page/:page -// Get a list of users and build the options object +// Get a list of users and build the options object // for the corresponding Mustache partial template exports.listUsers = function (query, params, baseUrl, callback) { listModels(User, query, params, ['name'], @@ -97,7 +97,7 @@ exports.listUsers = function (query, params, baseUrl, callback) { usersList.users = []; users.forEach(function (user) { - usersList.users.push({ + usersList.users.push({ name: user.name, url: '/users/' + user.name, }); @@ -109,7 +109,7 @@ exports.listUsers = function (query, params, baseUrl, callback) { }; // /list/size/:size/sort/:orderBy/dir/:direction/page/:page -// Get a list of removed content and build the options object +// Get a list of removed content and build the options object // for the corresponding Mustache partial template exports.listRemoved = function (query, params, baseUrl, callback) { listModels(Remove, query, params, ['removed'], @@ -121,14 +121,14 @@ exports.listRemoved = function (query, params, baseUrl, callback) { var key = null; var contentArr = []; var val = null; - + for (key in content) { val = content[key]; val = val && typeof val === 'object' ? JSON.stringify(val) : val; contentArr.push({ 'key': key, 'value': val }); } - removedList.removed.push({ + removedList.removed.push({ remover: result.removerName, removed: formatDate(result.removed), reason: result.reason, @@ -142,7 +142,7 @@ exports.listRemoved = function (query, params, baseUrl, callback) { }; // /groups/list/size/:size/sort/:orderBy/dir/:direction/page/:page -// Get a list of groups and build the options object +// Get a list of groups and build the options object // for the corresponding Mustache partial template exports.listGroups = function (query, params, baseUrl, callback) { listModels(Group, query, params, ['rating'], @@ -150,7 +150,7 @@ exports.listGroups = function (query, params, baseUrl, callback) { groupsList.groups = []; groups.forEach(function (group) { - groupsList.groups.push({ + groupsList.groups.push({ name: group.name, url: '/group/' + group.name.replace(/\s+/g, '_'), size: group._scriptIds.length, @@ -178,7 +178,7 @@ exports.listGroups = function (query, params, baseUrl, callback) { }; // /list/size/:size/sort/:orderBy/dir/:direction/page/:page -// Get a list of discussions and build the options object +// Get a list of discussions and build the options object // for the corresponding Mustache partial template exports.listDiscussions = function (query, params, baseUrl, callback) { listModels(Discussion, query, params, ['updated', 'rating'], @@ -186,7 +186,7 @@ exports.listDiscussions = function (query, params, baseUrl, callback) { discussionsList.discussions = []; discussions.forEach(function (discussion) { - discussionsList.discussions.push({ + discussionsList.discussions.push({ topic: discussion.topic, comments: discussion.comments, author: discussion.author, @@ -195,8 +195,8 @@ exports.listDiscussions = function (query, params, baseUrl, callback) { discussion.lastCommentor : null, updated: formatDate(discussion.updated), rating: discussion.rating, - url: discussion.path - + (discussion.duplicateId ? '_' + discussion.duplicateId : '') + url: discussion.path + + (discussion.duplicateId ? '_' + discussion.duplicateId : '') }); }); @@ -206,7 +206,7 @@ exports.listDiscussions = function (query, params, baseUrl, callback) { }; // /list/size/:size/sort/:orderBy/dir/:direction/page/:page -// Get a list of comments and build the options object +// Get a list of comments and build the options object // for the corresponding Mustache partial template exports.listComments = function (query, params, baseUrl, callback) { listModels(Comment, query, params, ['created'], @@ -214,7 +214,7 @@ exports.listComments = function (query, params, baseUrl, callback) { commentsList.comments = []; comments.forEach(function (comment) { - commentsList.comments.push({ + commentsList.comments.push({ author: comment.author, content: renderMd(comment.content), created: formatDate(comment.created), @@ -228,7 +228,7 @@ exports.listComments = function (query, params, baseUrl, callback) { }); }; -// options = { +// options = { // size: (Number), orderBy: (String or Array or Object), // direction: (String), page: (Number), omit: (Array) // } @@ -266,7 +266,7 @@ function listModels (model, query, options, defaultOrder, callback) { params.sort[orderBy] = direction; } else if (orderBy instanceof Array) { orderBy.forEach(function (order) { - params.sort[order] = -1 !== fields.indexOf(order) && + params.sort[order] = -1 !== fields.indexOf(order) && model.schema.paths[order].instance === 'String' ? 1 : -1; }); } else if (typeof orderBy === 'object') { @@ -297,7 +297,7 @@ function listModels (model, query, options, defaultOrder, callback) { model.find(query, omit, params, function (err, models) { var list = {}; - if (!models) { models = [] } + if (!models) { models = []; } if (size < 0) { size = models.length; } orderBy = typeof orderBy === 'string' ? orderBy : ''; direction = direction === 1 ? 'asc' : 'desc'; @@ -315,4 +315,4 @@ function listModels (model, query, options, defaultOrder, callback) { if (list.next) { models.pop(); } callback(models, list); }); -}; +} diff --git a/libs/passportVerify.js b/libs/passportVerify.js index d93369b6f..aa9c4b4fb 100644 --- a/libs/passportVerify.js +++ b/libs/passportVerify.js @@ -66,4 +66,4 @@ exports.verify = function (id, strategy, username, loggedIn, done) { return done(null, user); } }); -} +}; diff --git a/libs/remove.js b/libs/remove.js index fe63293d8..c5923d3f4 100644 --- a/libs/remove.js +++ b/libs/remove.js @@ -7,7 +7,7 @@ var modelNames = ['Script']; var models = {}; modelNames.forEach(function (modelName) { - models[modelName] = require('../models/' + + models[modelName] = require('../models/' + modelName.toLowerCase())[modelName]; }); @@ -42,7 +42,7 @@ function removeable (model, content, user, callback) { exports.removeable = removeable; function remove (model, content, user, reason, callback) { - var remove = new Remove({ + var rem = new Remove({ 'model': model.modelName, 'content': content.toObject(), 'removed': new Date(), @@ -52,7 +52,7 @@ function remove (model, content, user, reason, callback) { '_removerId': user._id }); - remove.save(function (err, remove) { + rem.save(function (err, remove) { content.remove(function (err) { callback(remove); }); }); } @@ -80,7 +80,7 @@ exports.remove = function (model, content, user, reason, callback) { }); }; -// This function is similar to findOne but expands +// This function is similar to findOne but expands // the search to removed content // You pass it the model of the content, the search query, // the user making the query (or boolean true for internal usage), diff --git a/libs/repoManager.js b/libs/repoManager.js index 0f3b745fb..018284ede 100644 --- a/libs/repoManager.js +++ b/libs/repoManager.js @@ -27,9 +27,9 @@ function fetchRaw (host, path, callback) { else { res.on('data', function(d) { bufs.push(d); }); res.on('end', function() { - callback(bufs); + callback(bufs); }); - } + } }); req.end(); } @@ -58,7 +58,7 @@ RepoManager.prototype.fetchRepos = function (callback) { fetchJSON('/user/' + this.userId + '/repos', function (json) { json.forEach(function (repo) { if (that.user.ghUsername !== repo.owner.login) { - that.user.ghUsername = repo.owner.login; + that.user.ghUsername = repo.owner.login; that.user.save(function (err, user) {}); } @@ -82,22 +82,22 @@ RepoManager.prototype.loadScripts = function (callback, update) { var that = this; var scripts = []; - // TODO: remove usage of makeRepoArray since it causes redundant looping + // TODO: remove usage of makeRepoArray since it causes redundant looping arrayOfRepos.forEach(function (repo) { async.each(repo.scripts, function (script, cb) { - var url = '/' + encodeURI(repo.user) + '/' + encodeURI(repo.repo) - + '/master' + script.path; + var url = '/' + encodeURI(repo.user) + '/' + encodeURI(repo.repo) + + '/master' + script.path; fetchRaw('raw.githubusercontent.com', url, function (bufs) { scriptStorage.getMeta(bufs, function (meta) { if (meta) { - scriptStorage.storeScript(that.user, meta, Buffer.concat(bufs), + scriptStorage.storeScript(that.user, meta, Buffer.concat(bufs), cb, update); } }); }); }, callback); }); -} +}; // Create the Mustache object to display repos with their user scrips RepoManager.prototype.makeRepoArray = function () { @@ -122,7 +122,7 @@ RepoManager.prototype.makeRepoArray = function () { } return retOptions; -} +}; // Manages a single repo function Repo(manager, username, reponame) { @@ -146,8 +146,8 @@ Repo.prototype.parseTree = function (tree, path, done) { tree.forEach(function (object) { if (object.type === 'tree') { - trees.push({ sha: object.sha, path: path + '/' - + encodeURI(object.path) }); + trees.push({ sha: object.sha, path: path + + '/' + encodeURI(object.path) }); } else if (object.path.substr(-8) === '.user.js') { if (!repos[that.repo]) { repos[that.repo] = nil(); } repos[that.repo][object.path] = path + '/' + encodeURI(object.path); @@ -157,20 +157,20 @@ Repo.prototype.parseTree = function (tree, path, done) { async.each(trees, function(tree, cb) { that.getTree(tree.sha, tree.path, cb); }, function () { - done(); + done(); }); }; // Gets information about a directory Repo.prototype.getTree = function (sha, path, cb) { var that = this; - fetchJSON('/repos/' + encodeURI(this.user) + '/' + encodeURI(this.repo) - + '/git/trees/' + sha, + fetchJSON('/repos/' + encodeURI(this.user) + '/' + encodeURI(this.repo) + + '/git/trees/' + sha, function (json) { that.parseTree(json.tree, path, cb); }); }; -exports.getManager = function (userId, user, repos) { - return new RepoManager(userId, user, repos); +exports.getManager = function (userId, user, repos) { + return new RepoManager(userId, user, repos); }; diff --git a/package.json b/package.json index 00127a3e3..59af1ba0a 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,15 @@ "passport-steam": "*", "passport-tumblr": "*" }, + "devDependencies": { + "grunt": "~0.4", + "grunt-contrib-clean": "~0.5", + "grunt-contrib-copy": "~0.5", + "grunt-contrib-cssmin": "~0.9", + "grunt-contrib-htmlmin": "~0.3", + "grunt-contrib-jshint": "~0.10", + "grunt-contrib-uglify": "~0.4" + }, "repository": { "type": "git", "url": "https://github.com/OpenUserJs/OpenUserJS.org.git" diff --git a/tasks/clean.js b/tasks/clean.js new file mode 100644 index 000000000..d5371d5e3 --- /dev/null +++ b/tasks/clean.js @@ -0,0 +1,3 @@ +module.exports = { + main: ['builds'], +}; diff --git a/tasks/copy.js b/tasks/copy.js new file mode 100644 index 000000000..c5bc4a46c --- /dev/null +++ b/tasks/copy.js @@ -0,0 +1,61 @@ +module.exports = { + main: { + files: [ + + //root files + { + expand: true, + cwd: '', + src: 'app.js', + dest: 'builds/' + }, + { + expand: true, + cwd: '', + src: 'fakes3.sh', + dest: 'builds/' + }, + //libs + { + expand: true, + cwd: 'node_modules/', + src: '**/lib/*.js', + dest: 'builds/node_modules/' + }, + { + expand: true, + cwd: 'libs/', + src: '**/*', + dest: 'builds/libs/' + }, + //public + //css in tasks/cssmin.js + { + expand: true, + cwd: 'public/images', + src: '**/*', + dest: 'builds/public/images' + }, + { + expand: true, + cwd: 'public/js', + src: '**/*.js', + dest: 'builds/public/js' + }, + //mvc + { + expand: true, + cwd: 'controllers/', + src: '**/*', + dest: 'builds/controllers/' + }, + { + expand: true, + cwd: 'models/', + src: '**/*', + dest: 'builds/models/' + } + //html in tasks/htmlmin.js + ] + } +}; diff --git a/tasks/cssmin.js b/tasks/cssmin.js new file mode 100644 index 000000000..74e7331b4 --- /dev/null +++ b/tasks/cssmin.js @@ -0,0 +1,10 @@ +module.exports = { + minify: { + files: [{ + expand: true, + cwd: 'public/css', + src: '**/*.css', + dest: 'builds/public/css' + }] + } +}; diff --git a/tasks/htmlmin.js b/tasks/htmlmin.js new file mode 100644 index 000000000..4844986bb --- /dev/null +++ b/tasks/htmlmin.js @@ -0,0 +1,14 @@ +module.exports = { + dist: { + options: { + removeComments: true, + collapseWhitespace: true + }, + files: [{ + expand: true, + cwd: 'views/', + src: '**/*.html', + dest: 'builds/views/' + }] + } +}; diff --git a/tasks/jshint.js b/tasks/jshint.js new file mode 100644 index 000000000..02613d4c6 --- /dev/null +++ b/tasks/jshint.js @@ -0,0 +1,6 @@ +module.exports = { + files: [ + 'libs/**.js', + 'models/**.js' + ] +}; diff --git a/tasks/uglify.js b/tasks/uglify.js new file mode 100644 index 000000000..7a4122385 --- /dev/null +++ b/tasks/uglify.js @@ -0,0 +1,13 @@ +module.exports = { + options: { + //banner: '/* <%= pkg.name %>.<%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd") %>) <%= pkg.repository.url %> */' + }, + my_target: { + // files: [{ + // expand: true, + // cwd: 'public/js', + // src: '**/*.js', + // dest: 'builds/public/js' + // }] + } +};