...
 
Commits (25)
# 3rd party stuff
/vendor/
/node_modules/
/bower_components/
# compiled by gulp
/app/views/assets/*
/assets/build
/public/css/*
/public/fonts/*
/public/img/*
/public/js/*
# misc files
composer.phar
.env.local.php
.env.php
.DS_Store
Thumbs.db
.vagrant/
.ftpquota
error_log
*.sublime-workspace
.sublime-grunt-cache
/.tmp
# IntelliJ stuff
/.idea/workspace.xml
/.idea/vagrant.xml
/.idea/dictionaries
vetruvet.com
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{jquery}" />
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" />
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/vetruvet.com.iml" filepath="$PROJECT_DIR$/.idea/vetruvet.com.iml" />
</modules>
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpProjectSharedConfiguration" php_language_level="5.5.0" />
</project>
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/node_modules" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="sass-stdlib" level="application" />
<orderEntry type="library" name="jquery" level="application" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions">
<TaskOptions isEnabled="true">
<option name="arguments" value="css" />
<option name="checkSyntaxErrors" value="true" />
<option name="description" value="Compiles .scss files into .css files" />
<option name="exitCodeBehavior" value="ERROR" />
<option name="fileExtension" value="scss" />
<option name="immediateSync" value="false" />
<option name="name" value="SCSS" />
<option name="output" value="$ProjectFileDir$/public/css/$FileNameWithoutExtension$.css" />
<option name="outputFilters">
<array />
</option>
<option name="outputFromStdout" value="false" />
<option name="passParentEnvs" value="true" />
<option name="program" value="gulp" />
<option name="scopeName" value="Project Assets" />
<option name="trackOnlyRoot" value="false" />
<option name="workingDir" value="$ProjectFileDir$" />
<envs />
</TaskOptions>
<TaskOptions isEnabled="true">
<option name="arguments" value="js" />
<option name="checkSyntaxErrors" value="true" />
<option name="description" value="Minifies JS files" />
<option name="exitCodeBehavior" value="ERROR" />
<option name="fileExtension" value="js" />
<option name="immediateSync" value="false" />
<option name="name" value="JS" />
<option name="output" value="$ProjectFileDir$/public/css/$FileNameWithoutExtension$.min.js" />
<option name="outputFilters">
<array />
</option>
<option name="outputFromStdout" value="false" />
<option name="passParentEnvs" value="true" />
<option name="program" value="gulp" />
<option name="scopeName" value="Project Assets" />
<option name="trackOnlyRoot" value="false" />
<option name="workingDir" value="$ProjectFileDir$" />
<envs />
</TaskOptions>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebResourcesPaths">
<contentEntries>
<entry url="file://$PROJECT_DIR$">
<entryData>
<resourceRoots>
<path value="file://$PROJECT_DIR$/public" />
</resourceRoots>
</entryData>
</entry>
</contentEntries>
</component>
</project>
<?php
/** @var Application $app */
$app['debug'] = true;
error_reporting(E_ALL);
ini_set('display_errors', 1);
$app->register(new Silex\Provider\UrlGeneratorServiceProvider());
$app->register(new Silex\Provider\TwigServiceProvider(), array(
'twig.path' => __DIR__.'/views',
));
<?php
use Symfony\Component\HttpFoundation\Request;
class HomeController {
public function home(Request $request, Application $app) {
return $app->render('pages/home.twig');
}
public function portfolio(Request $request, Application $app) {
return $app->json(new Portfolio($app));
}
}
<?php
/** @var Application $app */
$app['twig'] = $app->share($app->extend('twig', function (Twig_Environment $twig, Application $app) {
$twig->addFunction(new Twig_SimpleFunction('asset', function ($path) use ($app) {
return $app->asset($path);
}));
return $twig;
}));
<?php
use Silex\Application as BaseApplication;
class Application extends BaseApplication {
use BaseApplication\TwigTrait;
use BaseApplication\UrlGeneratorTrait;
public function asset($path) {
return $this['request']->getBaseUrl() . '/' . trim($path, '/');
}
}
<?php
class Portfolio implements JsonSerializable {
protected $app;
function __construct(Application $app) {
$this->app = $app;
}
function jsonSerialize() {
return [
'hardware' => $this->getHardwareSection(),
'server' => $this->getServerSection(),
'backend' => $this->getBackendSection(),
'frontend' => $this->getFrontendSection(),
'android' => $this->getAndroidSection(),
];
}
protected function getHardwareSection() {
return [
[
'name' => 'Home Setup',
'content' => $this->app->renderView('portfolio/hardware/home_setup.twig'),
'thumb' => 'http://placehold.it/207x166&text=Home+Setup+Thumb', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'gallery' => [
'http://placehold.it/600x400&text=Home+Setup+1', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Home+Setup+2', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Home+Setup+3', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
],
],
[
'name' => 'Robot Driver Station',
'content' => $this->app->renderView('portfolio/hardware/home_setup.twig'),
'thumb' => 'http://placehold.it/207x166&text=Driver+Station+Thumb', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'gallery' => [
'http://placehold.it/600x400&text=Driver+Station+1', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Driver+Station+2', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Driver+Station+3', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
],
],
];
}
protected function getServerSection() {
return [
[
'name' => 'Personal Server',
'content' => $this->app->renderView('portfolio/hardware/home_setup.twig'),
'thumb' => 'http://placehold.it/207x166&text=Personal+Server+Thumb', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'gallery' => [
'http://placehold.it/600x400&text=Personal+Server+1', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Personal+Server+2', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Personal+Server+3', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
],
],
[
'name' => 'OTA Update Center',
'content' => $this->app->renderView('portfolio/hardware/home_setup.twig'),
'thumb' => 'http://placehold.it/207x166&text=OTA+Thumb', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'gallery' => [
'http://placehold.it/600x400&text=OTA+1', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=OTA+2', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=OTA+3', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
],
],
];
}
protected function getBackendSection() {
return [
[
'name' => 'Personal Site',
'content' => $this->app->renderView('portfolio/hardware/home_setup.twig'),
'thumb' => 'http://placehold.it/207x166&text=Personal_Site+Thumb', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'gallery' => [
'http://placehold.it/600x400&text=Personal_Site+1', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Personal_Site+2', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Personal_Site+3', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
],
],
[
'name' => 'OTA Update Center',
'content' => $this->app->renderView('portfolio/hardware/home_setup.twig'),
'thumb' => 'http://placehold.it/207x166&text=OTA+Thumb', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'gallery' => [
'http://placehold.it/600x400&text=OTA+1', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=OTA+2', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=OTA+3', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
],
],
[
'name' => 'Celestial Seasonings',
'content' => $this->app->renderView('portfolio/hardware/home_setup.twig'),
'thumb' => 'http://placehold.it/207x166&text=Celestial+Seasonings',
'gallery' => [
'http://placehold.it/600x400&text=Celestial+Seasonings+1', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Celestial+Seasonings+2', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Celestial+Seasonings+3', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
],
]
];
}
protected function getFrontendSection() {
return [
[
'name' => 'Personal Site',
'content' => $this->app->renderView('portfolio/hardware/home_setup.twig'),
'thumb' => 'http://placehold.it/207x166&text=Personal Site+Thumb', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'gallery' => [
'http://placehold.it/600x400&text=Personal Site+1', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Personal Site+2', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=Personal Site+3', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
],
],
[
'name' => 'OTA Update Center',
'content' => $this->app->renderView('portfolio/hardware/home_setup.twig'),
'thumb' => 'http://placehold.it/207x166&text=OTA+Thumb', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'gallery' => [
'http://placehold.it/600x400&text=OTA+1', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=OTA+2', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=OTA+3', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
],
],
];
}
protected function getAndroidSection() {
return [
[
'name' => 'OTA Update Center',
'content' => $this->app->renderView('portfolio/hardware/home_setup.twig'),
'thumb' => 'http://placehold.it/207x166&text=OTA+Thumb', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'gallery' => [
'http://placehold.it/600x400&text=OTA+1', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=OTA+2', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
'http://placehold.it/600x400&text=OTA+3', //$this->app->asset('img/portfolio/hardware/home_setup.jpg'),
],
],
];
}
}
<?php
/** @var Application $app */
$app->get('/', 'HomeController::home')->bind('home');
$app->get('/portfolio.json', 'HomeController::portfolio')->bind('portfolio');
{% spaceless %}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimal-ui" />
<title>{{ title|default('Valeriy E Trubachev, Full Stack Web Developer') }}</title>
<meta name="description" content="{{ metadesc|default('lipsum') }}" />
<style type="text/css">
{{ source('assets/critical.css') }}
{% block critical_css %}{% endblock %}
</style>
{# loadCSS/JS will inject tags after this one #}
<script type="text/javascript"></script>
{# assets loaded async below #}
{#<link rel="stylesheet" href="{{ app.asset('css/style.min.css') }}" />#}
{#<script type="text/javascript" src="{{ app.asset('js/scripts.min.js') }}"></script>#}
</head>
<body class="{{ body_class|default('') }}">
<div id="root" class="parallax">
<header id="intro" class="parallax__group">
<div id="intro_back" class="parallax_layer parallax__layer--back"></div>
<div class="parallax__layer parallax__layer--base">
<nav class="social">
<ul>
<li><a href="https://github.com/vetruvet" target="_blank"><span class="fa fa-github"></span><label>Github</label></a></li>
<li><a href="https://plus.google.com/+ValeraTrubachev" target="_blank"><span class="fa fa-google-plus"></span><label>Google+</label></a></li>
{#<li><a href="http://vetruvet.blogspot.com/" target="_blank"><span class="fa fa-book"></span><label>Blog</label></a></li>#}
</ul>
</nav>
<section>
{% block intro %}{% endblock %}
</section>
<button type="button" id="scroll_hint" class="fa fa-chevron-down"></button>
<noscript>
<p class="noscript-warn">For a better experience, please enable JavaScript!</p>
</noscript>
</div>
</header>
<article id="body">
{% block content %}{% endblock %}
<footer>
<p class="copy">&copy; 2015 Valeriy E Trubachev.</p>
</footer>
</article>
</div>
{% block extra_body %}{% endblock %}
<script type="text/javascript">
{{ source('assets/critical.js') }}
loadCSS('{{ app.asset('css/style.min.css') }}');
loadJS('{{ app.asset('js/scripts.min.js') }}');
{% block critical_js %}{% endblock %}
</script>
<noscript>
<link rel="stylesheet" href="{{ app.asset('css/style.min.css') }}" />
<link rel="stylesheet" href="{{ app.asset('css/noscript.min.css') }}" />
<iframe src="//www.googletagmanager.com/ns.html?id=GTM-W8NH52" height="0" width="0" style="display:none;visibility:hidden"></iframe>
</noscript>
</body>
</html>
{% endspaceless %}
{% extends 'layout.twig' %}
{% set body_class = 'home' %}
{% block intro %}
<h1>Valeriy E Trubachev</h1>
<small>vetruvet</small>
<h2>Full Stack Web Developer</h2>
<aside>
I am a full stack web developer who doesn't like to compromise quality.
<br />
I use modern tools and languages and try to push the boundary of what is considered best practice.
<br />
I have pride in my work and like to tackle problems that benefit society as a whole.
<br />
I pay attention to detail and try to perfect what I work on.
</aside>
{% endblock %}
{% block content %}
<aside>
<h4>The Stack</h4>
<ul>
<li>Ubuntu</li>
<li>Nginx</li>
<li>Percona</li>
<li>Redis</li>
<li>PHP-FPM</li>
<li>Laravel/Silex</li>
<li>Gulp</li>
<li>Sass</li>
<li>ES6+Babel</li>
</ul>
</aside>
<section class="main">
<nav id="left-section-nav">
<ul>
<li class="hardware active"><a href="#hardware"><i class="fa fa-hdd-o"></i><label>Hardware</label></a></li>
<li class="server"><a href="#server"><i class="fa fa-terminal"></i><label>Server</label></a></li>
<li class="backend"><a href="#backend"><i class="fa fa-database"></i><label>Backend</label></a></li>
<li class="frontend"><a href="#frontend"><i class="fa fa-code"></i><label>Frontend</label></a></li>
<li class="android"><a href="#android"><i class="fa fa-android"></i><label>Android</label></a></li>
<li class="deployment"><a href="#deployment"><i class="fa fa-paper-plane"></i><label>Deployment</label></a></li>
</ul>
</nav>
<article id="hardware">
<header>
<h3><span class="fa fa-hdd-o"></span>Hardware</h3>
<h4>Oooh, wires and blinking lights!</h4>
</header>
<p>
The stack always starts at the hardware level.
Any truly full-stack developer should understand the hardware basics of a modern computer.
</p>
<div class="portfolio"><div class="gallery"></div></div>
</article>
<article id="server">
<header>
<h3><span class="fa fa-terminal"></span>Server</h3>
<h4>Have you hugged your sysadmin recently?</h4>
</header>
<p>
Servers are the foundation of the Internet.
A basic knowledge of setting up and maintaining a server is a necessity for a modern web developer.
I have experience in starting up, configuring, and running different types of Linux servers on different hosting providers:
</p>
<ul>
<li><strong>Hosting Providers</strong>: traditional server/VPS providers and cloud providers like Amazon AWS, Google App Engine, and Century Link Cloud</li>
<li><strong>Distributions</strong>: Ubuntu (preferred), RedHat, CentOS (optionally with WHM/cPanel)</li>
<li><strong>Web Server software</strong>: Nginx (preferred) and Apache</li>
<li><strong>Database software</strong>: MySQL (preferred) and Postgres</li>
<li><strong>Backend engine:</strong>: PHP (preferably with FPM)</li>
</ul>
<p>
I have created provisioning scripts for production servers and for local Vagrant development VMs that mimic them using this software.
</p>
<p>
I have also developed a build system that utilizes git and continuous integration to automatically test, build, and deploy sites, all triggered by a git push.
</p>
<div class="portfolio"><div class="gallery"></div></div>
</article>
<article id="backend">
<header>
<h3><span class="fa fa-database"></span>Backend</h3>
<h4>The stuff nobody sees but everybody depends on</h4>
</header>
<p>
All sites (with the exception of simple, static, single-page sites) have a backend.
My backend components almost always consist of PHP code with a MySQL database,
though I have dabbled in Postgres and have experience manipulating data in MsSQL (for reports/views).
I always some PHP framework for my projects:
</p>
<ul>
<li><strong>Laravel</strong>: my preferred framework, and the one I am most familiar with. I have also made contributions to this framework</li>
<li><strong>Lumen</strong>: closely related to Laravel, I use this framework for smaller, faster projects</li>
<li><strong>Silex</strong>: before Lumen, this was my preferred framework for smaller projects. This site is built using Silex.</li>
</ul>
<p>
I follow best-practices and PSR's in my code to ensure good organization and maintainability.
I will only stray from those if I feel that there is a solid reason for straying from the standard that makes my code better.
</p>
<p>
I have experience in designing and building APIs to enable interaction with other systems.
</p>
<div class="portfolio"><div class="gallery"></div></div>
</article>
<article id="frontend">
<header>
<h3><span class="fa fa-code"></span>Frontend</h3>
<h4>Slick interfaces and .... </h4>
</header>
<p>
This is the part that everybody sees, no website would exist without a frontend.
I use build tools (Gulp, Sass, ES6+Babel, etc) to allow writing better code and automating a lot of my workflow.
From automatically compressing images to compiling CSS and JavaScript, I automate as much of front-end workflow as possible to avoid boilerplate work.
</p>
<p>
I write portable, semantic front-end code that works on all browsers and degrades gracefully when feature support is lacking.
</p>
<div class="portfolio"><div class="gallery"></div></div>
</article>
<article id="android">
<header>
<h3><span class="fa fa-android"></span>Android</h3>
<h4>That green bug-thing and "the next iPhone killer"</h4>
</header>
<p>
As an extension of web development, I also have experience writing Android apps.
It is a natural addition to web development as it builds on my foundation of server and back-end experience to enable communication with a server in my apps.
</p>
<div class="portfolio"><div class="gallery"></div></div>
</article>
<article id="deployment">
<header>
<h3><span class="fa fa-paper-plane"></span>Deployment</h3>
<h4>SHIP IT! SHIP IT! SHIP IT!</h4>
</header>
<p>
Without deployment, all the wonderful code we write would be stuck on our computer (and GitHub, of course).
With traditional apps, deployment is simpler as there is a generated artifact (APK, JAR, installer, executable, etc), but with web applications it's more complicated.
What's the right way to deploy PHP sites and applications &mdash; FTP? SCP/rsync? SSH + git pull?
All these ways seem cumbersome and prone to human error.
</p>
<p>
I have developed a custom deployment solution built on top of GitLab and Gitlab CI that
runs unit tests (front-end and back-end), generates built assets (artifacts) using Gulp, and deploys them to a server if everything succeeded.
It integrates into our team's workflow very smoothly, has support for multiple environments (using branches), and [something].
</p>
<p>
This deployment system has enabled more collaboration between developers, automated error checking, and faster reliable deployments.
</p>
<div class="portfolio"><div class="gallery"></div></div>
</article>
</section>
{% endblock %}
{% block extra_body %}
<div id="portfolio_modal" class="modal">
<div class="modal-curtain"></div>
<div class="modal-root">
<div class="modal-title">
<i class="fa fa-close"></i>
<h4></h4>
</div>
<div class="modal-body">
<div class="gallery"></div>
<div class="content"></div>
</div>
</div>
</div>
{% endblock %}
{% block critical_js %}
SiteConfig.portfolio_url = '{{ app.path('portfolio') }}';
{% endblock %}
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam animi, aperiam consequatur illo, ipsa ipsam iusto molestiae natus odio officiis quia quis ratione rem temporibus unde vitae voluptate voluptates voluptatibus.
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
/**
* Handles debouncing of events via requestAnimationFrame
* @see http://www.html5rocks.com/en/tutorials/speed/animations/
* @param {Function} callback The callback to handle whichever event
*/
function Debouncer (callback) {
this.callback = callback;
this.ticking = false;
}
Debouncer.prototype = {
constructor : Debouncer,
/**
* dispatches the event to the supplied callback
* @private
*/
update : function() {
this.callback && this.callback();
this.ticking = false;
},
/**
* ensures events don't get stacked
* @private
*/
requestTick : function() {
if(!this.ticking) {
requestAnimationFrame(this.rafCallback || (this.rafCallback = this.update.bind(this)));
this.ticking = true;
}
},
/**
* Attach this as the event listeners
*/
handleEvent : function() {
this.requestTick();
}
};
//= include ../../../bower_components/loadCSS/loadCSS.js
//= include ../../../bower_components/loadJS/loadJS.js
window.SiteConfig = {};
//= include ../../build/modernizr.js
//= include ../../../bower_components/jquery/dist/jquery.js
//= include ../../../bower_components/flickity/dist/flickity.pkgd.js
//= include ../_debouncer.js
//var $window = $(window);
var $body = $(document.body);
WebFontConfig = {
google: { families: [ 'Roboto:400,700:latin' ] }
};
(function() {
var wf = document.createElement('script');
wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
})();
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-W8NH52');
function preloadImage(src, callback) {
$('<img>').hide().on('load error', function() {
$(this).remove();
if (callback != null) callback(src);
}).attr('src', src).appendTo($body);
}
$(function() {
var $root = $('#root'),
$intro_back = $('#intro_back'),
$scroll_hint = $('#scroll_hint'),
$left_nav = $('#left-section-nav'),
$left_nav_section = $left_nav.parent(),
$body = $('#body'),
$sections = $body.children('section.main').children('article');
var animateScroll = function (top) {
top -= $root.offset().top;
top += $root.scrollTop();
$root.animate({ scrollTop: top }, { complete: function() { $root.removeClass('scrolling') }});
$root.addClass('scrolling');
};
var introBackground = /^url\((.*)\)$/i.exec($intro_back.addClass('back-loaded').css('background-image'))[1];
$intro_back.removeClass('back-loaded');
preloadImage(introBackground, function () { $intro_back.addClass('back-loaded'); });
var debouncer = new Debouncer(function () {
var top = $root.scrollTop();
if (top > left_nav_top) {
if (!$left_nav.hasClass('fixed')) $left_nav.appendTo('body').addClass('fixed').css('left', $body.offset().left);
} else {
if ($left_nav.hasClass('fixed')) $left_nav.prependTo($left_nav_section).removeClass('fixed').css('left', '');
}
if (top == 0) {
$scroll_hint.removeClass('not-top');
} else {
$scroll_hint.addClass('not-top');
}
if (!$root.hasClass('scrolling')) {
$sections.each(function() {
if (($(this).offset().top + $(this).height()) / $root.height() < 0.25) return;
$left_nav.find('a[href="#'+$(this).attr('id')+'"]').closest('li').addClass('active').siblings('li').removeClass('active');
return false;
});
}
});
var left_nav_top = $left_nav.offset().top - $root.offset().top;
$root.on('scroll', debouncer.handleEvent.bind(debouncer)).on('resize orientationchange', function () {
left_nav_top = $left_nav.offset().top - $root.offset().top;
$root.trigger('scroll');
});
$scroll_hint.on('click', function () {
animateScroll($body.offset().top - $root.offset().top);
});
$left_nav.find('a').on('click', function (event) {
event.preventDefault();
$(this).closest('li').addClass('active').siblings().removeClass('active');
animateScroll($($(this).attr('href')).offset().top);
});
var $portfolio_modal = $('#portfolio_modal'),
$portfolio_title = $portfolio_modal.find('.modal-title h4'),
$portfolio_gallery = $portfolio_modal.find('.gallery'),
$portfolio_content = $portfolio_modal.find('.content');
var portfolio = null,
portfolio_sections = [];
var showPortfolioDetails = function (project) {
if (portfolio === null) return;
var project_parts = project.split(':', 2);
var section = project_parts[0];
var index = parseInt(project_parts[1]);
var project_data = portfolio[section][index];
$portfolio_title.html(project_data.name);
$portfolio_content.html(project_data.content);
$portfolio_gallery.flickity('remove', $portfolio_gallery.flickity('getCellElements'));
for (var q = 0; q < project_data.gallery.length; q++) {
$portfolio_gallery.flickity('append',
$('<div>').addClass('gallery-cell').append(
$('<img>').data('src', project_data.gallery[q])
)
);
}
$portfolio_modal.removeClass(portfolio_sections.join(' ')).addClass(section);
$portfolio_modal.addClass('open').find('.gallery').flickity('resize');
$portfolio_gallery.find('img').on('load', function () {
$(this).closest('.gallery').flickity('resize').flickity('reposition');
}).on('error', function () {
$(this).closest('.gallery').flickity('remove', $(this).closest('.gallery-cell')).flickity('reposition');
}).each(function () {
$(this).prop('src', $(this).data('src'));
});
};
$('article .portfolio .gallery').flickity({
freeScroll: true,
contain: true,
prevNextButtons: false,
pageDots: false,
cellAlign: 'left'
}).on('click', '.gallery-cell', function (event) {
event.preventDefault();
showPortfolioDetails($(this).data('project'));
});
$portfolio_gallery.flickity({
wrapAround: true
});
$(document.body).on('click', '.modal-title .fa-close, .modal > .modal-curtain', function () {
$(this).closest('.modal').removeClass('open');
});
$(document.body).on('keydown', function (event) {
if (event.which == 27 && $portfolio_modal.hasClass('open')) {
event.preventDefault();
$portfolio_modal.removeClass('open');
}
});
$.ajax({
url: SiteConfig.portfolio_url,
method: 'get',
dataType: 'json'
}).done(function (data) {
portfolio = data;
portfolio_sections = [];
$.each(portfolio, function (key) {
portfolio_sections.push(key);
var $gallery = $('#' + key).find('.portfolio .gallery');
for (var q = 0; q < this.length; q++) {
var project = this[q];
$gallery.flickity('append',
$('<div>').addClass('gallery-cell').attr('data-project', key + ':' + q).append(
$('<a>').attr('href', 'javascript:void(0)').append(
$('<img>').attr('src', project.thumb)
).append(
$('<label>').html(project.name)
)
)
);
}
$gallery.flickity('resize');
});
$('section .portfolio img').on('load', function () {
$(this).closest('.gallery').flickity('resize').flickity('reposition');
}).on('error', function () {
$(this).closest('.gallery').flickity('remove', $(this).closest('.gallery-cell')).flickity('reposition');
});
});
});
%vertical-center {
top: 50%;
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
%horizontal-center {
left: 50%;
-webkit-transform: translateX(-50%);
-ms-transform: translateX(-50%);
transform: translateX(-50%);
}
%both-center {
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
%nowrap {
white-space: nowrap;
}
%square {
position: relative;
padding-bottom: 100%;
> :first-child {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
$body_back_color: #acacac;
$intro_back_color: #606060;
$social_tooltip_color: rgba(0, 0, 0, 0.5);
$stack_colors: (
hardware: #000000,
server: #009000,
backend: #4f5b93,
frontend: #e44d26,
android: #92b83e,
deployment: #1ab88c
);
$container_width: 1000px;
$left_nav_size: 1.75rem;
$left_nav_transition: 0.125s;
$modal_transition: 0.33s;
$thumb_label_transition: 0.25s;
@import "../../../bower_components/bourbon/app/assets/stylesheets/bourbon";
@import "../mixins";
@import "../variables";
#intro {
background: $intro_back_color url(/img/hard-drive.jpg) no-repeat center center;
background-size: cover;
&:before {
background: rgba(darken($intro_back_color, 15%), 0.75);
}
#scroll_hint {
display: none;
}
.noscript-warn {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
line-height: 1.5;
font-weight: bold;
color: red;
background: rgba(0,0,0,0.5);
padding: 0.5em;
font-size: 3.75vw;
text-align: center;
@media screen and (min-width: 426px) {
font-size: 1em;
}
}
}
.body {
#left-section-nav {
display: none;
}
> article {
padding-left: 1em;
.gallery {
margin: 0 -0.5em;
.gallery-cell {
display: inline-block;
}
}
}
}
This diff is collapsed.
{
"name": "vetruvet.com",
"version": "0.0.0",
"authors": [
"Valera Trubachev <vetruvet@gmail.com>"
],
"license": "MIT",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"jquery": "~2.1.1",
"loadCSS": "https://github.com/filamentgroup/loadCSS.git#~0.1.2",
"loadJS": "https://github.com/filamentgroup/loadJS.git#~0.1.2",
"zepto": "~1.1.4",
"bourbon": "~4.2.0",
"normalize.css": "~3.0.2",
"font-awesome": "4.3.0",
"flickity": "~1.0.0"
}
}
{
"name": "vetruvet/vetruvet.com",
"authors": [
{
"name": "Valera Trubachev",
"email": "vetruvet@gmail.com"
}
],
"autoload": {
"classmap": [
"app/controllers",
"app/lib"
]
},
"require": {
"silex/silex": "~1.2",
"twig/twig": ">=1.8,<2.0-dev"
}
}
This diff is collapsed.
'use strict';
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var fs = require('fs');
var connect = require('gulp-connect-php');
var browserSync = require('browser-sync');
//var penthouse = require('penthouse');
var options = {
production: false,
bowerrc: './.bowerrc',
bower_dir: 'bower_components',
src_paths: {
sass: {
critical: ['assets/sass/critical/**/*.scss'],
main: ['assets/sass/main/**/*.scss']
},
js: {
critical: ['assets/js/critical/**/*.js'],
main: ['assets/js/main/**/*.js']
},
img: ['assets/img/**/*.png', 'assets/img/**/*.jpg', 'assets/img/**/*.gif', 'assets/img/**/*.svg'],
font: ['assets/fonts']
},
dest_paths: {
sass: {
critical: 'app/views/assets',
main: 'public/css'
},
js: {
critical: 'app/views/assets',
main: 'public/js'
},
img: 'public/img',
font: 'public/fonts',
build: 'resources/build'
},
php_server: {
base: 'public',
router: 'server.php',
port: '8000',
hostname: '0.0.0.0'
}
};
(function() {
var knownOptions = {
string: 'env',
default: { env: process.env.NODE_ENV || 'local' }
};
var minimist = require('minimist');
var cli_options = minimist(process.argv.slice(2), knownOptions);
options.production = cli_options.env === 'production';
if (fs.existsSync('.bowerrc')) options.bower_dir = require('./.bowerrc').directory || options.bower_dir;
options.src_paths.sass.critical.push('!**/_*.scss');
options.src_paths.sass.main.push('!**/_*.scss');
options.src_paths.js.critical.push('!**/_*.js');
options.src_paths.js.main.push('!**/_*.js');
options.src_paths.font.push('!**/.git*');
// options.src_paths.img.push('!**/.git*');
})();
gulp.task('copy:fontawesome', function () {
gulp.src(options.bower_dir + '/font-awesome/fonts/**')
.pipe($.newer('public/fonts'))
.pipe(gulp.dest('public/fonts'))
.pipe($.size({title: 'font awesome'}));
});
//gulp.task('css_critical_extract', ['sass'], function () {
// connect.server(options.php_server);
//
// penthouse({
// url: 'http://localhost:8000',
// css: './public/css/style.min.css',
// width: 1200,
// height: 900
// }, function (error, css) {
// connect.closeServer(function(){});
//
// if (!fs.existsSync('app/views/assets')) fs.mkdirSync('app/views/assets');
// fs.writeFileSync('app/views/assets/critical.css', css);
// });
//});
gulp.task('modernizr', function () {
return gulp.src(['assets/sass/**/*.scss', 'assets/js/**/*.js'])
.pipe($.modernizr('modernizr.js', {
options: [ 'setClasses', 'addTest', 'html5printshiv', 'testProp', 'fnBind' ]
}))
.pipe(gulp.dest('assets/build'));
});
gulp.task('sass:css_to_scss', function () {
return gulp.src([options.bower_dir + '/**/*.css', '!' + options.bower_dir + '/**/*.min.css'], { base: options.bower_dir })
.pipe($.newer({ dest: options.bower_dir, ext: '.scss' }))
.pipe($.rename({ extname: '.scss' }))
.pipe(gulp.dest(options.bower_dir));
});
//gulp.task('fonts:copy', function () {
// return gulp.src(options.src_paths.font)
// .pipe($.newer(options.dest_paths.font))
// .pipe(gulp.dest(options.dest_paths.font))
// .pipe($.size({title: 'fonts'}));
//});
gulp.task('sass:critical', function () {
return gulp.src(options.src_paths.sass.critical)
.pipe($.sass({
outputStyle: options.production ? 'compressed' : 'nested',
onError: console.error.bind(console, 'SASS ERROR:')
}))
.pipe(gulp.dest('.tmp/styles/critical'))
// .pipe($.rename({ extname: '.twig', suffix: '_css' }))
.pipe(gulp.dest(options.dest_paths.sass.critical))
.pipe($.size({title: 'critical css'}));
});
gulp.task('sass:main', function () {
return gulp.src(options.src_paths.sass.main)
.pipe($.sourcemaps.init())
.pipe($.sass({
outputStyle: options.production ? 'compressed' : 'nested',
onError: console.error.bind(console, 'SASS ERROR:')
}))
.pipe(gulp.dest('.tmp/styles/main'))
.pipe($.rename({ extname: '.min.css'}))
.pipe($.if(!options.production, $.sourcemaps.write('.', {sourceRoot: '../../../assets/sass/main'})))
.pipe(gulp.dest(options.dest_paths.sass.main))
.pipe($.size({title: 'main css'}));
});
gulp.task('css', ['sass:css_to_scss'], function(cb) {
runSequence(['sass:critical', 'sass:main'], cb);
});
gulp.task('img', function () {
return gulp.src(options.src_paths.img)
.pipe($.cache($.imagemin({
progressive: true,
interlaced: true
})))
.pipe(gulp.dest(options.dest_paths.img))
.pipe($.size({title: 'images'}));
});
gulp.task('js:critical', function () {
return gulp.src(options.src_paths.js.critical)
.pipe($.include())
.pipe($.uglify({
mangle: !!options.production
}))
//.pipe($.rename({ extname: '.twig', suffix: '_js' }))
.pipe(gulp.dest(options.dest_paths.js.critical))
.pipe($.size({title: 'critical js'}));
});
gulp.task('js:main', function () {
return gulp.src(options.src_paths.js.main)
.pipe($.sourcemaps.init())
.pipe($.include())
.pipe($.uglify({
mangle: !!options.production
}))
.pipe($.rename({ extname: '.min.js'}))
.pipe($.if(!options.production, $.sourcemaps.write('.', {sourceRoot: '../../../assets/js/main'})))
.pipe(gulp.dest(options.dest_paths.js.main))
.pipe($.size({title: 'main js'}));
});
gulp.task('js', ['modernizr'], function (cb) {
runSequence(['js:critical', 'js:main'], cb);
});
gulp.task('clean', del.bind(null, [
'.tmp',
options.dest_paths.sass.critical,
options.dest_paths.sass.main,
options.dest_paths.js.critical,
options.dest_paths.js.main,
options.dest_paths.img,
options.dest_paths.font,
options.dest_paths.build
], {dot: true}));
gulp.task('compile', function (cb) {
runSequence(['css', 'js'], cb);
});
gulp.task('default', options.production ? ['clean'] : [], function (cb) {
runSequence([/*'fonts:copy',*/ 'copy:fontawesome', 'img', 'compile'], cb);
});
gulp.task('watch', ['default'], function () {
gulp.watch('assets/js/**/*.js', ['js']);
gulp.watch('assets/sass/**/*.scss', ['css']);
gulp.watch('assets/img/**/*', ['img']);
});
gulp.task('serve', ['default'], function () {
connect.server(options.php_server, function () {
browserSync({
proxy: 'localhost:8000'
});
});
gulp.watch(['public/**', 'app/**']).on('change', function () {
browserSync.reload();
})
});
{
"devDependencies": {
"browser-sync": "^2.2.4",
"del": "^1.1.0",
"gulp": "^3.8.5",
"gulp-cache": "^0.2.8",
"gulp-connect-php": "0.0.5",
"gulp-if": "^1.2.1",
"gulp-imagemin": "^2.0.0",
"gulp-include": "^1.1.1",
"gulp-load-plugins": "^0.8.0",
"gulp-modernizr": "^1.0.0-alpha",
"gulp-newer": "^0.5.0",
"gulp-rename": "^1.2.0",
"gulp-sass": "^1.2.0",
"gulp-size": "^1.0.0",
"gulp-sourcemaps": "^1.3.0",
"gulp-uglify": "^1.0.1",
"gulp-watch": "^4.1.1",
"minimist": "^1.1.1",
"run-sequence": "^1.0.1"
},
"engines": {
"node": ">=0.10.0"
},
"private": true,
"scripts": {
"test": "gulp && git status | grep 'working directory clean' >/dev/null || (echo 'Please commit all changes generated by building'; exit 1)"
}
}
<?php
require_once __DIR__.'/../vendor/autoload.php';
$app = new Application();
require_once __DIR__.'/../app/config.php';
require_once __DIR__.'/../app/helpers.php';
require_once __DIR__.'/../app/routes.php';
$app->run();
<?php
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uri = urldecode($uri);
$requested = __DIR__ . $uri;
if ($uri !== '/' && file_exists($requested)) {
return false;
}
if (substr($uri, 0, 8) === '/assets/' && file_exists(__DIR__ . '/..' . $uri)) {
echo file_get_contents(__DIR__ . '/..' . $uri);
exit;
}
require_once __DIR__ . '/index.php';