)
10 |
11 | 4. Install the latest version of ChefDK ()
12 |
13 | 5. Install the latest version of Node.js ()
14 |
15 | 6. Install the Berkshelf, Omnibus, and VBGuest plugins for VirtualBox:
16 |
17 | For Mac:
18 |
19 | `NOKOGIRI_USE_SYSTEM_LIBRARIES=1 vagrant plugin install vagrant-berkshelf vagrant-omnibus vagrant-vbguest`
20 |
21 | For Windows:
22 |
23 | `vagrant plugin install vagrant-berkshelf vagrant-omnibus vagrant-vbguest`
24 |
25 | 7. Install global dependencies:
26 |
27 | `npm install -g grunt-cli bower pm2 stylus`
28 |
29 | 8. Install local dependencies:
30 |
31 | `npm install`
32 |
33 | 9. Spin up your Vagrant VM:
34 |
35 | `vagrant up`
36 |
37 | ## Grunt Tasks
38 | `grunt --help` lists available tasks.
39 |
40 | * `grunt test` - Runs unit tests (default)
41 | * `grunt analyze` - Validates code style
42 | * `grunt status` - Shows status of node processes on Vagrant VM
43 | * `grunt stop` - Stop node processes on Vagrant VM
44 | * `grunt start` - Start node processes on Vagrant VM
45 | * `grunt restart` - Restart node processes on Vagrant VM
46 | * `grunt logs` - Tail logs for all node processes on Vagrant VM
47 |
48 | ## Vagrant Commands
49 | `vagrant --help` lists available commands.
50 |
51 | * `vagrant status` - Display status of the VM
52 | * `vagrant up` - Power up, un-pause, or create the VM (dependent on status)
53 | * `vagrant destroy` - Delete the VM
54 | * `vagrant halt` - Power down the VM
55 | * `vagrant suspend` - Pause the VM
56 | * `vagrant reload` - Reboot the VM
57 | * `vagrant ssh` - Open an SSH connection into the VM
58 | * `vagrant provision` - (Re)provision the VM
59 | * `vagrant destroy -f && vagrant up` - Rebuild VM
60 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | VM = 'studiovictory'
5 |
6 | Vagrant.configure(2) do |config|
7 | # Selecting Cent OS 7.0 Box
8 | config.vm.box = 'chef/centos-7.0'
9 | config.vbguest.auto_update = false
10 |
11 | # Proxy network, Node: 3000 & Apache: 9898
12 | config.vm.network :forwarded_port, guest: 3000, host: 9898, auto_correct: true
13 | config.vm.network :forwarded_port, guest: 22, host: 2200, id: "ssh", disabled: "true"
14 | config.vm.network :forwarded_port, guest: 22, host: 2201
15 |
16 | # Virtualbox setup
17 | config.vm.define VM
18 | config.vm.provider :virtualbox do |vb|
19 | vb.name = VM
20 | end
21 |
22 | # Fixing issue: stdin is not a tty
23 | ssh_fix = 'bash -c "BASH_ENV=/etc/profile exec bash"'
24 | config.ssh.shell = ssh_fix unless ARGV[0] == 'ssh'
25 |
26 | # Adding omnibus & berkshelf plugins
27 | config.omnibus.chef_version = :latest
28 | config.berkshelf.enabled = true
29 | config.berkshelf.berksfile_path = './Berksfile'
30 |
31 | # Chef provisioning
32 | config.vm.provision :chef_solo do |chef|
33 | chef.add_recipe VM
34 | chef.custom_config_path = 'Vagrantfile.chef'
35 | chef.json = {
36 | :nodejs => {
37 | :install_method => "package",
38 | :npm => "2.13.4"
39 | }
40 | }
41 | end
42 |
43 | # Start node app
44 | config.vm.provision :shell do |s|
45 | s.privileged = false
46 | s.inline = 'cd /vagrant && pm2 start pm2.json'
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/Vagrantfile.chef:
--------------------------------------------------------------------------------
1 | Chef::Config.ssl_verify_mode = :verify_peer
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "StudioVictory",
3 | "version": "0.0.1",
4 | "author": "Carlos Santana",
5 | "license": "MIT",
6 | "dependencies": {
7 | "angular": "~1.4.3",
8 | "font-awesome": "latest",
9 | "lodash": "2.4.1",
10 | "normalize-css": "latest",
11 | "requirejs": "latest"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/cookbooks/studiovictory/attributes/default.rb:
--------------------------------------------------------------------------------
1 | default['nodejs']['packages'] = %w(nodejs npm)
2 | default['npm']['packages'] = {
3 | 'pm2' => '0.14.6',
4 | 'stylus' => '0.52.0',
5 | 'grunt-cli' => '0.1.13',
6 | 'bower' => '1.4.1',
7 | 'mocha' => '2.2.5'
8 | }
9 |
--------------------------------------------------------------------------------
/cookbooks/studiovictory/metadata.rb:
--------------------------------------------------------------------------------
1 | name 'studiovictory'
2 | maintainer 'Carlos Santana'
3 | maintainer_email 'carlos@milkzoft.com'
4 | license 'MIT'
5 | description 'Installs/Configures studiovictory'
6 | version '0.0.1'
7 |
8 | depends 'redisio'
9 | depends 'nodejs'
10 |
--------------------------------------------------------------------------------
/cookbooks/studiovictory/recipes/default.rb:
--------------------------------------------------------------------------------
1 | include_recipe 'redisio'
2 | include_recipe 'redisio::enable'
3 | include_recipe 'yum-epel'
4 |
5 | node['nodejs']['packages'].each do |node_pkg|
6 | package node_pkg
7 | end
8 |
9 | node['npm']['packages'].each do |pkg, ver|
10 | nodejs_npm pkg do
11 | version ver
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/grunt/grunt-server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(grunt) {
4 | grunt.registerTask('status', 'Shows status of node processes', ['shell:vagrantStatus']);
5 | grunt.registerTask('stop', 'Stop node processes', ['shell:vagrantStop']);
6 | grunt.registerTask('start', 'Start node processes', ['shell:vagrantStart']);
7 | grunt.registerTask('restart', 'Restart node processes', ['stop', 'start']);
8 | grunt.registerTask('logs', 'Tail logs for all pm2 processes', ['shell:vagrantLogs']);
9 | };
10 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(config) {
4 | config.set({
5 | basePath: '',
6 |
7 | frameworks: ['mocha', 'chai', 'sinon'],
8 |
9 | files: [
10 | // Dependencies
11 | 'src/public/bower_components/lodash/dist/lodash.min.js',
12 |
13 | // Tests files
14 | 'test/public/js/**/*Test.js'
15 | ],
16 |
17 | exclude: [],
18 |
19 | preprocessors: {
20 | 'test/fixtures/public/js/**/*.html': ['html2js'],
21 | 'test/fixtures/public/js/**/*.json': ['html2js']
22 | },
23 |
24 | reporters: ['mocha'],
25 |
26 | port: 9876,
27 |
28 | colors: true,
29 |
30 | logLevel: config.LOG_INFO,
31 |
32 | autoWatch: false,
33 |
34 | browsers: ['PhantomJS'],
35 |
36 | singleRun: true,
37 |
38 | client: {
39 | captureConsole: true
40 | }
41 | });
42 | };
43 |
--------------------------------------------------------------------------------
/logs/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeJobs/StudioVictory/464333ec84851bb99832119e3d476385c0dcbb57/logs/.gitkeep
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "StudioVictory",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "preinstall": "npm prune",
7 | "postinstall": "bower prune; bower install; grunt githooks"
8 | },
9 | "author": "Carlos Santana",
10 | "license": "MIT",
11 | "repository": {
12 | "type": "git",
13 | "url": "git://github.com/CodeJobs/StudioVictory.git"
14 | },
15 | "dependencies": {
16 | "body-parser": "~1.10.2",
17 | "cookie-parser": "~1.3.3",
18 | "debug": "~2.1.1",
19 | "express": "~4.11.1",
20 | "express-handlebars": "latest",
21 | "lodash": "latest",
22 | "morgan": "~1.5.1",
23 | "stylus": "latest",
24 | "js-yaml": "latest",
25 | "html-minifier": "latest"
26 | },
27 | "devDependencies": {
28 | "bower": "latest",
29 | "chai": "latest",
30 | "grunt": "latest",
31 | "grunt-contrib-jshint": "latest",
32 | "grunt-contrib-stylus": "latest",
33 | "grunt-githooks": "latest",
34 | "grunt-jscs": "latest",
35 | "grunt-karma": "latest",
36 | "grunt-mocha-test": "latest",
37 | "grunt-shell": "latest",
38 | "karma": "latest",
39 | "karma-chai": "latest",
40 | "karma-html2js-preprocessor": "latest",
41 | "karma-mocha": "latest",
42 | "karma-mocha-reporter": "latest",
43 | "karma-phantomjs-launcher": "latest",
44 | "karma-sinon": "latest",
45 | "mocha": "latest",
46 | "rewire": "latest",
47 | "sinon": "latest"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/pm2.json:
--------------------------------------------------------------------------------
1 | {
2 | "apps": [{
3 | "name" : "CLIENT",
4 | "script" : "./src/app.js",
5 | "port": "9898",
6 | "watch": "./src",
7 | "ignore_watch": ["./src/stylus", "./src/public"],
8 | "watch_options": {
9 | "usePolling": true
10 | },
11 | "error_file": "./logs/app-error.log",
12 | "out_file": "./logs/app-out.log",
13 | "env": {
14 | "NODE_ENV": "development"
15 | }
16 | }]
17 | }
18 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Loading dependencies
4 | var express = require('express');
5 | var path = require('path');
6 |
7 | // Initializing express application
8 | var app = express();
9 |
10 | // Loading Config
11 | var config = require('./lib/config');
12 |
13 | // Body Parser
14 | var bodyParser = require('body-parser');
15 | app.use(bodyParser.json());
16 | app.use(bodyParser.urlencoded({
17 | extended: false
18 | }));
19 |
20 | // Logger
21 | var logger = require('morgan');
22 | app.use(logger('dev'));
23 |
24 | // Cookies / Session
25 | var cookieParser = require('cookie-parser');
26 | var session = require('./lib/helpers/session');
27 |
28 | app.use(cookieParser());
29 | app.use(session);
30 |
31 | // Layout setup
32 | var exphbs = require('express-handlebars');
33 | var hbsHelpers = require('./lib/helpers/handlebars');
34 |
35 | // Stylus setup
36 | var stylus = require('stylus');
37 |
38 | // Compile Stylus on the fly
39 | if (!config().html.css.stylusPrecompile) {
40 | app.use(
41 | stylus.middleware({
42 | src: __dirname + '/stylus',
43 | dest: __dirname + '/public/css',
44 | compile: function(str, path) {
45 | return stylus(str)
46 | .set('filename', path)
47 | .set('compress', config().html.css.compress);
48 | }
49 | })
50 | );
51 | }
52 |
53 | // Handlebars setup
54 | app.engine(config().views.engine, exphbs({
55 | extname: config().views.extension,
56 | defaultLayout: config().views.layout,
57 | layoutsDir: __dirname + '/views/layouts',
58 | partialsDir: __dirname + '/views/partials',
59 | helpers: hbsHelpers
60 | }));
61 |
62 | // View engine setup
63 | app.set('views', path.join(__dirname, 'views'));
64 | app.set('view engine', config().views.engine);
65 | app.use(express.static(path.join(__dirname, 'public')));
66 |
67 | // Sending config to templates
68 | app.use(function(req, res, next) {
69 | res.locals.config = config();
70 | next();
71 | });
72 |
73 | // Disabling x-powered-by
74 | app.disable('x-powered-by');
75 |
76 | require('./router')(app);
77 |
78 | // Export application or start the server
79 | if (!!module.parent) {
80 | module.exports = app;
81 | } else {
82 | app.listen(config().serverPort);
83 | }
84 |
--------------------------------------------------------------------------------
/src/config/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | production: &default
3 | serverPort: 3000
4 | baseUrl: http://www.studiovictory.com
5 | baseApi: /api/
6 | views:
7 | engine: .hbs
8 | extension: .hbs
9 | layout: main
10 | html:
11 | minify: true
12 | css:
13 | compress: true
14 | stylusPrecompile: true
15 | controllers:
16 | default: home
17 | languages:
18 | default: en
19 | list: [en, es]
20 | security:
21 | secret: stud10v1ct0ry.com
22 | session:
23 | cookieDomain: .studiovictory.com
24 | maxAge: 86400
25 | cookiePrefix: 'svSession_'
26 | path: '/'
27 | httpOnly: true
28 |
29 | stage: &stage
30 | <<: *default
31 | baseUrl: http://stage.studiovictory.com
32 |
33 | latest: &latest
34 | <<: *stage
35 | baseUrl: http://latest.studiovictory.com
36 |
37 | development:
38 | <<: *latest
39 | baseUrl: http://local.studiovictory.com
40 | html:
41 | minify: false
42 | css:
43 | compress: false
44 | stylusPrecompile: false
45 |
--------------------------------------------------------------------------------
/src/content/i18n/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "site": {
3 | "language": "en",
4 | "title": "StudioVictory - English",
5 | "meta": {
6 | "abstract": "Learn to code",
7 | "description": "Learn to code",
8 | "keywords": "HTML5, JavaScript, Node.js"
9 | }
10 | },
11 | "welcome": "Welcome to",
12 | "visited": "You have visited this site",
13 | "time": "time",
14 | "times": "times"
15 | }
16 |
--------------------------------------------------------------------------------
/src/content/i18n/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "site": {
3 | "language": "en",
4 | "title": "StudioVictory - Español",
5 | "meta": {
6 | "abstract": "Aprende a programar",
7 | "description": "Aprende a programar",
8 | "keywords": "HTML5, JavaScript, Node.js"
9 | }
10 | },
11 | "welcome": "Bienvenido a",
12 | "visited": "Usted ha visitado este sitio",
13 | "time": "vez",
14 | "times": "veces"
15 | }
16 |
--------------------------------------------------------------------------------
/src/controllers/home.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | router.get('/', function(req, res, next) {
5 | var visits = res.session('visits') || 0;
6 |
7 | res.session('visits', ++visits);
8 |
9 | res.render('home/welcome', {
10 | siteName: 'StudioVictory',
11 | visits: visits
12 | });
13 | });
14 |
15 | module.exports = router;
16 |
--------------------------------------------------------------------------------
/src/lib/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var yaml = require('js-yaml');
5 | var environment = require('./environment');
6 | var config = yaml.safeLoad(fs.readFileSync(__dirname + '/../config/config.yml', 'utf-8'));
7 |
8 | module.exports = function() {
9 | return config[environment().name] || {};
10 | };
11 |
--------------------------------------------------------------------------------
/src/lib/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function() {
4 | return {
5 | name: process.env.NODE_ENV ? process.env.NODE_ENV : 'production'
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/helpers/handlebars.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var config = require('../config');
4 | var minify = require('html-minifier').minify;
5 | var utils = require('./utils');
6 |
7 | module.exports = {
8 | // String helpers
9 | debug: function(variable) {
10 | console.log('Debugging Handlebars:');
11 | console.log('=====================');
12 | console.log(this);
13 |
14 | console.log('Dumping Variable:');
15 | console.log('========================');
16 | console.log(variable);
17 | },
18 |
19 | lowercase: function(str) {
20 | return str.toLowerCase();
21 | },
22 |
23 | uppercase: function(str) {
24 | return str.toUpperCase();
25 | },
26 |
27 | reverse: function(str) {
28 | return str.split('').reverse().join('');
29 | },
30 |
31 | // Numbers helpers
32 | ceil: function(number) {
33 | return Math.ceil(parseFloat(number));
34 | },
35 |
36 | // Date helpers
37 | now: function() {
38 | return new Date();
39 | },
40 |
41 | // Conditionals helpers
42 | is: function(variable, value, options) {
43 | if (variable && variable === value) {
44 | return options.fn(this);
45 | } else {
46 | return options.inverse(this);
47 | }
48 | },
49 |
50 | isNot: function(variable, value, options) {
51 | if (!variable || variable !== value) {
52 | return options.fn(this);
53 | } else {
54 | return options.inverse(this);
55 | }
56 | },
57 |
58 | gt: function(value1, value2, options) {
59 | if (value1 > value2) {
60 | return options.fn(this);
61 | } else {
62 | return options.inverse(this);
63 | }
64 | },
65 |
66 | gte: function(value1, value2, options) {
67 | if (value1 >= value2) {
68 | return options.fn(this);
69 | } else {
70 | return options.inverse(this);
71 | }
72 | },
73 |
74 | lt: function(value1, value2, options) {
75 | if (value1 < value2) {
76 | return options.fn(this);
77 | } else {
78 | return options.inverse(this);
79 | }
80 | },
81 |
82 | lte: function(value1, value2, options) {
83 | if (value1 <= value2) {
84 | return options.fn(this);
85 | } else {
86 | return options.inverse(this);
87 | }
88 | },
89 |
90 | // HTML & Json helpers
91 | json: function(content) {
92 | return JSON.stringify(content);
93 | },
94 |
95 | minify: function(content) {
96 | if (config().html.minify) {
97 | return minify(content.fn(this), {
98 | removeComments: true,
99 | collapseWhitespace: true,
100 | minifyJS: true
101 | });
102 | }
103 |
104 | return content.fn(this);
105 | }
106 | };
107 |
--------------------------------------------------------------------------------
/src/lib/helpers/i18n.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var config = require('../config');
4 | var utils = require('./utils');
5 | var _ = require('lodash');
6 |
7 | module.exports = {
8 | load: function(language) {
9 | var content;
10 |
11 | if (_.contains(config().languages.list, language)) {
12 | try {
13 | content = require('../../content/i18n/' + language);
14 | } catch (e) {
15 | content = require('../../content/i18n/' + config().languages.default);
16 | }
17 | } else {
18 | content = require('../../content/i18n/' + config().languages.default);
19 | }
20 |
21 | return content;
22 | },
23 |
24 | getCurrentLanguage: function(url) {
25 | var params = utils.getParamsFromUrl(url);
26 |
27 | if (_.contains(config().languages.list, params[0])) {
28 | return params[0];
29 | } else {
30 | return config().languages.default;
31 | }
32 | },
33 |
34 | getLanguagePath: function(url) {
35 | var params = utils.getParamsFromUrl(url);
36 |
37 | if (_.contains(config().languages.list, params[0])) {
38 | return '/' + params[0];
39 | } else {
40 | return '';
41 | }
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/src/lib/helpers/security.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var config = require('../config');
4 | var crypto = require('crypto');
5 | var salt = config().security.secret;
6 |
7 | module.exports = {
8 | sha1: function(str) {
9 | return crypto.createHash('sha1').update(salt + str.toString()).digest('hex');
10 | },
11 |
12 | md5: function(str) {
13 | return crypto.createHash('md5').update(salt + str.toString()).digest('hex');
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/src/lib/helpers/session.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var _ = require('lodash');
4 | var utils = require('./utils');
5 | var config = require('../config');
6 |
7 | module.exports = function(req, res, next) {
8 | var configData = config().session;
9 | var cookiePrefix = configData.cookiePrefix;
10 | var sessionData = parseSession();
11 |
12 | var options = {
13 | domain: configData.cookieDomain,
14 | path: configData.path,
15 | maxAge: configData.maxAge,
16 | httpOnly: configData.httpOnly
17 | };
18 |
19 | var deleteOptions = {
20 | domain: configData.cookieDomain,
21 | path: configData.path,
22 | httpOnly: configData.httpOnly
23 | };
24 |
25 | res.session = session;
26 | res.clearSession = clearSession;
27 | res.destroySessions = destroySessions;
28 |
29 | next();
30 |
31 | function parseSession() {
32 | var rVal = {};
33 |
34 | _.forEach(req.cookies, function(value, key) {
35 | var sessionPrefix = new RegExp('^' + cookiePrefix);
36 | var isSessionCookie = key.search(sessionPrefix) !== -1;
37 |
38 | if (isSessionCookie) {
39 | key = key.replace(sessionPrefix, '');
40 |
41 | if (utils.isJson(value)) {
42 | value = JSON.parse(value);
43 | }
44 |
45 | rVal[key] = value;
46 | }
47 | });
48 |
49 | return rVal;
50 | }
51 |
52 | function session(key, value) {
53 | var domain;
54 | var cookieKey;
55 | var cookieValue;
56 |
57 | // required params missing
58 | if (!key && (typeof value === 'undefined' || value === null)) {
59 | return sessionData;
60 | }
61 |
62 | // retrieve value
63 | if (!value) {
64 | return sessionData[key];
65 | }
66 |
67 | // set value
68 | sessionData[key] = value;
69 |
70 | // set cookie
71 | cookieKey = cookiePrefix + key;
72 | cookieValue = typeof value === 'string' ? value : JSON.stringify(value);
73 |
74 | res.cookie(cookieKey, cookieValue, options);
75 | }
76 |
77 | function clearSession(keys) {
78 | var cookieKey;
79 | var key = keys;
80 |
81 | if (keys instanceof Array) {
82 | _.forEach(keys, function(key) {
83 | delete sessionData[key];
84 | cookieKey = cookiePrefix + key;
85 | res.clearCookie(cookieKey, deleteOptions);
86 | });
87 | } else {
88 | delete sessionData[key];
89 | cookieKey = cookiePrefix + key;
90 | res.clearCookie(cookieKey, deleteOptions);
91 | }
92 | }
93 |
94 | function destroySessions() {
95 | if (sessionData) {
96 | var cookieKey;
97 |
98 | _.forEach(sessionData, function(value, key) {
99 | delete sessionData[key];
100 |
101 | cookieKey = cookiePrefix + key;
102 | res.clearCookie(cookieKey, deleteOptions);
103 | });
104 | } else {
105 | return;
106 | }
107 | }
108 | };
109 |
--------------------------------------------------------------------------------
/src/lib/helpers/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var config = require('../config');
4 | var security = require('./security');
5 |
6 | module.exports = {
7 | md5: function(str) {
8 | if (this.isDefined(str)) {
9 | return security.md5(str);
10 | }
11 |
12 | return false;
13 | },
14 |
15 | sha1: function(str) {
16 | if (this.isDefined(str)) {
17 | return security.sha1(str);
18 | }
19 |
20 | return false;
21 | },
22 |
23 | encrypt: function(str) {
24 | return security.sha1(security.md5(str));
25 | },
26 |
27 | hash: function(str) {
28 | if (this.isDefined(str) && config().encryptInputs) {
29 | return security.md5(str);
30 | }
31 |
32 | return false;
33 | },
34 |
35 | isYear: function(year) {
36 | return (typeof year !== 'undefined' && year.length === 4 && !isNaN(year));
37 | },
38 |
39 | isMonth: function(month) {
40 | return (typeof month !== 'undefined' && month.length === 2 && !isNaN(month) && month <= 12);
41 | },
42 |
43 | isDay: function(day) {
44 | return (typeof day !== 'undefined' && day.length === 2 && !isNaN(day) && day <= 31);
45 | },
46 |
47 | isDesktop: function(ua) {
48 | return !(/mobile/i.test(ua));
49 | },
50 |
51 | isMobile: function(ua) {
52 | return (/mobile/i.test(ua));
53 | },
54 |
55 | getCurrentDevice: function(ua) {
56 | return (/mobile/i.test(ua)) ? 'mobile' : 'desktop';
57 | },
58 |
59 | isFunction: function(func) {
60 | return (typeof func === 'function') ? true : false;
61 | },
62 |
63 | isDefined: function(variable) {
64 | return (typeof variable !== 'undefined') ? true : false;
65 | },
66 |
67 | isUndefined: function(variable) {
68 | return (typeof variable === 'undefined') ? true : false;
69 | },
70 |
71 | isNumber: function(number) {
72 | return !isNaN(number) ? true : false;
73 | },
74 |
75 | getParamsFromUrl: function(params) {
76 | params = params.split('/');
77 | params.shift();
78 |
79 | return params;
80 | },
81 |
82 | randomCode: function(max, charSet) {
83 | var randomCode = '';
84 | var randomPoz;
85 |
86 | max = max || 12;
87 | charSet = charSet || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
88 |
89 | for (var i = 0; i < max; i++) {
90 | randomPoz = Math.floor(Math.random() * charSet.length);
91 | randomCode += charSet.substring(randomPoz, randomPoz + 1);
92 | }
93 |
94 | return randomCode;
95 | },
96 |
97 | removeHTML: function(str) {
98 | if (this.isDefined(str)) {
99 | return str.replace(/(<([^>]+)>)/ig, '');
100 | }
101 |
102 | return false;
103 | },
104 |
105 | escape: function(str) {
106 | if (this.isDefined(str)) {
107 | return str
108 | .replace(/'/g, '\\\'')
109 | .replace(/"/g, '\\\\"')
110 | .replace(/&/g, '&')
111 | .replace(//g, '>');
113 | }
114 |
115 | return false;
116 | },
117 |
118 | clean: function(str) {
119 | if (this.isDefined(str)) {
120 | return this.removeHTML(str).replace(/[`ª´·¨Ç~¿!#$%^&*()_|+\-=?;'",<>\{\}\[\]\\]/gi, '');
121 | }
122 |
123 | return false;
124 | },
125 |
126 | convertSecondsToHHMMSS: function(seconds) {
127 | if (!seconds) {
128 | return '00:00:00';
129 | }
130 |
131 | var time;
132 | var hours = Math.floor(seconds / 3600);
133 | var minutes = Math.floor((seconds - (hours * 3600)) / 60);
134 |
135 | seconds = seconds - (hours * 3600) - (minutes * 60);
136 |
137 | if (hours < 10) {
138 | hours = '0' + hours;
139 | }
140 |
141 | if (minutes < 10) {
142 | minutes = '0' + minutes;
143 | }
144 |
145 | if (seconds < 10) {
146 | seconds = '0' + seconds;
147 | }
148 |
149 | time = hours + ':' + minutes + ':' + seconds;
150 |
151 | return time;
152 | },
153 |
154 | convertCamelToNatural: function(str) {
155 | str = str.charAt(0).toUpperCase() + str.slice(1);
156 |
157 | return str.split(/(?=[A-Z])/).join(' ');
158 | },
159 |
160 | isJson: function(str) {
161 | if (str === null) {
162 | return false;
163 | }
164 |
165 | try {
166 | JSON.parse(str);
167 | } catch (e) {
168 | return false;
169 | }
170 |
171 | return true;
172 | }
173 | };
174 |
--------------------------------------------------------------------------------
/src/public/images/articles/article1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeJobs/StudioVictory/464333ec84851bb99832119e3d476385c0dcbb57/src/public/images/articles/article1.jpg
--------------------------------------------------------------------------------
/src/public/images/articles/article2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeJobs/StudioVictory/464333ec84851bb99832119e3d476385c0dcbb57/src/public/images/articles/article2.jpg
--------------------------------------------------------------------------------
/src/public/images/layout/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeJobs/StudioVictory/464333ec84851bb99832119e3d476385c0dcbb57/src/public/images/layout/favicon.png
--------------------------------------------------------------------------------
/src/public/images/layout/logo-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeJobs/StudioVictory/464333ec84851bb99832119e3d476385c0dcbb57/src/public/images/layout/logo-black.png
--------------------------------------------------------------------------------
/src/public/images/layout/logo_@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeJobs/StudioVictory/464333ec84851bb99832119e3d476385c0dcbb57/src/public/images/layout/logo_@1x.png
--------------------------------------------------------------------------------
/src/public/images/layout/logo_@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeJobs/StudioVictory/464333ec84851bb99832119e3d476385c0dcbb57/src/public/images/layout/logo_@2x.png
--------------------------------------------------------------------------------
/src/public/images/layout/scroll-to-top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeJobs/StudioVictory/464333ec84851bb99832119e3d476385c0dcbb57/src/public/images/layout/scroll-to-top.png
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var config = require('./lib/config');
4 | var availableLanguages = config().languages.list.join('|');
5 | var defaultController;
6 | var homeController;
7 |
8 | module.exports = function(app) {
9 | // Loading controllers
10 | defaultController = require('./controllers/' + config().controllers.default);
11 | homeController = require('./controllers/home');
12 |
13 | // Loading necessary helpers
14 | var i18n = require('./lib/helpers/i18n');
15 | var utils = require('./lib/helpers/utils');
16 |
17 | // Sending variables to templates
18 | app.use(function(req, res, next) {
19 | res.locals.isMobile = utils.isMobile(req.headers['user-agent']);
20 | res.locals.config.basePath = config().baseUrl + i18n.getLanguagePath(req.url);
21 | res.locals.currentLanguage = i18n.getCurrentLanguage(req.url);
22 | res.__ = res.locals.__ = i18n.load(i18n.getCurrentLanguage(req.url));
23 | next();
24 | });
25 |
26 | // Default css & js files
27 | app.use(function(req, res, next) {
28 | res.locals.css = [
29 | '/css/style.css'
30 | ];
31 |
32 | res.locals.js = [];
33 |
34 | next();
35 | });
36 |
37 | // Controller dispatch
38 | app.use('/', defaultController);
39 | app.use('/:language(' + availableLanguages + ')', defaultController);
40 | app.use('/:language(' + availableLanguages + ')/home', homeController);
41 |
42 | // catch 404 and forward to error handler
43 | app.use(function(req, res, next) {
44 | var err = new Error('Not Found');
45 | err.status = 404;
46 | next(err);
47 | });
48 |
49 | // error handlers
50 | if (app.get('env') === 'development') {
51 | app.use(function(err, req, res, next) {
52 | res.status(err.status || 500);
53 | res.render('error', {
54 | message: err.message,
55 | error: err
56 | });
57 | });
58 | }
59 |
60 | app.use(function(err, req, res, next) {
61 | res.status(err.status || 500);
62 | res.render('error', {
63 | message: err.message,
64 | error: {}
65 | });
66 | });
67 | };
68 |
--------------------------------------------------------------------------------
/src/stylus/_global.styl:
--------------------------------------------------------------------------------
1 | body
2 | font('PT Serif', 1em)
3 |
4 | .no-display
5 | display none
6 |
7 | .clear
8 | clear both
9 |
10 | .btn
11 | borderRadius(5px)
12 | color grayColor(10)
13 | display inline-block
14 | font-size .875rem
15 | padding 10px
16 |
17 | #editor
18 | width 90%
19 | min-height 280px
20 | margin-left 5%
21 |
22 |
--------------------------------------------------------------------------------
/src/stylus/_variables.styl:
--------------------------------------------------------------------------------
1 | /* Vendors Prefixes */
2 | vendor-prefixes = o ms moz webkit khtml official
3 |
4 | /* General Colors */
5 | graysColors = (#010 #111 #222 #333 #444 #555 #666 #777 #888 #999 #AAA #BBB #CCC #DDD #EEE)
6 | white = #FFF
7 | darkWhite = #FEFEFE
8 | almostWhite = #F5F5F5
9 | black = #000
10 | darkBlue = #069
11 |
12 | /* Dashboard colors */
13 | blueLink = #06F
14 |
15 | /* Site */
16 | blueFacebook = #3B5999
17 | blueBorderFacebook = #193777
18 | blueTwitter = #60A9DE
19 | blueBorderTwitter = #3E87BC
20 | blueLinkedin = #678BD5
21 | blueBorderLinkedin = #1D4D78
22 | redGooglePlus = #DE4B39
23 | redBorderGooglePlus = #B43525
24 | redYoutube = #ED4632
25 | grayGithub = #2E3436
26 | grayBorderGithub = #0C1214
27 |
28 | yellowStar = #f1c40f
29 | orangeBorderStar = #e67e22
30 |
31 | /* Logo & menu options */
32 | grayWhite = #F5F5F5
33 | blueCodejobs = #0073BC
34 | darkBlueCodejobs = #004C88
35 | grayCodejobs = #6B6B6B
36 |
37 | /* Syntax */
38 | darkGraySyntaxBg = #262720
39 | blueSyntax = #00D6FF
40 | pinkSyntax = #F92772
41 | seaBlueSyntax = #0099F5
42 | orangeSyntax = #FFA337
43 | graySyntax = #7B959E
44 | redSyntax = #8D0014
45 | purpleSyntax = #AE81FF
46 | greenSyntax = #8CBC2B
47 | lightYellowSyntax = #FFFFE3
48 | yellowSyntax = #FFF8AB
49 |
50 | /* Technologies */
51 | blueCSS3 = #1CA8E2
52 | blueDocker = #007DA6
53 | blueVagrant = #1475AC
54 | brownMongo = #553924
55 | grayExpress = #666666
56 | greenNode = #569D00
57 | orangeHTML5 = #D14119
58 | purpleBootstrap = #55284A
59 | purplePHP = #455792
60 | redAngular = #CB0323
61 | yellowGrunt = #DD8D00
62 | yellowJS = #DFB300
63 | yellowSublime = #EA8B0F
64 | azureEntrepreneur = #00A0AA
65 | greenGames = #2F5500
66 | blueTesting = #24B9F9
67 | blueWordpress = #1A759D
68 |
69 | /* Buttons */
70 | defaultBg = #E6E6E6
71 | defaultBorder = #ADADAD
72 | primaryBg = #428BCA
73 | primaryBorder = #357EBD
74 | primaryBgHover = #3071A9
75 | primaryBorderHover = #285E8E
76 | sucessBg = #5CB85C
77 | sucessBorder = #27763A
78 | sucessBgHover = #449D44
79 | sucessBorderHover = #398439
80 | infoBg = #3498DB
81 | infoBorder = #17374D
82 | infoBgHover = #1C7DC3
83 | infoBorderHover = #3B68A9
84 | infoDisabledBg = #5BC0DE
85 | infoDisabledBorder = #46B8DA
86 | warningBg = #F0AD4E
87 | warningBorder = #EEA236
88 | warningBgHover = #EC971F
89 | warningBorderHover = #D58512
90 | warningDisabledBg = #F0AD4E
91 | warningDisabledBorder = #EEA236
92 | dangerBg = #DA4D49
93 | dangerBorder = #762727
94 | dangerBgHover = #C9302C
95 | dangerBorderHover = #AC2925
96 | dangerDisabledBg = #D9534F
97 | dangerDisabledBorder = #D43F3A
98 | redPink = #e06161
99 |
100 | /* Font weights */
101 | fontWeightLighter = 100
102 | fontWeightLight = 300
103 | fontWeightNormal = 400
104 | fontWeightRegular = 500
105 | fontWeightBold = 700
106 | fontWeightHeavy = 900
107 |
108 | /* Mediaqueries */
109 | iPadW = 768px
110 | iPadH = 1024px
111 | iP4W = 320px
112 | iP4H = 480px
113 | iP5W = 320px
114 | iP5H = 568px
115 | iP6W = 375px
116 | iP6H = 667px
117 | iP6PW = 414px
118 | iP6PH = 736px
119 | gW = 360px
120 | gH = 640px
121 | all = 1023px
122 |
123 | s = 'only screen and'
124 | l = 'orientation: landscape'
125 | maxW = 'max-device-width:'
126 | minW = 'min-device-width:'
127 |
128 | /* Devices */
129 | anyDevice = s + ' (' + maxW + all + ')'
130 | anyDeviceLandscape = s + ' (' + maxW + all + ') and (' + l + ')'
131 | galaxy = s + ' (' + minW + gW + ') and (' + maxW + gH + ')'
132 | galaxyLandscape = s + ' (' + minW + gW + ') and (' + maxW + gH + ') and (' + l + ')'
133 | iPad = s + ' (' + minW + iPadW + ') and (' + maxW + iPadH + ')'
134 | iPadLandscape = s + ' (' + minW + iPadW + ') and (' + maxW + iPadH + ') and (' + l + ')'
135 | iPhone6P = s + ' (' + minW + iP4W + ') and (' + maxW + iP4H + ')'
136 | iPhone6PLandscape = s + ' (' + minW + iP6PW + ') and (' + maxW + iP6PH + ') and (' + l + ')'
137 | iPhone6 = s + ' (' + minW + iP6W + ') and (' + maxW + iP6H + ')'
138 | iPhone6Landscape = s + ' (' + minW + iP6W + ') and (' + maxW + iP6H + ') and (' + l + ')'
139 | iPhone5 = s + ' (' + minW + iP5W + ') and (' + maxW + iP5H + ')'
140 | iPhone5Landscape = s + ' (' + minW + iP5W + ') and (' + maxW + iP5H + ') and (' + l + ')'
141 | iPhone4 = s + ' (' + minW + iP4W + ') and (' + maxW + iP4H + ')'
142 | iPhone4Landscape = s + ' (' + minW + iP4W + ') and (' + maxW + iP4H + ') and (' + l + ')'
143 |
--------------------------------------------------------------------------------
/src/stylus/blog/_post.styl:
--------------------------------------------------------------------------------
1 | .post-content
2 | centerWithMargin(0, false, false, 40px)
3 | width 90%
4 |
5 | h1
6 | font-size 2.5rem
7 | margin 20px
8 | text-align center
9 |
10 | h2
11 | font-size 2rem
12 | margin 20px
13 | margin-left 5%
14 |
15 | .meta
16 | color grayColor(6)
17 | font-style italic
18 | margin-bottom 20px
19 | text-align center
20 |
21 | .main-image
22 | centerWithMargin(0, false, false, 20px)
23 | display block
24 | width 90%
25 |
26 | p
27 | centerWithMargin()
28 | font-size 1.1rem
29 | line-height 2.2rem
30 | padding-bottom 10px
31 | width 90%
32 | a
33 | color grayColor(6)
34 | font-weight bold
35 | text-decoration none
36 | &:hover
37 | padding-bottom 2px
38 | border-bottom 1px dotted grayColor(12)
39 | img
40 | width 100%
41 |
42 | ul
43 | margin-left 5%
44 | li
45 | list-style none
46 | ul
47 | margin-left -20px
48 | li
49 | &:before
50 | color grayColor(6)
51 |
52 | &:before
53 | font-family 'FontAwesome'
54 | content '\f0da'
55 | margin 0 5px 0 -15px
56 | color grayColor(3)
57 |
--------------------------------------------------------------------------------
/src/stylus/blog/_posts.styl:
--------------------------------------------------------------------------------
1 | .page-content-wrapper
2 | centerWithMargin(50px, false, false, 0)
3 | width 88%
4 |
5 | .columns
6 | prefix(column-count, 3)
7 | prefix(column-gap, 10px)
8 | prefix(column-fill, auto)
9 |
10 | .post
11 | border(grayColor(13))
12 | background -webkit-linear-gradient(45deg, white, almostWhite)
13 | background white
14 | column-break-inside avoid
15 | display inline-block
16 | margin 0 2px 15px
17 | padding 10px
18 | padding-bottom 5px
19 |
20 | img
21 | margin-bottom 5px
22 | padding-bottom 15px
23 | width 100%
24 | iframe
25 | height 250px
26 | margin-bottom 5px
27 | padding-bottom 15px
28 | width 100%
29 | h2
30 | font-size 1.7rem
31 | margin-bottom 10px
32 | a
33 | color grayColor(3)
34 | text-decoration none
35 | &:hover
36 | border-bottom 1px dotted grayColor(12)
37 | p
38 | color grayColor(3)
39 | font 0.7rem / 1.1rem Arial, sans-serif
40 | margin 0
41 |
42 | .details
43 | color grayColor(12)
44 | margin-top 20px
45 | font-weight fontWeightNormal
46 |
47 | .date
48 | float left
49 |
50 | .social
51 | margin-left 10px
52 |
53 | a
54 | color grayColor(12)
55 | text-decoration none
56 |
57 | &.facebook
58 | &:hover
59 | color blueFacebook
60 | &.twitter
61 | &:hover
62 | color blueTwitter
63 | &.linkedin
64 | &:hover
65 | color blueLinkedin
66 | &.google
67 | &:hover
68 | color redGooglePlus
69 |
70 | .category
71 | float right
72 |
73 | a
74 | color grayColor(12)
75 | text-decoration none
76 | &:hover
77 | color grayColor(6)
78 | &.angular
79 | &:hover
80 | color redAngular
81 |
--------------------------------------------------------------------------------
/src/stylus/mediaqueries/desktop.styl:
--------------------------------------------------------------------------------
1 | @import '../mixins/_responsive'
2 |
3 | @media only screen and (max-width: 940px)
4 | changeColumns(2)
5 |
6 | @media only screen and (max-width: 768px)
7 | changeColumns(2)
8 |
9 | header
10 | nav
11 | noDisplay()
12 |
13 | .nav-mobile
14 | centerWithMargin(0, 30px)
15 | display block
16 | text-align center
17 |
18 | i
19 | color grayColor(3)
20 | font-size 24px
21 |
22 | @media only screen and (max-width: 568px)
23 | changeColumns(1)
24 |
25 |
--------------------------------------------------------------------------------
/src/stylus/mixins/_default.styl:
--------------------------------------------------------------------------------
1 | /* Utils */
2 | prefix(property, value)
3 | -webkit-{property} value
4 | -moz-{property} value
5 | -o-{property} value
6 | -ms-{property} value
7 | {property} value
8 |
9 | bg(color = transparent)
10 | background color
11 |
12 | bgImage(url, repeat = no-repeat)
13 | background-image url(url)
14 | background-repeat repeat
15 |
16 | blackRgba(opacity = 1)
17 | background-color rgba(0, 0, 0, opacity)
18 |
19 | cover()
20 | background-size cover
21 |
22 | resetElement()
23 | border none
24 | margin 0
25 | padding 0
26 |
27 | clearfix()
28 | *zoom 1
29 | &:after
30 | content ''
31 | display block
32 | clear both
33 | height 0
34 |
35 | position(position, args...)
36 | position position
37 |
38 | if length(args) == 4
39 | if args[0] != false
40 | top args[0]
41 |
42 | if args[1] != false
43 | right args[1]
44 |
45 | if args[2] != false
46 | bottom args[2]
47 |
48 | if args[3] != false
49 | left args[3]
50 | else if length(args) == 3
51 | if args[0] != false
52 | top args[0]
53 |
54 | if args[1] != false
55 | right args[1]
56 |
57 | if args[2] != false
58 | bottom args[2]
59 | else if length(args) == 2
60 | if args[0] != false
61 | top args[0]
62 |
63 | if args[1] != false
64 | right args[1]
65 | else
66 | if args[0] != false
67 | top args[0]
68 |
69 | font(font, size = false)
70 | font-family font, sans-serif
71 |
72 | if size != false
73 | font-size size
74 |
75 | capitalize()
76 | text-transform capitalize
77 |
78 | uppercase()
79 | text-transform uppercase
80 |
81 | lowercase()
82 | text-transform lowercase
83 |
84 | italic()
85 | font-style italic
86 |
87 | centerImage(width = 100%)
88 | display block
89 | margin 0 auto
90 | width width
91 |
92 | centerWithMargin(initMargin = 0, args...)
93 | margin initMargin auto
94 |
95 | if length(args) == 4
96 | if args[0] != false
97 | margin-top args[0]
98 |
99 | if args[1] != false
100 | margin-right args[1]
101 |
102 | if args[2] != false
103 | margin-bottom args[2]
104 |
105 | if args[3] != false
106 | margin-left args[3]
107 | else if length(args) == 3
108 | if args[0] != false
109 | margin-top args[0]
110 |
111 | if args[1] != false
112 | margin-right args[1]
113 |
114 | if args[2] != false
115 | margin-bottom args[2]
116 | else if length(args) == 2
117 | if args[0] != false
118 | margin-top args[0]
119 |
120 | if args[1] != false
121 | margin-right args[1]
122 | else
123 | if args[0] != false
124 | margin-top args[0]
125 |
126 | hide()
127 | display none
128 |
129 | noContent()
130 | content ''
131 | background transparent
132 |
133 | noBorder()
134 | border none
135 |
136 | noDisplay()
137 | display none
138 |
139 | noSelect()
140 | outline none
141 | touch-callout none
142 | user-select none
143 |
144 | pointer()
145 | cursor pointer
146 |
147 | /* Font Awesome */
148 | fa(code, size = 1em, color = blueDevWay, bgColor = transparent)
149 | background-color bgColor
150 | color color
151 | content code
152 | font-family FontAwesome
153 | font-size size
154 |
155 | /* Borders */
156 | border(color = red, size = 1px, type = solid)
157 | border size type color
158 |
159 | borderRadius(radius = 5px, args...)
160 | if length(args) == 3
161 | border-top-right-radius radius
162 | border-bottom-right-radius args[0]
163 | border-bottom-left-radius args[1]
164 | border-top-left-radius args[2]
165 | else if length(args) == 2
166 | border-top-right-radius radius
167 | border-bottom-right-radius args[0]
168 | border-bottom-left-radius args[1]
169 | else if length(args) == 1
170 | border-top-right-radius radius
171 | border-bottom-right-radius args[0]
172 | else
173 | border-radius radius
174 |
175 | /* BoxShadow */
176 | boxShadow(h = 2px, v = 2px, blur = 2px, opacity = 0.3, inset = false)
177 | if (!inset)
178 | box-shadow h v blur rgba(0, 0, 0, opacity)
179 | else
180 | box-shadow inset h v blur rgba(0, 0, 0, opacity)
181 |
182 | /* Filters */
183 | grayscale(percentage)
184 | filter grayscale(percentage)
185 | -webkit-filter grayscale(percentage)
186 | -o-filter grayscale(percentage)
187 | -moz-filter grayscale(percentage)
188 | -ms-filter grayscale(percentage)
189 |
190 | grayColor(position)
191 | return graysColors[position]
192 |
193 | /* Lists */
194 | resetUl()
195 | margin 0
196 | padding 0
197 | list-style none
198 |
199 | /* Images */
200 | retina(image, extension = 'png')
201 | background-image url(image'.'extension)
202 |
203 | @media (min-device-pixel-ratio: 2),
204 | (min-device-pixel-ratio: 2),
205 | (min-resolution: 192dpi),
206 | (min-resolution: 2dppx)
207 | background-image url(image'_@2x.'extension)
208 | background-size cover
209 |
--------------------------------------------------------------------------------
/src/stylus/mixins/_responsive.styl:
--------------------------------------------------------------------------------
1 | @import '_default'
2 |
3 | changeColumns(count)
4 | .page-content-wrapper
5 | .columns
6 | prefix(column-count, count)
7 |
--------------------------------------------------------------------------------
/src/stylus/mixins/_studiovictory.styl:
--------------------------------------------------------------------------------
1 | /* Logo */
2 | $displayLogo()
3 | border-right 1px solid $lightGray
4 | font-size 26px
5 | line-height 49px
6 | margin 0 auto
7 | margin-left 10px
8 | padding-right 10px
9 | text-transform uppercase
10 | width 175px
11 |
12 | a
13 | $noSelect()
14 |
15 | .code
16 | color $grayCodejobs
17 | display inline-block
18 | vertical-align middle
19 |
20 | .jobs
21 | color $blueCodejobs
22 | display inline-block
23 | font-weight $fontWeightBold
24 | vertical-align middle
25 |
26 | .isotipo
27 | display inline-block
28 | height 25px
29 | vertical-align middle
30 | width 35px
31 |
32 | &:hover
33 | .code
34 | color $blueCodejobs
35 |
36 | .jobs
37 | color $grayCodejobs
--------------------------------------------------------------------------------
/src/stylus/site/_footer.styl:
--------------------------------------------------------------------------------
1 | .scroll-to-top
2 | pointer()
3 | noDisplay()
4 | position(fixed, false, 0px)
5 | background url(../images/layout/scroll-to-top.png) no-repeat
6 | bottom 20px
7 | height 55px
8 | padding-right 1em
9 | width 55px
10 | z-index 9999
11 |
12 | .footer
13 | p
14 | color almostLightGray
15 | font-family Arial, serif
16 | font-size 0.95em
17 | margin-bottom 10px
18 | text-align center
19 |
--------------------------------------------------------------------------------
/src/stylus/site/_header.styl:
--------------------------------------------------------------------------------
1 | header
2 | font('Montserrat')
3 | centerWithMargin()
4 | width 100%
5 |
6 | .logo
7 | centerWithMargin()
8 | width 260px
9 |
10 | img
11 | centerWithMargin()
12 | margin 0
13 | margin-top 10px
14 | padding 0
15 | width 40px
16 |
17 | .logo-text
18 | position(absolute, false)
19 | color grayColor(3)
20 | font-size 2em
21 | height 40px
22 | line-height 55px
23 | margin-left 5px
24 | nav
25 | margin 0 auto
26 | width 70%
27 |
28 | ul
29 | margin-top 20px
30 | text-align center
31 | li
32 | display inline-block
33 | a
34 | color grayColor(3)
35 | margin-left 15px
36 | text-decoration none
37 | &:hover
38 | border-bottom 1px dotted grayColor(10)
39 | padding-bottom 2px
40 |
41 | .nav-mobile-menu
42 | noDisplay()
43 | centerWithMargin()
44 | margin-top 20px
45 | text-align center
46 |
47 | ul
48 | resetUl()
49 | margin-left -10px
50 | li
51 | a
52 | color grayColor(3)
53 | line-height 30px
54 | margin-left 15px
55 | text-decoration none
56 | &:hover
57 | border-bottom 1px dotted grayColor(10)
58 | padding-bottom 2px
59 |
--------------------------------------------------------------------------------
/src/stylus/site/_pagination.styl:
--------------------------------------------------------------------------------
1 | .pagination
2 | font('sans-serif', 12px)
3 | color grayColor(6)
4 | line-height 24px
5 | padding 20px
6 | margin-bottom 20px
7 | text-align center
8 |
9 | .page
10 | borderRadius(3px)
11 | background grayColor(14)
12 | border solid 1px grayColor(13)
13 | box-shadow inset 0px 1px 0px rgba(255, 255, 255, .8), 0px 1px 3px rgba(0, 0, 0, .1)
14 | color grayColor(7)
15 | display inline-block
16 | font-size .875em
17 | font-weight fontWeightBold
18 | margin-right 4px
19 | padding 0px 9px
20 | text-decoration none
21 | text-shadow 0px 1px 0px rgba(255, 255, 255, 1)
22 |
23 | &.active
24 | background grayColor(6)
25 | border none
26 | box-shadow inset 0px 0px 8px rgba(0, 0, 0, .5), 0px 1px 0px rgba(255, 255, 255, .8)
27 | color almostWhite
28 | text-shadow 0px 0px 3px rgba(0, 0, 0, .5)
29 |
30 | &.gradient
31 | background -moz-linear-gradient(0% 0% 270deg,#f8f8f8, #e9e9e9)
32 | background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#f8f8f8), to(#e9e9e9))
33 |
34 | &:hover,
35 | &.gradient
36 | &:hover
37 | background almostWhite
38 | background -webkit-gradient(linear, 0% 0%, 0% 100%, from(grayColor(14)), to(almostWhite))
39 | background -moz-linear-gradient(0% 0% 270deg, grayColor(14), almostWhite)
40 |
--------------------------------------------------------------------------------
/src/stylus/style.styl:
--------------------------------------------------------------------------------
1 | @import url('http://fonts.googleapis.com/css?family=PT+Serif')
2 | @import url('../bower_components/normalize-css/normalize.css')
3 | @import url('../bower_components/font-awesome/css/font-awesome.min.css')
4 |
5 | @import '_variables'
6 | @import 'mixins/_default'
7 | @import 'mixins/_studiovictory'
8 |
9 | @import '_global'
10 |
11 | @import 'site/_header'
12 |
13 | @import 'blog/_posts'
14 |
15 | @import 'site/_pagination'
16 |
17 | @import 'site/_footer'
18 |
19 | @import 'mediaqueries/desktop'
20 |
--------------------------------------------------------------------------------
/src/views/error.hbs:
--------------------------------------------------------------------------------
1 | Error: {{ message }}
2 |
--------------------------------------------------------------------------------
/src/views/home/welcome.hbs:
--------------------------------------------------------------------------------
1 | {{ __.welcome }} {{ siteName }}
2 |
3 |
4 | {{ __.visited }} {{ visits }} {{#is visits 1 }} {{ __.time }} {{else}} {{ __.times }} {{/is}}
5 |
6 |
7 | {{#debug visits }}{{/debug}}
8 |
--------------------------------------------------------------------------------
/src/views/layouts/main.hbs:
--------------------------------------------------------------------------------
1 | {{#minify}}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | {{ __.site.title }}
13 |
14 | {{#if isMobile}}
15 |
16 | {{/if}}
17 |
18 | {{#css}}
19 |
20 | {{/css}}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
StudioVictory
29 |
30 |
31 |
46 |
47 |
58 |
59 |
62 |
63 |
64 | {{{ body }}}
65 |
66 |
70 |
71 |
72 | {{/minify}}
73 |
--------------------------------------------------------------------------------
/test/fixtures/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | foo: &default
3 | a: foo
4 | b: bar
5 | c: baz
6 |
7 | bar:
8 | <<: *default
9 | c: zab
10 |
--------------------------------------------------------------------------------
/test/fixtures/public/js/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeJobs/StudioVictory/464333ec84851bb99832119e3d476385c0dcbb57/test/fixtures/public/js/.gitkeep
--------------------------------------------------------------------------------
/test/lib/configTest.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var testHelper = require('./../testHelper');
4 | var config = testHelper.rewireFromProjectRoot('lib/config');
5 |
6 | // use fixture config
7 | config.__set__('environment', function() {
8 | return { name: 'foo' };
9 | });
10 |
11 | config.__set__('config', testHelper.loadFixtureYML('config.yml'));
12 |
13 | describe('Config', function() {
14 | it('should return an object', function() {
15 | assert.typeOf(config(), 'Object', 'Config returns an object');
16 | });
17 |
18 | it('should return config by environment', function() {
19 | config.__set__('environment', function() {
20 | return { name: 'foo' };
21 | });
22 |
23 | assert.deepEqual(config(), { a: 'foo', b: 'bar', c: 'baz' }, 'Config returns the foo config');
24 |
25 | config.__set__('environment', function() {
26 | return { name: 'bar' };
27 | });
28 |
29 | assert.deepEqual(config(), { a: 'foo', b: 'bar', c: 'zab' }, 'Config returns the bar config');
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/test/public/js/fakeTest.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Give it some context', function() {
4 | describe('maybe a bit more context here', function() {
5 | it('should run here few assertions', function() {
6 | console.log('Testing fake assertions....');
7 | });
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/test/testHelper.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 | var rewire = require('rewire');
5 | var yaml = require('js-yaml');
6 |
7 | global.sinon = require('sinon');
8 | global.assert = require('chai').assert;
9 |
10 | module.exports = {
11 | rewireFromProjectRoot: function(relativePath) {
12 | return rewire(__dirname + '/../src/' + relativePath);
13 | },
14 | loadFixtureYML: function(filename) {
15 | return yaml.safeLoad(fs.readFileSync(__dirname + '/fixtures/' + filename, 'utf8'));
16 | },
17 | loadFixtureJSONRaw: function(filename) {
18 | return fs.readFileSync(__dirname + '/fixtures/' + filename, 'utf8');
19 | },
20 | loadFixtureJSON: function(filename) {
21 | return JSON.parse(this.loadFixtureJSONRaw(filename));
22 | }
23 | };
24 |
--------------------------------------------------------------------------------