├── .gitignore ├── README.md ├── app.js ├── components ├── BlogList.jsx ├── BlogSingle.jsx ├── Footer.jsx ├── Header.jsx ├── Loading.jsx ├── Nav.jsx ├── WorkList.jsx └── WorkSingle.jsx ├── config.js ├── dispatcher └── AppDispatcher.js ├── dist ├── bundle.js ├── css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── clean-blog.css │ ├── clean-blog.min.css │ └── cosmic-custom.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── img │ ├── about-bg.jpg │ ├── contact-bg.jpg │ ├── home-bg.jpg │ ├── post-bg.jpg │ └── post-sample-image.jpg └── js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── clean-blog.js │ ├── clean-blog.min.js │ ├── jquery.js │ └── jquery.min.js ├── index.html ├── package.json ├── pages ├── Blog.jsx ├── Default.jsx ├── NoMatch.jsx └── Work.jsx ├── server.js ├── stores └── AppStore.js ├── tonyspirocom.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tonyspiro.com-react 2 | This app demonstrates how to build a portfolio blog using React on the [Cosmic JS](https://cosmicjs.com) content platform. Uses [React Router](https://github.com/rackt/react-router), Flux and Cosmic JS to load pages fast in a single page application. 3 | 4 | #####[View a demo here](http://tonyspiro-react.cosmicapp.co) 5 | 6 | ### Getting started 7 | 8 | #####Install 9 | ``` 10 | git clone https://github.com/tonyspiro/tonyspiro.com-react 11 | cd tonyspiro.com-react 12 | npm install 13 | ``` 14 | #####Run webpack dev 15 | ``` 16 | npm run dev 17 | ``` 18 | Go to [http://localhost:8080/webpack-dev-server](http://localhost:8080/webpack-dev-server) 19 | #####Run production 20 | ``` 21 | npm start 22 | ``` 23 | Go to [http://localhost:8000](http://localhost:8000) 24 | 25 | ### About the app 26 | * My original website at [http://tonyspiro.com](tonyspiro.com) is built on the WordPress platform. After running benchmark tests with ApacheBench between my WordPress site and this React / Cosmic JS version, the React version clocks in over 10x FASTER to "first paint". This is the real benefit from the React / API set up, less server overhead and faster page load times between "pages". You can really see a significant speed difference on mobile. 27 | 28 | WordPress (26.7 seconds): 29 | 30 | 31 | 32 | React / Cosmic JS (2.3 seconds): 33 | 34 | 35 | 36 | * Benchmarking was performed by running ApacheBench. ```ab -n 100 -c 10 http://tonyspiro.com/``` and ```ab -n 100 -c 10 http://tonyspiro.com:8000/``` 37 | 38 | * React Router is used to perform the navigation between "pages". Running ```npm start``` boots a node server that allows for the pages to render properly when loaded in from a url or on page refresh. 39 | 40 | * The [Cosmic JS browser package](https://github.com/cosmicjs/cosmicjs-browser) is used to pull the data from the Cosmic JS API into the app. 41 | 42 | * [Webpack](https://webpack.github.io/) is used for the dev and build process to turn ES6 JS into browser-friendly ES5 JS using the babel-loader. 43 | 44 | * The front end style is based off [a theme from Start Bootstrap](http://startbootstrap.com/template-overviews/clean-blog/). 45 | 46 | ###How I did it 47 | Here are the steps that were taken to build this app: 48 | 49 | 1. I exported the data out of [my WP blog](http://tonyspiro.com) using the [Cosmic JS WP Export plugin](https://github.com/cosmicjs/cosmicjs-wp-export). The exported JSON file is included in this repo at [tonyspirocom.json](https://github.com/tonyspiro/tonyspiro.com-react/blob/master/tonyspirocom.json) 50 | 2. I created a new bucket in [Cosmic JS](https://cosmicjs.com) 51 | 3. I then imported the JSON file into my Cosmic JS bucket 52 | 4. I built the React app and edited [config.js](https://github.com/tonyspiro/tonyspiro.com-react/blob/master/config.js) to point to the correct bucket in Cosmic JS 53 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // app.js 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import { Router, Route, IndexRoute, Link } from 'react-router'; 5 | import createBrowserHistory from 'history/lib/createBrowserHistory'; 6 | let history = createBrowserHistory(); 7 | 8 | // Utilities 9 | import AppStore from './stores/AppStore'; 10 | import AppDispatcher from './dispatcher/AppDispatcher'; 11 | 12 | // Components 13 | import Nav from './components/Nav'; 14 | import Footer from './components/Footer'; 15 | import Loading from './components/Loading'; 16 | 17 | // Pages 18 | import Blog from './pages/Blog'; 19 | import Work from './pages/Work'; 20 | import Default from './pages/Default'; 21 | import NoMatch from './pages/NoMatch'; 22 | 23 | 24 | let getAppState = () => { 25 | return { 26 | data: AppStore.data 27 | }; 28 | } 29 | 30 | class App extends React.Component{ 31 | 32 | // Method to setState based upon Store changes 33 | _onChange() { 34 | this.setState(getAppState()); 35 | } 36 | 37 | constructor() { 38 | 39 | super(); 40 | 41 | // API data 42 | AppDispatcher.dispatch({ 43 | action: 'init-app' 44 | }); 45 | 46 | } 47 | 48 | // Add change listeners to stores 49 | componentDidMount() { 50 | AppStore.addChangeListener(this._onChange.bind(this)); 51 | } 52 | 53 | // Remove change listeners from stores 54 | componentWillUnmount() { 55 | AppStore.removeChangeListener(this._onChange.bind(this)); 56 | } 57 | 58 | render(){ 59 | 60 | if(!AppStore.data.ready){ 61 | let style = { 62 | marginTop: 120 63 | } 64 | return ( 65 |
66 | 67 |
68 | ); 69 | } 70 | 71 | let globals = AppStore.data.globals; 72 | let pages = AppStore.data.pages; 73 | 74 | // Pass this down!!!! 75 | let data = AppStore.data; 76 | let Routes = React.cloneElement(this.props.children, { data: data }); 77 | 78 | return ( 79 |
80 |
84 | ); 85 | } 86 | } 87 | 88 | ReactDOM.render(( 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | ), document.getElementById('app-root')) -------------------------------------------------------------------------------- /components/BlogList.jsx: -------------------------------------------------------------------------------- 1 | // BlogList.jsx 2 | import React from 'react'; 3 | import _ from 'lodash'; 4 | import { Link } from 'react-router'; 5 | 6 | import AppDispatcher from '../dispatcher/AppDispatcher'; 7 | import AppStore from '../stores/AppStore'; 8 | 9 | class BlogList extends React.Component{ 10 | 11 | scrollTop(){ 12 | $('html, body').animate({ 13 | scrollTop: $("#main-content").offset().top 14 | }, 500); 15 | } 16 | 17 | getMoreArticles(){ 18 | 19 | AppDispatcher.dispatch({ 20 | action: 'get-more-items' 21 | }); 22 | 23 | } 24 | 25 | render(){ 26 | 27 | let item_num = AppStore.data.item_num; 28 | let _this = this; 29 | 30 | let articles = AppStore.data.articles; 31 | 32 | let load_more; 33 | let show_more_text = 'Show More Articles'; 34 | 35 | if(AppStore.data.loading){ 36 | show_more_text = 'Loading...'; 37 | } 38 | 39 | if(articles && item_num <= articles.length){ 40 | load_more = ( 41 |
42 | 45 |
46 | ); 47 | } 48 | 49 | articles = _.take(articles, item_num); 50 | 51 | let articles_html = articles.map(( article ) => { 52 | let date_obj = new Date(article.created); 53 | let created = (date_obj.getMonth()+1) + '/' + date_obj.getDate() + '/' + date_obj.getFullYear(); 54 | return ( 55 |
56 |
57 |

58 | { article.title } 59 |

60 |

Posted by Tony Spiro on { created }

61 |
62 |
63 |
64 | ); 65 | }); 66 | 67 | return ( 68 |
69 |
{ articles_html }
70 | { load_more } 71 |
72 | ); 73 | } 74 | } 75 | 76 | export default BlogList; -------------------------------------------------------------------------------- /components/BlogSingle.jsx: -------------------------------------------------------------------------------- 1 | // BlogSingle.jsx 2 | import React from 'react'; 3 | import _ from 'lodash'; 4 | import { Link } from 'react-router'; 5 | 6 | import AppDispatcher from '../dispatcher/AppDispatcher'; 7 | import AppStore from '../stores/AppStore'; 8 | 9 | class BlogSingle extends React.Component{ 10 | 11 | render(){ 12 | 13 | let articles = AppStore.data.articles; 14 | 15 | // Get current page slug 16 | let slug = this.props.slug; 17 | let articles_object = _.indexBy(articles, 'slug'); 18 | let article = articles_object[slug]; 19 | 20 | let style = { 21 | marginBottom: 20 22 | }; 23 | 24 | return ( 25 |
26 | << Back to Article List 27 |

{ article.title }

28 |
29 |
30 | ); 31 | } 32 | } 33 | 34 | export default BlogSingle; -------------------------------------------------------------------------------- /components/Footer.jsx: -------------------------------------------------------------------------------- 1 | // Footer.jsx 2 | import React from 'react'; 3 | 4 | class Footer extends React.Component { 5 | 6 | render(){ 7 | 8 | let footer_text; 9 | if(this.props.globals.text){ 10 | footer_text = this.props.globals.text.footer_text; 11 | } 12 | 13 | let twitter; 14 | let facebook; 15 | let github; 16 | if(this.props.globals.social){ 17 | twitter = this.props.globals.social.twitter; 18 | facebook = this.props.globals.social.facebook; 19 | github = this.props.globals.social.github; 20 | } 21 | 22 | return ( 23 | 58 | ); 59 | } 60 | } 61 | 62 | export default Footer; 63 | -------------------------------------------------------------------------------- /components/Header.jsx: -------------------------------------------------------------------------------- 1 | // Header.jsx 2 | import React from 'react'; 3 | import Nav from './Nav'; 4 | import AppStore from '../stores/AppStore'; 5 | 6 | class Header extends React.Component { 7 | 8 | render(){ 9 | 10 | let hero; 11 | let headline; 12 | let subheadline; 13 | 14 | if(this.props.page){ 15 | hero = this.props.page.hero; 16 | headline = this.props.page.headline; 17 | subheadline = this.props.page.subheadline; 18 | } 19 | 20 | return ( 21 |
22 |
37 | ); 38 | } 39 | } 40 | 41 | export default Header; 42 | -------------------------------------------------------------------------------- /components/Loading.jsx: -------------------------------------------------------------------------------- 1 | // Loading.jsx 2 | import React from 'react'; 3 | 4 | class Loading extends React.Component{ 5 | render(){ 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | ); 22 | } 23 | } 24 | 25 | export default Loading; -------------------------------------------------------------------------------- /components/Nav.jsx: -------------------------------------------------------------------------------- 1 | // Nav.jsx 2 | import React from 'react'; 3 | import { Link } from 'react-router'; 4 | 5 | // Utilities 6 | import AppStore from '../stores/AppStore'; 7 | import AppDispatcher from '../dispatcher/AppDispatcher'; 8 | 9 | class Nav extends React.Component { 10 | 11 | handleClick(){ 12 | $('.navbar-collapse').removeClass('in'); 13 | $('html,body').scrollTop(0); 14 | } 15 | 16 | render(){ 17 | 18 | let _this = this; 19 | 20 | let pages = AppStore.data.pages; 21 | let globals = AppStore.data.globals; 22 | 23 | let page_link; 24 | 25 | let menu_items = pages.map(( page ) => { 26 | 27 | if(page.slug == 'home'){ 28 | 29 | page_link = ''; 30 | 31 | } else { 32 | 33 | page_link = page.slug; 34 | 35 | } 36 | 37 | return ( 38 |
  • 39 | { page.title } 40 |
  • 41 | ); 42 | }); 43 | 44 | return ( 45 | 64 | ); 65 | } 66 | } 67 | 68 | export default Nav; -------------------------------------------------------------------------------- /components/WorkList.jsx: -------------------------------------------------------------------------------- 1 | // WorkList.jsx 2 | import React from 'react'; 3 | import _ from 'lodash'; 4 | import { Link } from 'react-router'; 5 | 6 | import AppDispatcher from '../dispatcher/AppDispatcher'; 7 | import AppStore from '../stores/AppStore'; 8 | 9 | class WorkList extends React.Component{ 10 | 11 | scrollTop(){ 12 | $('html, body').animate({ 13 | scrollTop: $("#main-content").offset().top 14 | }, 500); 15 | } 16 | 17 | getMoreWorkItems(){ 18 | 19 | AppDispatcher.dispatch({ 20 | action: 'get-more-items' 21 | }); 22 | 23 | } 24 | 25 | render(){ 26 | 27 | let item_num = AppStore.data.item_num; 28 | let _this = this; 29 | 30 | let work_items = AppStore.data.work_items; 31 | 32 | let load_more; 33 | let show_more_text = 'Show More Work'; 34 | 35 | if(AppStore.data.loading){ 36 | show_more_text = 'Loading...'; 37 | } 38 | 39 | if(work_items && item_num <= work_items.length){ 40 | load_more = ( 41 |
    42 | 45 |
    46 | ); 47 | } 48 | 49 | work_items = _.take(work_items, item_num); 50 | 51 | let articles_html = work_items.map(( work_item ) => { 52 | let date_obj = new Date(work_item.created); 53 | let created = (date_obj.getMonth()+1) + '/' + date_obj.getDate() + '/' + date_obj.getFullYear(); 54 | return ( 55 |
    56 |
    57 |

    58 | { work_item.title } 59 |

    60 |

    Posted by Tony Spiro on { created }

    61 |
    62 |
    63 |
    64 | ); 65 | }); 66 | 67 | return ( 68 |
    69 |
    { articles_html }
    70 | { load_more } 71 |
    72 | ); 73 | } 74 | } 75 | 76 | export default WorkList; -------------------------------------------------------------------------------- /components/WorkSingle.jsx: -------------------------------------------------------------------------------- 1 | // WorkSingle.jsx 2 | import React from 'react'; 3 | import _ from 'lodash'; 4 | import { Link } from 'react-router'; 5 | 6 | import AppDispatcher from '../dispatcher/AppDispatcher'; 7 | import AppStore from '../stores/AppStore'; 8 | 9 | class WorkSingle extends React.Component{ 10 | 11 | render(){ 12 | 13 | let work_items = AppStore.data.work_items; 14 | 15 | // Get current page slug 16 | let slug = this.props.slug; 17 | let articles_object = _.indexBy(work_items, 'slug'); 18 | let work_item = articles_object[slug]; 19 | 20 | let style = { 21 | marginBottom: 20 22 | }; 23 | 24 | return ( 25 |
    26 | << Back to Work List 27 |

    { work_item.title }

    28 |
    29 |
    30 | ); 31 | } 32 | } 33 | 34 | export default WorkSingle; -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | /* Configure 2 | ================================ */ 3 | let config = {}; 4 | config.bucket = { 5 | slug: 'tonyspiro-react', // this is your Cosmic JS bucket slug. Sign up for Cosmic JS (Free) and add a bucket: https://cosmicjs.com/ 6 | read_key: '', // add read_key if added to your Cosmic JS bucket settings 7 | write_key: '' // add write_key if added to your Cosmic JS bucket settings 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /dispatcher/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | // AppDispatcher.js 2 | import {Dispatcher} from 'flux'; 3 | import AppStore from '../stores/AppStore'; 4 | 5 | const AppDispatcher = new Dispatcher(); 6 | 7 | // Register callback with AppDispatcher 8 | AppDispatcher.register((payload) => { 9 | 10 | let action = payload.action; 11 | 12 | switch(action) { 13 | 14 | case 'init-app': 15 | AppStore.init(payload); 16 | break; 17 | 18 | case 'get-more-items': 19 | AppStore.getMoreItems(); 20 | break; 21 | 22 | default: 23 | return true; 24 | 25 | } 26 | 27 | return true; 28 | 29 | }); 30 | 31 | export default AppDispatcher; 32 | -------------------------------------------------------------------------------- /dist/css/clean-blog.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Clean Blog v1.0.0 (http://startbootstrap.com) 3 | * Copyright 2014 Start Bootstrap 4 | * Licensed under Apache 2.0 (https://github.com/IronSummitMedia/startbootstrap/blob/gh-pages/LICENSE) 5 | */ 6 | 7 | body { 8 | font-family: 'Lora', 'Times New Roman', serif; 9 | font-size: 20px; 10 | color: #404040; 11 | } 12 | p { 13 | line-height: 1.5; 14 | margin: 30px 0; 15 | } 16 | p a { 17 | text-decoration: underline; 18 | } 19 | h1, 20 | h2, 21 | h3, 22 | h4, 23 | h5, 24 | h6 { 25 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 26 | font-weight: 800; 27 | } 28 | a { 29 | color: #404040; 30 | } 31 | a:hover, 32 | a:focus { 33 | color: #0085a1; 34 | } 35 | a img:hover, 36 | a img:focus { 37 | cursor: zoom-in; 38 | } 39 | blockquote { 40 | color: #808080; 41 | font-style: italic; 42 | } 43 | hr.small { 44 | max-width: 100px; 45 | margin: 15px auto; 46 | border-width: 4px; 47 | border-color: white; 48 | } 49 | .navbar-custom { 50 | position: absolute; 51 | top: 0; 52 | left: 0; 53 | width: 100%; 54 | z-index: 3; 55 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 56 | } 57 | .navbar-custom .navbar-brand { 58 | font-weight: 800; 59 | } 60 | .navbar-custom .nav li a { 61 | text-transform: uppercase; 62 | font-size: 12px; 63 | font-weight: 800; 64 | letter-spacing: 1px; 65 | } 66 | @media only screen and (min-width: 768px) { 67 | .navbar-custom { 68 | background: transparent; 69 | border-bottom: 1px solid transparent; 70 | } 71 | .navbar-custom .navbar-brand { 72 | color: white; 73 | padding: 20px; 74 | } 75 | .navbar-custom .navbar-brand:hover, 76 | .navbar-custom .navbar-brand:focus { 77 | color: rgba(255, 255, 255, 0.8); 78 | } 79 | .navbar-custom .nav li a { 80 | color: white; 81 | padding: 20px; 82 | } 83 | .navbar-custom .nav li a:hover, 84 | .navbar-custom .nav li a:focus { 85 | color: rgba(255, 255, 255, 0.8); 86 | } 87 | } 88 | @media only screen and (min-width: 1170px) { 89 | .navbar-custom { 90 | -webkit-transition: background-color 0.3s; 91 | -moz-transition: background-color 0.3s; 92 | transition: background-color 0.3s; 93 | /* Force Hardware Acceleration in WebKit */ 94 | -webkit-transform: translate3d(0, 0, 0); 95 | -moz-transform: translate3d(0, 0, 0); 96 | -ms-transform: translate3d(0, 0, 0); 97 | -o-transform: translate3d(0, 0, 0); 98 | transform: translate3d(0, 0, 0); 99 | -webkit-backface-visibility: hidden; 100 | backface-visibility: hidden; 101 | } 102 | .navbar-custom.is-fixed { 103 | /* when the user scrolls down, we hide the header right above the viewport */ 104 | position: fixed; 105 | top: -61px; 106 | background-color: rgba(255, 255, 255, 0.9); 107 | border-bottom: 1px solid #f2f2f2; 108 | -webkit-transition: -webkit-transform 0.3s; 109 | -moz-transition: -moz-transform 0.3s; 110 | transition: transform 0.3s; 111 | } 112 | .navbar-custom.is-fixed .navbar-brand { 113 | color: #404040; 114 | } 115 | .navbar-custom.is-fixed .navbar-brand:hover, 116 | .navbar-custom.is-fixed .navbar-brand:focus { 117 | color: #0085a1; 118 | } 119 | .navbar-custom.is-fixed .nav li a { 120 | color: #404040; 121 | } 122 | .navbar-custom.is-fixed .nav li a:hover, 123 | .navbar-custom.is-fixed .nav li a:focus { 124 | color: #0085a1; 125 | } 126 | .navbar-custom.is-visible { 127 | /* if the user changes the scrolling direction, we show the header */ 128 | -webkit-transform: translate3d(0, 100%, 0); 129 | -moz-transform: translate3d(0, 100%, 0); 130 | -ms-transform: translate3d(0, 100%, 0); 131 | -o-transform: translate3d(0, 100%, 0); 132 | transform: translate3d(0, 100%, 0); 133 | } 134 | } 135 | .intro-header { 136 | background-color: #808080; 137 | background: no-repeat center center; 138 | background-attachment: scroll; 139 | -webkit-background-size: cover; 140 | -moz-background-size: cover; 141 | background-size: cover; 142 | -o-background-size: cover; 143 | margin-bottom: 50px; 144 | } 145 | .intro-header .site-heading, 146 | .intro-header .post-heading, 147 | .intro-header .page-heading { 148 | padding: 100px 0 50px; 149 | color: white; 150 | } 151 | @media only screen and (min-width: 768px) { 152 | .intro-header .site-heading, 153 | .intro-header .post-heading, 154 | .intro-header .page-heading { 155 | padding: 150px 0; 156 | } 157 | } 158 | .intro-header .site-heading, 159 | .intro-header .page-heading { 160 | text-align: center; 161 | } 162 | .intro-header .site-heading h1, 163 | .intro-header .page-heading h1 { 164 | margin-top: 0; 165 | font-size: 50px; 166 | } 167 | .intro-header .site-heading .subheading, 168 | .intro-header .page-heading .subheading { 169 | font-size: 24px; 170 | line-height: 1.1; 171 | display: block; 172 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 173 | font-weight: 300; 174 | margin: 10px 0 0; 175 | } 176 | @media only screen and (min-width: 768px) { 177 | .intro-header .site-heading h1, 178 | .intro-header .page-heading h1 { 179 | font-size: 80px; 180 | } 181 | } 182 | .intro-header .post-heading h1 { 183 | font-size: 35px; 184 | } 185 | .intro-header .post-heading .subheading, 186 | .intro-header .post-heading .meta { 187 | line-height: 1.1; 188 | display: block; 189 | } 190 | .intro-header .post-heading .subheading { 191 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 192 | font-size: 24px; 193 | margin: 10px 0 30px; 194 | font-weight: 600; 195 | } 196 | .intro-header .post-heading .meta { 197 | font-family: 'Lora', 'Times New Roman', serif; 198 | font-style: italic; 199 | font-weight: 300; 200 | font-size: 20px; 201 | } 202 | .intro-header .post-heading .meta a { 203 | color: white; 204 | } 205 | @media only screen and (min-width: 768px) { 206 | .intro-header .post-heading h1 { 207 | font-size: 55px; 208 | } 209 | .intro-header .post-heading .subheading { 210 | font-size: 30px; 211 | } 212 | } 213 | .post-preview > a { 214 | color: #404040; 215 | } 216 | .post-preview > a:hover, 217 | .post-preview > a:focus { 218 | text-decoration: none; 219 | color: #0085a1; 220 | } 221 | .post-preview > a > .post-title { 222 | font-size: 30px; 223 | margin-top: 30px; 224 | margin-bottom: 10px; 225 | } 226 | .post-preview > a > .post-subtitle { 227 | margin: 0; 228 | font-weight: 300; 229 | margin-bottom: 10px; 230 | } 231 | .post-preview > .post-meta { 232 | color: #808080; 233 | font-size: 18px; 234 | font-style: italic; 235 | margin-top: 0; 236 | } 237 | .post-preview > .post-meta > a { 238 | text-decoration: none; 239 | color: #404040; 240 | } 241 | .post-preview > .post-meta > a:hover, 242 | .post-preview > .post-meta > a:focus { 243 | color: #0085a1; 244 | text-decoration: underline; 245 | } 246 | @media only screen and (min-width: 768px) { 247 | .post-preview > a > .post-title { 248 | font-size: 36px; 249 | } 250 | } 251 | .section-heading { 252 | font-size: 36px; 253 | margin-top: 60px; 254 | font-weight: 700; 255 | } 256 | .caption { 257 | text-align: center; 258 | font-size: 14px; 259 | padding: 10px; 260 | font-style: italic; 261 | margin: 0; 262 | display: block; 263 | border-bottom-right-radius: 5px; 264 | border-bottom-left-radius: 5px; 265 | } 266 | footer { 267 | padding: 50px 0 65px; 268 | } 269 | footer .list-inline { 270 | margin: 0; 271 | padding: 0; 272 | } 273 | footer .copyright { 274 | font-size: 14px; 275 | text-align: center; 276 | margin-bottom: 0; 277 | } 278 | .floating-label-form-group { 279 | font-size: 14px; 280 | position: relative; 281 | margin-bottom: 0; 282 | padding-bottom: 0.5em; 283 | border-bottom: 1px solid #eeeeee; 284 | } 285 | .floating-label-form-group input, 286 | .floating-label-form-group textarea { 287 | z-index: 1; 288 | position: relative; 289 | padding-right: 0; 290 | padding-left: 0; 291 | border: none; 292 | border-radius: 0; 293 | font-size: 1.5em; 294 | background: none; 295 | box-shadow: none !important; 296 | resize: none; 297 | } 298 | .floating-label-form-group label { 299 | display: block; 300 | z-index: 0; 301 | position: relative; 302 | top: 2em; 303 | margin: 0; 304 | font-size: 0.85em; 305 | line-height: 1.764705882em; 306 | vertical-align: middle; 307 | vertical-align: baseline; 308 | opacity: 0; 309 | -webkit-transition: top 0.3s ease,opacity 0.3s ease; 310 | -moz-transition: top 0.3s ease,opacity 0.3s ease; 311 | -ms-transition: top 0.3s ease,opacity 0.3s ease; 312 | transition: top 0.3s ease,opacity 0.3s ease; 313 | } 314 | .floating-label-form-group::not(:first-child) { 315 | padding-left: 14px; 316 | border-left: 1px solid #eeeeee; 317 | } 318 | .floating-label-form-group-with-value label { 319 | top: 0; 320 | opacity: 1; 321 | } 322 | .floating-label-form-group-with-focus label { 323 | color: #0085a1; 324 | } 325 | form .row:first-child .floating-label-form-group { 326 | border-top: 1px solid #eeeeee; 327 | } 328 | .btn { 329 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 330 | text-transform: uppercase; 331 | font-size: 14px; 332 | font-weight: 800; 333 | letter-spacing: 1px; 334 | border-radius: 0; 335 | padding: 15px 25px; 336 | } 337 | .btn-lg { 338 | font-size: 16px; 339 | padding: 25px 35px; 340 | } 341 | .btn-default:hover, 342 | .btn-default:focus { 343 | background-color: #0085a1; 344 | border: 1px solid #0085a1; 345 | color: white; 346 | } 347 | .pager { 348 | margin: 20px 0 0; 349 | } 350 | .pager li > a, 351 | .pager li > span { 352 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 353 | text-transform: uppercase; 354 | font-size: 14px; 355 | font-weight: 800; 356 | letter-spacing: 1px; 357 | padding: 15px 25px; 358 | background-color: white; 359 | border-radius: 0; 360 | } 361 | .pager li > a:hover, 362 | .pager li > a:focus { 363 | color: white; 364 | background-color: #0085a1; 365 | border: 1px solid #0085a1; 366 | } 367 | .pager .disabled > a, 368 | .pager .disabled > a:hover, 369 | .pager .disabled > a:focus, 370 | .pager .disabled > span { 371 | color: #808080; 372 | background-color: #404040; 373 | cursor: not-allowed; 374 | } 375 | ::-moz-selection { 376 | color: white; 377 | text-shadow: none; 378 | background: #0085a1; 379 | } 380 | ::selection { 381 | color: white; 382 | text-shadow: none; 383 | background: #0085a1; 384 | } 385 | img::selection { 386 | color: white; 387 | background: transparent; 388 | } 389 | img::-moz-selection { 390 | color: white; 391 | background: transparent; 392 | } 393 | body { 394 | webkit-tap-highlight-color: #0085a1; 395 | } 396 | -------------------------------------------------------------------------------- /dist/css/clean-blog.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Clean Blog v1.0.0 (http://startbootstrap.com) 3 | * Copyright 2014 Start Bootstrap 4 | * Licensed under Apache 2.0 (https://github.com/IronSummitMedia/startbootstrap/blob/gh-pages/LICENSE) 5 | */ 6 | 7 | body{font-family:Lora,'Times New Roman',serif;font-size:20px;color:#404040}p{line-height:1.5;margin:30px 0}p a{text-decoration:underline}h1,h2,h3,h4,h5,h6{font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-weight:800}a{color:#404040}a:hover,a:focus{color:#0085a1}a img:hover,a img:focus{cursor:zoom-in}blockquote{color:gray;font-style:italic}hr.small{max-width:100px;margin:15px auto;border-width:4px;border-color:#fff}.navbar-custom{position:absolute;top:0;left:0;width:100%;z-index:3;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif}.navbar-custom .navbar-brand{font-weight:800}.navbar-custom .nav li a{text-transform:uppercase;font-size:12px;font-weight:800;letter-spacing:1px}@media only screen and (min-width:768px){.navbar-custom{background:0 0;border-bottom:1px solid transparent}.navbar-custom .navbar-brand{color:#fff;padding:20px}.navbar-custom .navbar-brand:hover,.navbar-custom .navbar-brand:focus{color:rgba(255,255,255,.8)}.navbar-custom .nav li a{color:#fff;padding:20px}.navbar-custom .nav li a:hover,.navbar-custom .nav li a:focus{color:rgba(255,255,255,.8)}}@media only screen and (min-width:1170px){.navbar-custom{-webkit-transition:background-color .3s;-moz-transition:background-color .3s;transition:background-color .3s;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.navbar-custom.is-fixed{position:fixed;top:-61px;background-color:rgba(255,255,255,.9);border-bottom:1px solid #f2f2f2;-webkit-transition:-webkit-transform .3s;-moz-transition:-moz-transform .3s;transition:transform .3s}.navbar-custom.is-fixed .navbar-brand{color:#404040}.navbar-custom.is-fixed .navbar-brand:hover,.navbar-custom.is-fixed .navbar-brand:focus{color:#0085a1}.navbar-custom.is-fixed .nav li a{color:#404040}.navbar-custom.is-fixed .nav li a:hover,.navbar-custom.is-fixed .nav li a:focus{color:#0085a1}.navbar-custom.is-visible{-webkit-transform:translate3d(0,100%,0);-moz-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);-o-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.intro-header{background-color:gray;background:no-repeat center center;background-attachment:scroll;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;-o-background-size:cover;margin-bottom:50px}.intro-header .site-heading,.intro-header .post-heading,.intro-header .page-heading{padding:100px 0 50px;color:#fff}@media only screen and (min-width:768px){.intro-header .site-heading,.intro-header .post-heading,.intro-header .page-heading{padding:150px 0}}.intro-header .site-heading,.intro-header .page-heading{text-align:center}.intro-header .site-heading h1,.intro-header .page-heading h1{margin-top:0;font-size:50px}.intro-header .site-heading .subheading,.intro-header .page-heading .subheading{font-size:24px;line-height:1.1;display:block;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-weight:300;margin:10px 0 0}@media only screen and (min-width:768px){.intro-header .site-heading h1,.intro-header .page-heading h1{font-size:80px}}.intro-header .post-heading h1{font-size:35px}.intro-header .post-heading .subheading,.intro-header .post-heading .meta{line-height:1.1;display:block}.intro-header .post-heading .subheading{font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:24px;margin:10px 0 30px;font-weight:600}.intro-header .post-heading .meta{font-family:Lora,'Times New Roman',serif;font-style:italic;font-weight:300;font-size:20px}.intro-header .post-heading .meta a{color:#fff}@media only screen and (min-width:768px){.intro-header .post-heading h1{font-size:55px}.intro-header .post-heading .subheading{font-size:30px}}.post-preview>a{color:#404040}.post-preview>a:hover,.post-preview>a:focus{text-decoration:none;color:#0085a1}.post-preview>a>.post-title{font-size:30px;margin-top:30px;margin-bottom:10px}.post-preview>a>.post-subtitle{margin:0;font-weight:300;margin-bottom:10px}.post-preview>.post-meta{color:gray;font-size:18px;font-style:italic;margin-top:0}.post-preview>.post-meta>a{text-decoration:none;color:#404040}.post-preview>.post-meta>a:hover,.post-preview>.post-meta>a:focus{color:#0085a1;text-decoration:underline}@media only screen and (min-width:768px){.post-preview>a>.post-title{font-size:36px}}.section-heading{font-size:36px;margin-top:60px;font-weight:700}.caption{text-align:center;font-size:14px;padding:10px;font-style:italic;margin:0;display:block;border-bottom-right-radius:5px;border-bottom-left-radius:5px}footer{padding:50px 0 65px}footer .list-inline{margin:0;padding:0}footer .copyright{font-size:14px;text-align:center;margin-bottom:0}.floating-label-form-group{font-size:14px;position:relative;margin-bottom:0;padding-bottom:.5em;border-bottom:1px solid #eee}.floating-label-form-group input,.floating-label-form-group textarea{z-index:1;position:relative;padding-right:0;padding-left:0;border:none;border-radius:0;font-size:1.5em;background:0 0;box-shadow:none!important;resize:none}.floating-label-form-group label{display:block;z-index:0;position:relative;top:2em;margin:0;font-size:.85em;line-height:1.764705882em;vertical-align:middle;vertical-align:baseline;opacity:0;-webkit-transition:top .3s ease,opacity .3s ease;-moz-transition:top .3s ease,opacity .3s ease;-ms-transition:top .3s ease,opacity .3s ease;transition:top .3s ease,opacity .3s ease}.floating-label-form-group::not(:first-child){padding-left:14px;border-left:1px solid #eee}.floating-label-form-group-with-value label{top:0;opacity:1}.floating-label-form-group-with-focus label{color:#0085a1}form .row:first-child .floating-label-form-group{border-top:1px solid #eee}.btn{font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;text-transform:uppercase;font-size:14px;font-weight:800;letter-spacing:1px;border-radius:0;padding:15px 25px}.btn-lg{font-size:16px;padding:25px 35px}.btn-default:hover,.btn-default:focus{background-color:#0085a1;border:1px solid #0085a1;color:#fff}.pager{margin:20px 0 0}.pager li>a,.pager li>span{font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;text-transform:uppercase;font-size:14px;font-weight:800;letter-spacing:1px;padding:15px 25px;background-color:#fff;border-radius:0}.pager li>a:hover,.pager li>a:focus{color:#fff;background-color:#0085a1;border:1px solid #0085a1}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:gray;background-color:#404040;cursor:not-allowed}::-moz-selection{color:#fff;text-shadow:none;background:#0085a1}::selection{color:#fff;text-shadow:none;background:#0085a1}img::selection{color:#fff;background:0 0}img::-moz-selection{color:#fff;background:0 0}body{webkit-tap-highlight-color:#0085a1} 8 | -------------------------------------------------------------------------------- /dist/css/cosmic-custom.css: -------------------------------------------------------------------------------- 1 | .pointer{ 2 | cursor: pointer; 3 | } 4 | .intro-header{ 5 | height: 500px; 6 | background-size: cover; 7 | } 8 | #main-content img{ 9 | max-width: 100%; 10 | } 11 | @media(max-width : 768px){ 12 | .intro-header{ 13 | height: 280px; 14 | } 15 | } 16 | .sk-circle { 17 | margin: 100px auto; 18 | width: 40px; 19 | height: 40px; 20 | position: relative; 21 | } 22 | .sk-circle .sk-child { 23 | width: 100%; 24 | height: 100%; 25 | position: absolute; 26 | left: 0; 27 | top: 0; 28 | } 29 | .sk-circle .sk-child:before { 30 | content: ''; 31 | display: block; 32 | margin: 0 auto; 33 | width: 15%; 34 | height: 15%; 35 | background-color: #00afd7; 36 | border-radius: 100%; 37 | -webkit-animation: sk-circleBounceDelay 1.2s infinite ease-in-out both; 38 | animation: sk-circleBounceDelay 1.2s infinite ease-in-out both; 39 | } 40 | .sk-circle .sk-circle2 { 41 | -webkit-transform: rotate(30deg); 42 | -ms-transform: rotate(30deg); 43 | transform: rotate(30deg); } 44 | .sk-circle .sk-circle3 { 45 | -webkit-transform: rotate(60deg); 46 | -ms-transform: rotate(60deg); 47 | transform: rotate(60deg); } 48 | .sk-circle .sk-circle4 { 49 | -webkit-transform: rotate(90deg); 50 | -ms-transform: rotate(90deg); 51 | transform: rotate(90deg); } 52 | .sk-circle .sk-circle5 { 53 | -webkit-transform: rotate(120deg); 54 | -ms-transform: rotate(120deg); 55 | transform: rotate(120deg); } 56 | .sk-circle .sk-circle6 { 57 | -webkit-transform: rotate(150deg); 58 | -ms-transform: rotate(150deg); 59 | transform: rotate(150deg); } 60 | .sk-circle .sk-circle7 { 61 | -webkit-transform: rotate(180deg); 62 | -ms-transform: rotate(180deg); 63 | transform: rotate(180deg); } 64 | .sk-circle .sk-circle8 { 65 | -webkit-transform: rotate(210deg); 66 | -ms-transform: rotate(210deg); 67 | transform: rotate(210deg); } 68 | .sk-circle .sk-circle9 { 69 | -webkit-transform: rotate(240deg); 70 | -ms-transform: rotate(240deg); 71 | transform: rotate(240deg); } 72 | .sk-circle .sk-circle10 { 73 | -webkit-transform: rotate(270deg); 74 | -ms-transform: rotate(270deg); 75 | transform: rotate(270deg); } 76 | .sk-circle .sk-circle11 { 77 | -webkit-transform: rotate(300deg); 78 | -ms-transform: rotate(300deg); 79 | transform: rotate(300deg); } 80 | .sk-circle .sk-circle12 { 81 | -webkit-transform: rotate(330deg); 82 | -ms-transform: rotate(330deg); 83 | transform: rotate(330deg); } 84 | .sk-circle .sk-circle2:before { 85 | -webkit-animation-delay: -1.1s; 86 | animation-delay: -1.1s; } 87 | .sk-circle .sk-circle3:before { 88 | -webkit-animation-delay: -1s; 89 | animation-delay: -1s; } 90 | .sk-circle .sk-circle4:before { 91 | -webkit-animation-delay: -0.9s; 92 | animation-delay: -0.9s; } 93 | .sk-circle .sk-circle5:before { 94 | -webkit-animation-delay: -0.8s; 95 | animation-delay: -0.8s; } 96 | .sk-circle .sk-circle6:before { 97 | -webkit-animation-delay: -0.7s; 98 | animation-delay: -0.7s; } 99 | .sk-circle .sk-circle7:before { 100 | -webkit-animation-delay: -0.6s; 101 | animation-delay: -0.6s; } 102 | .sk-circle .sk-circle8:before { 103 | -webkit-animation-delay: -0.5s; 104 | animation-delay: -0.5s; } 105 | .sk-circle .sk-circle9:before { 106 | -webkit-animation-delay: -0.4s; 107 | animation-delay: -0.4s; } 108 | .sk-circle .sk-circle10:before { 109 | -webkit-animation-delay: -0.3s; 110 | animation-delay: -0.3s; } 111 | .sk-circle .sk-circle11:before { 112 | -webkit-animation-delay: -0.2s; 113 | animation-delay: -0.2s; } 114 | .sk-circle .sk-circle12:before { 115 | -webkit-animation-delay: -0.1s; 116 | animation-delay: -0.1s; } 117 | 118 | @-webkit-keyframes sk-circleBounceDelay { 119 | 0%, 80%, 100% { 120 | -webkit-transform: scale(0); 121 | transform: scale(0); 122 | } 40% { 123 | -webkit-transform: scale(1); 124 | transform: scale(1); 125 | } 126 | } 127 | 128 | @keyframes sk-circleBounceDelay { 129 | 0%, 80%, 100% { 130 | -webkit-transform: scale(0); 131 | transform: scale(0); 132 | } 40% { 133 | -webkit-transform: scale(1); 134 | transform: scale(1); 135 | } 136 | } -------------------------------------------------------------------------------- /dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonyspiro/tonyspiro.com-react/1510acf6a110ed42d5fa41f914fc11705d7e1d8e/dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonyspiro/tonyspiro.com-react/1510acf6a110ed42d5fa41f914fc11705d7e1d8e/dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonyspiro/tonyspiro.com-react/1510acf6a110ed42d5fa41f914fc11705d7e1d8e/dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonyspiro/tonyspiro.com-react/1510acf6a110ed42d5fa41f914fc11705d7e1d8e/dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /dist/img/about-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonyspiro/tonyspiro.com-react/1510acf6a110ed42d5fa41f914fc11705d7e1d8e/dist/img/about-bg.jpg -------------------------------------------------------------------------------- /dist/img/contact-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonyspiro/tonyspiro.com-react/1510acf6a110ed42d5fa41f914fc11705d7e1d8e/dist/img/contact-bg.jpg -------------------------------------------------------------------------------- /dist/img/home-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonyspiro/tonyspiro.com-react/1510acf6a110ed42d5fa41f914fc11705d7e1d8e/dist/img/home-bg.jpg -------------------------------------------------------------------------------- /dist/img/post-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonyspiro/tonyspiro.com-react/1510acf6a110ed42d5fa41f914fc11705d7e1d8e/dist/img/post-bg.jpg -------------------------------------------------------------------------------- /dist/img/post-sample-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tonyspiro/tonyspiro.com-react/1510acf6a110ed42d5fa41f914fc11705d7e1d8e/dist/img/post-sample-image.jpg -------------------------------------------------------------------------------- /dist/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.4 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.4",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.4",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.4",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.4",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.4",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport),this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-mp.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type)})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.4",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.4",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.4",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=a(document.body).height();"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /dist/js/clean-blog.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Clean Blog v1.0.0 (http://startbootstrap.com) 3 | * Copyright 2014 Start Bootstrap 4 | * Licensed under Apache 2.0 (https://github.com/IronSummitMedia/startbootstrap/blob/gh-pages/LICENSE) 5 | */ 6 | 7 | // Contact Form Scripts 8 | 9 | $(function() { 10 | 11 | $("input,textarea").jqBootstrapValidation({ 12 | preventSubmit: true, 13 | submitError: function($form, event, errors) { 14 | // additional error messages or events 15 | }, 16 | submitSuccess: function($form, event) { 17 | event.preventDefault(); // prevent default submit behaviour 18 | // get values from FORM 19 | var name = $("input#name").val(); 20 | var email = $("input#email").val(); 21 | var phone = $("input#phone").val(); 22 | var message = $("textarea#message").val(); 23 | var firstName = name; // For Success/Failure Message 24 | // Check for white space in name for Success/Fail message 25 | if (firstName.indexOf(' ') >= 0) { 26 | firstName = name.split(' ').slice(0, -1).join(' '); 27 | } 28 | $.ajax({ 29 | url: "././mail/contact_me.php", 30 | type: "POST", 31 | data: { 32 | name: name, 33 | phone: phone, 34 | email: email, 35 | message: message 36 | }, 37 | cache: false, 38 | success: function() { 39 | // Success message 40 | $('#success').html("
    "); 41 | $('#success > .alert-success').html(""); 43 | $('#success > .alert-success') 44 | .append("Your message has been sent. "); 45 | $('#success > .alert-success') 46 | .append('
    '); 47 | 48 | //clear all fields 49 | $('#contactForm').trigger("reset"); 50 | }, 51 | error: function() { 52 | // Fail message 53 | $('#success').html("
    "); 54 | $('#success > .alert-danger').html(""); 56 | $('#success > .alert-danger').append("Sorry " + firstName + ", it seems that my mail server is not responding. Please try again later!"); 57 | $('#success > .alert-danger').append('
    '); 58 | //clear all fields 59 | $('#contactForm').trigger("reset"); 60 | }, 61 | }) 62 | }, 63 | filter: function() { 64 | return $(this).is(":visible"); 65 | }, 66 | }); 67 | 68 | $("a[data-toggle=\"tab\"]").click(function(e) { 69 | e.preventDefault(); 70 | $(this).tab("show"); 71 | }); 72 | }); 73 | 74 | 75 | /*When clicking on Full hide fail/success boxes */ 76 | $('#name').focus(function() { 77 | $('#success').html(''); 78 | }); 79 | 80 | // jqBootstrapValidation 81 | // * A plugin for automating validation on Twitter Bootstrap formatted forms. 82 | // * 83 | // * v1.3.6 84 | // * 85 | // * License: MIT - see LICENSE file 86 | // * 87 | // * http://ReactiveRaven.github.com/jqBootstrapValidation/ 88 | 89 | 90 | (function( $ ){ 91 | 92 | var createdElements = []; 93 | 94 | var defaults = { 95 | options: { 96 | prependExistingHelpBlock: false, 97 | sniffHtml: true, // sniff for 'required', 'maxlength', etc 98 | preventSubmit: true, // stop the form submit event from firing if validation fails 99 | submitError: false, // function called if there is an error when trying to submit 100 | submitSuccess: false, // function called just before a successful submit event is sent to the server 101 | semanticallyStrict: false, // set to true to tidy up generated HTML output 102 | autoAdd: { 103 | helpBlocks: true 104 | }, 105 | filter: function () { 106 | // return $(this).is(":visible"); // only validate elements you can see 107 | return true; // validate everything 108 | } 109 | }, 110 | methods: { 111 | init : function( options ) { 112 | 113 | var settings = $.extend(true, {}, defaults); 114 | 115 | settings.options = $.extend(true, settings.options, options); 116 | 117 | var $siblingElements = this; 118 | 119 | var uniqueForms = $.unique( 120 | $siblingElements.map( function () { 121 | return $(this).parents("form")[0]; 122 | }).toArray() 123 | ); 124 | 125 | $(uniqueForms).bind("submit", function (e) { 126 | var $form = $(this); 127 | var warningsFound = 0; 128 | var $inputs = $form.find("input,textarea,select").not("[type=submit],[type=image]").filter(settings.options.filter); 129 | $inputs.trigger("submit.validation").trigger("validationLostFocus.validation"); 130 | 131 | $inputs.each(function (i, el) { 132 | var $this = $(el), 133 | $controlGroup = $this.parents(".form-group").first(); 134 | if ( 135 | $controlGroup.hasClass("warning") 136 | ) { 137 | $controlGroup.removeClass("warning").addClass("error"); 138 | warningsFound++; 139 | } 140 | }); 141 | 142 | $inputs.trigger("validationLostFocus.validation"); 143 | 144 | if (warningsFound) { 145 | if (settings.options.preventSubmit) { 146 | e.preventDefault(); 147 | } 148 | $form.addClass("error"); 149 | if ($.isFunction(settings.options.submitError)) { 150 | settings.options.submitError($form, e, $inputs.jqBootstrapValidation("collectErrors", true)); 151 | } 152 | } else { 153 | $form.removeClass("error"); 154 | if ($.isFunction(settings.options.submitSuccess)) { 155 | settings.options.submitSuccess($form, e); 156 | } 157 | } 158 | }); 159 | 160 | return this.each(function(){ 161 | 162 | // Get references to everything we're interested in 163 | var $this = $(this), 164 | $controlGroup = $this.parents(".form-group").first(), 165 | $helpBlock = $controlGroup.find(".help-block").first(), 166 | $form = $this.parents("form").first(), 167 | validatorNames = []; 168 | 169 | // create message container if not exists 170 | if (!$helpBlock.length && settings.options.autoAdd && settings.options.autoAdd.helpBlocks) { 171 | $helpBlock = $('
    '); 172 | $controlGroup.find('.controls').append($helpBlock); 173 | createdElements.push($helpBlock[0]); 174 | } 175 | 176 | // ============================================================= 177 | // SNIFF HTML FOR VALIDATORS 178 | // ============================================================= 179 | 180 | // *snort sniff snuffle* 181 | 182 | if (settings.options.sniffHtml) { 183 | var message = ""; 184 | // --------------------------------------------------------- 185 | // PATTERN 186 | // --------------------------------------------------------- 187 | if ($this.attr("pattern") !== undefined) { 188 | message = "Not in the expected format"; 189 | if ($this.data("validationPatternMessage")) { 190 | message = $this.data("validationPatternMessage"); 191 | } 192 | $this.data("validationPatternMessage", message); 193 | $this.data("validationPatternRegex", $this.attr("pattern")); 194 | } 195 | // --------------------------------------------------------- 196 | // MAX 197 | // --------------------------------------------------------- 198 | if ($this.attr("max") !== undefined || $this.attr("aria-valuemax") !== undefined) { 199 | var max = ($this.attr("max") !== undefined ? $this.attr("max") : $this.attr("aria-valuemax")); 200 | message = "Too high: Maximum of '" + max + "'"; 201 | if ($this.data("validationMaxMessage")) { 202 | message = $this.data("validationMaxMessage"); 203 | } 204 | $this.data("validationMaxMessage", message); 205 | $this.data("validationMaxMax", max); 206 | } 207 | // --------------------------------------------------------- 208 | // MIN 209 | // --------------------------------------------------------- 210 | if ($this.attr("min") !== undefined || $this.attr("aria-valuemin") !== undefined) { 211 | var min = ($this.attr("min") !== undefined ? $this.attr("min") : $this.attr("aria-valuemin")); 212 | message = "Too low: Minimum of '" + min + "'"; 213 | if ($this.data("validationMinMessage")) { 214 | message = $this.data("validationMinMessage"); 215 | } 216 | $this.data("validationMinMessage", message); 217 | $this.data("validationMinMin", min); 218 | } 219 | // --------------------------------------------------------- 220 | // MAXLENGTH 221 | // --------------------------------------------------------- 222 | if ($this.attr("maxlength") !== undefined) { 223 | message = "Too long: Maximum of '" + $this.attr("maxlength") + "' characters"; 224 | if ($this.data("validationMaxlengthMessage")) { 225 | message = $this.data("validationMaxlengthMessage"); 226 | } 227 | $this.data("validationMaxlengthMessage", message); 228 | $this.data("validationMaxlengthMaxlength", $this.attr("maxlength")); 229 | } 230 | // --------------------------------------------------------- 231 | // MINLENGTH 232 | // --------------------------------------------------------- 233 | if ($this.attr("minlength") !== undefined) { 234 | message = "Too short: Minimum of '" + $this.attr("minlength") + "' characters"; 235 | if ($this.data("validationMinlengthMessage")) { 236 | message = $this.data("validationMinlengthMessage"); 237 | } 238 | $this.data("validationMinlengthMessage", message); 239 | $this.data("validationMinlengthMinlength", $this.attr("minlength")); 240 | } 241 | // --------------------------------------------------------- 242 | // REQUIRED 243 | // --------------------------------------------------------- 244 | if ($this.attr("required") !== undefined || $this.attr("aria-required") !== undefined) { 245 | message = settings.builtInValidators.required.message; 246 | if ($this.data("validationRequiredMessage")) { 247 | message = $this.data("validationRequiredMessage"); 248 | } 249 | $this.data("validationRequiredMessage", message); 250 | } 251 | // --------------------------------------------------------- 252 | // NUMBER 253 | // --------------------------------------------------------- 254 | if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "number") { 255 | message = settings.builtInValidators.number.message; 256 | if ($this.data("validationNumberMessage")) { 257 | message = $this.data("validationNumberMessage"); 258 | } 259 | $this.data("validationNumberMessage", message); 260 | } 261 | // --------------------------------------------------------- 262 | // EMAIL 263 | // --------------------------------------------------------- 264 | if ($this.attr("type") !== undefined && $this.attr("type").toLowerCase() === "email") { 265 | message = "Not a valid email address"; 266 | if ($this.data("validationValidemailMessage")) { 267 | message = $this.data("validationValidemailMessage"); 268 | } else if ($this.data("validationEmailMessage")) { 269 | message = $this.data("validationEmailMessage"); 270 | } 271 | $this.data("validationValidemailMessage", message); 272 | } 273 | // --------------------------------------------------------- 274 | // MINCHECKED 275 | // --------------------------------------------------------- 276 | if ($this.attr("minchecked") !== undefined) { 277 | message = "Not enough options checked; Minimum of '" + $this.attr("minchecked") + "' required"; 278 | if ($this.data("validationMincheckedMessage")) { 279 | message = $this.data("validationMincheckedMessage"); 280 | } 281 | $this.data("validationMincheckedMessage", message); 282 | $this.data("validationMincheckedMinchecked", $this.attr("minchecked")); 283 | } 284 | // --------------------------------------------------------- 285 | // MAXCHECKED 286 | // --------------------------------------------------------- 287 | if ($this.attr("maxchecked") !== undefined) { 288 | message = "Too many options checked; Maximum of '" + $this.attr("maxchecked") + "' required"; 289 | if ($this.data("validationMaxcheckedMessage")) { 290 | message = $this.data("validationMaxcheckedMessage"); 291 | } 292 | $this.data("validationMaxcheckedMessage", message); 293 | $this.data("validationMaxcheckedMaxchecked", $this.attr("maxchecked")); 294 | } 295 | } 296 | 297 | // ============================================================= 298 | // COLLECT VALIDATOR NAMES 299 | // ============================================================= 300 | 301 | // Get named validators 302 | if ($this.data("validation") !== undefined) { 303 | validatorNames = $this.data("validation").split(","); 304 | } 305 | 306 | // Get extra ones defined on the element's data attributes 307 | $.each($this.data(), function (i, el) { 308 | var parts = i.replace(/([A-Z])/g, ",$1").split(","); 309 | if (parts[0] === "validation" && parts[1]) { 310 | validatorNames.push(parts[1]); 311 | } 312 | }); 313 | 314 | // ============================================================= 315 | // NORMALISE VALIDATOR NAMES 316 | // ============================================================= 317 | 318 | var validatorNamesToInspect = validatorNames; 319 | var newValidatorNamesToInspect = []; 320 | 321 | do // repeatedly expand 'shortcut' validators into their real validators 322 | { 323 | // Uppercase only the first letter of each name 324 | $.each(validatorNames, function (i, el) { 325 | validatorNames[i] = formatValidatorName(el); 326 | }); 327 | 328 | // Remove duplicate validator names 329 | validatorNames = $.unique(validatorNames); 330 | 331 | // Pull out the new validator names from each shortcut 332 | newValidatorNamesToInspect = []; 333 | $.each(validatorNamesToInspect, function(i, el) { 334 | if ($this.data("validation" + el + "Shortcut") !== undefined) { 335 | // Are these custom validators? 336 | // Pull them out! 337 | $.each($this.data("validation" + el + "Shortcut").split(","), function(i2, el2) { 338 | newValidatorNamesToInspect.push(el2); 339 | }); 340 | } else if (settings.builtInValidators[el.toLowerCase()]) { 341 | // Is this a recognised built-in? 342 | // Pull it out! 343 | var validator = settings.builtInValidators[el.toLowerCase()]; 344 | if (validator.type.toLowerCase() === "shortcut") { 345 | $.each(validator.shortcut.split(","), function (i, el) { 346 | el = formatValidatorName(el); 347 | newValidatorNamesToInspect.push(el); 348 | validatorNames.push(el); 349 | }); 350 | } 351 | } 352 | }); 353 | 354 | validatorNamesToInspect = newValidatorNamesToInspect; 355 | 356 | } while (validatorNamesToInspect.length > 0) 357 | 358 | // ============================================================= 359 | // SET UP VALIDATOR ARRAYS 360 | // ============================================================= 361 | 362 | var validators = {}; 363 | 364 | $.each(validatorNames, function (i, el) { 365 | // Set up the 'override' message 366 | var message = $this.data("validation" + el + "Message"); 367 | var hasOverrideMessage = (message !== undefined); 368 | var foundValidator = false; 369 | message = 370 | ( 371 | message 372 | ? message 373 | : "'" + el + "' validation failed " 374 | ) 375 | ; 376 | 377 | $.each( 378 | settings.validatorTypes, 379 | function (validatorType, validatorTemplate) { 380 | if (validators[validatorType] === undefined) { 381 | validators[validatorType] = []; 382 | } 383 | if (!foundValidator && $this.data("validation" + el + formatValidatorName(validatorTemplate.name)) !== undefined) { 384 | validators[validatorType].push( 385 | $.extend( 386 | true, 387 | { 388 | name: formatValidatorName(validatorTemplate.name), 389 | message: message 390 | }, 391 | validatorTemplate.init($this, el) 392 | ) 393 | ); 394 | foundValidator = true; 395 | } 396 | } 397 | ); 398 | 399 | if (!foundValidator && settings.builtInValidators[el.toLowerCase()]) { 400 | 401 | var validator = $.extend(true, {}, settings.builtInValidators[el.toLowerCase()]); 402 | if (hasOverrideMessage) { 403 | validator.message = message; 404 | } 405 | var validatorType = validator.type.toLowerCase(); 406 | 407 | if (validatorType === "shortcut") { 408 | foundValidator = true; 409 | } else { 410 | $.each( 411 | settings.validatorTypes, 412 | function (validatorTemplateType, validatorTemplate) { 413 | if (validators[validatorTemplateType] === undefined) { 414 | validators[validatorTemplateType] = []; 415 | } 416 | if (!foundValidator && validatorType === validatorTemplateType.toLowerCase()) { 417 | $this.data("validation" + el + formatValidatorName(validatorTemplate.name), validator[validatorTemplate.name.toLowerCase()]); 418 | validators[validatorType].push( 419 | $.extend( 420 | validator, 421 | validatorTemplate.init($this, el) 422 | ) 423 | ); 424 | foundValidator = true; 425 | } 426 | } 427 | ); 428 | } 429 | } 430 | 431 | if (! foundValidator) { 432 | $.error("Cannot find validation info for '" + el + "'"); 433 | } 434 | }); 435 | 436 | // ============================================================= 437 | // STORE FALLBACK VALUES 438 | // ============================================================= 439 | 440 | $helpBlock.data( 441 | "original-contents", 442 | ( 443 | $helpBlock.data("original-contents") 444 | ? $helpBlock.data("original-contents") 445 | : $helpBlock.html() 446 | ) 447 | ); 448 | 449 | $helpBlock.data( 450 | "original-role", 451 | ( 452 | $helpBlock.data("original-role") 453 | ? $helpBlock.data("original-role") 454 | : $helpBlock.attr("role") 455 | ) 456 | ); 457 | 458 | $controlGroup.data( 459 | "original-classes", 460 | ( 461 | $controlGroup.data("original-clases") 462 | ? $controlGroup.data("original-classes") 463 | : $controlGroup.attr("class") 464 | ) 465 | ); 466 | 467 | $this.data( 468 | "original-aria-invalid", 469 | ( 470 | $this.data("original-aria-invalid") 471 | ? $this.data("original-aria-invalid") 472 | : $this.attr("aria-invalid") 473 | ) 474 | ); 475 | 476 | // ============================================================= 477 | // VALIDATION 478 | // ============================================================= 479 | 480 | $this.bind( 481 | "validation.validation", 482 | function (event, params) { 483 | 484 | var value = getValue($this); 485 | 486 | // Get a list of the errors to apply 487 | var errorsFound = []; 488 | 489 | $.each(validators, function (validatorType, validatorTypeArray) { 490 | if (value || value.length || (params && params.includeEmpty) || (!!settings.validatorTypes[validatorType].blockSubmit && params && !!params.submitting)) { 491 | $.each(validatorTypeArray, function (i, validator) { 492 | if (settings.validatorTypes[validatorType].validate($this, value, validator)) { 493 | errorsFound.push(validator.message); 494 | } 495 | }); 496 | } 497 | }); 498 | 499 | return errorsFound; 500 | } 501 | ); 502 | 503 | $this.bind( 504 | "getValidators.validation", 505 | function () { 506 | return validators; 507 | } 508 | ); 509 | 510 | // ============================================================= 511 | // WATCH FOR CHANGES 512 | // ============================================================= 513 | $this.bind( 514 | "submit.validation", 515 | function () { 516 | return $this.triggerHandler("change.validation", {submitting: true}); 517 | } 518 | ); 519 | $this.bind( 520 | [ 521 | "keyup", 522 | "focus", 523 | "blur", 524 | "click", 525 | "keydown", 526 | "keypress", 527 | "change" 528 | ].join(".validation ") + ".validation", 529 | function (e, params) { 530 | 531 | var value = getValue($this); 532 | 533 | var errorsFound = []; 534 | 535 | $controlGroup.find("input,textarea,select").each(function (i, el) { 536 | var oldCount = errorsFound.length; 537 | $.each($(el).triggerHandler("validation.validation", params), function (j, message) { 538 | errorsFound.push(message); 539 | }); 540 | if (errorsFound.length > oldCount) { 541 | $(el).attr("aria-invalid", "true"); 542 | } else { 543 | var original = $this.data("original-aria-invalid"); 544 | $(el).attr("aria-invalid", (original !== undefined ? original : false)); 545 | } 546 | }); 547 | 548 | $form.find("input,select,textarea").not($this).not("[name=\"" + $this.attr("name") + "\"]").trigger("validationLostFocus.validation"); 549 | 550 | errorsFound = $.unique(errorsFound.sort()); 551 | 552 | // Were there any errors? 553 | if (errorsFound.length) { 554 | // Better flag it up as a warning. 555 | $controlGroup.removeClass("success error").addClass("warning"); 556 | 557 | // How many errors did we find? 558 | if (settings.options.semanticallyStrict && errorsFound.length === 1) { 559 | // Only one? Being strict? Just output it. 560 | $helpBlock.html(errorsFound[0] + 561 | ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" )); 562 | } else { 563 | // Multiple? Being sloppy? Glue them together into an UL. 564 | $helpBlock.html("
    • " + errorsFound.join("
    • ") + "
    " + 565 | ( settings.options.prependExistingHelpBlock ? $helpBlock.data("original-contents") : "" )); 566 | } 567 | } else { 568 | $controlGroup.removeClass("warning error success"); 569 | if (value.length > 0) { 570 | $controlGroup.addClass("success"); 571 | } 572 | $helpBlock.html($helpBlock.data("original-contents")); 573 | } 574 | 575 | if (e.type === "blur") { 576 | $controlGroup.removeClass("success"); 577 | } 578 | } 579 | ); 580 | $this.bind("validationLostFocus.validation", function () { 581 | $controlGroup.removeClass("success"); 582 | }); 583 | }); 584 | }, 585 | destroy : function( ) { 586 | 587 | return this.each( 588 | function() { 589 | 590 | var 591 | $this = $(this), 592 | $controlGroup = $this.parents(".form-group").first(), 593 | $helpBlock = $controlGroup.find(".help-block").first(); 594 | 595 | // remove our events 596 | $this.unbind('.validation'); // events are namespaced. 597 | // reset help text 598 | $helpBlock.html($helpBlock.data("original-contents")); 599 | // reset classes 600 | $controlGroup.attr("class", $controlGroup.data("original-classes")); 601 | // reset aria 602 | $this.attr("aria-invalid", $this.data("original-aria-invalid")); 603 | // reset role 604 | $helpBlock.attr("role", $this.data("original-role")); 605 | // remove all elements we created 606 | if (createdElements.indexOf($helpBlock[0]) > -1) { 607 | $helpBlock.remove(); 608 | } 609 | 610 | } 611 | ); 612 | 613 | }, 614 | collectErrors : function(includeEmpty) { 615 | 616 | var errorMessages = {}; 617 | this.each(function (i, el) { 618 | var $el = $(el); 619 | var name = $el.attr("name"); 620 | var errors = $el.triggerHandler("validation.validation", {includeEmpty: true}); 621 | errorMessages[name] = $.extend(true, errors, errorMessages[name]); 622 | }); 623 | 624 | $.each(errorMessages, function (i, el) { 625 | if (el.length === 0) { 626 | delete errorMessages[i]; 627 | } 628 | }); 629 | 630 | return errorMessages; 631 | 632 | }, 633 | hasErrors: function() { 634 | 635 | var errorMessages = []; 636 | 637 | this.each(function (i, el) { 638 | errorMessages = errorMessages.concat( 639 | $(el).triggerHandler("getValidators.validation") ? $(el).triggerHandler("validation.validation", {submitting: true}) : [] 640 | ); 641 | }); 642 | 643 | return (errorMessages.length > 0); 644 | }, 645 | override : function (newDefaults) { 646 | defaults = $.extend(true, defaults, newDefaults); 647 | } 648 | }, 649 | validatorTypes: { 650 | callback: { 651 | name: "callback", 652 | init: function ($this, name) { 653 | return { 654 | validatorName: name, 655 | callback: $this.data("validation" + name + "Callback"), 656 | lastValue: $this.val(), 657 | lastValid: true, 658 | lastFinished: true 659 | }; 660 | }, 661 | validate: function ($this, value, validator) { 662 | if (validator.lastValue === value && validator.lastFinished) { 663 | return !validator.lastValid; 664 | } 665 | 666 | if (validator.lastFinished === true) 667 | { 668 | validator.lastValue = value; 669 | validator.lastValid = true; 670 | validator.lastFinished = false; 671 | 672 | var rrjqbvValidator = validator; 673 | var rrjqbvThis = $this; 674 | executeFunctionByName( 675 | validator.callback, 676 | window, 677 | $this, 678 | value, 679 | function (data) { 680 | if (rrjqbvValidator.lastValue === data.value) { 681 | rrjqbvValidator.lastValid = data.valid; 682 | if (data.message) { 683 | rrjqbvValidator.message = data.message; 684 | } 685 | rrjqbvValidator.lastFinished = true; 686 | rrjqbvThis.data("validation" + rrjqbvValidator.validatorName + "Message", rrjqbvValidator.message); 687 | // Timeout is set to avoid problems with the events being considered 'already fired' 688 | setTimeout(function () { 689 | rrjqbvThis.trigger("change.validation"); 690 | }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst 691 | } 692 | } 693 | ); 694 | } 695 | 696 | return false; 697 | 698 | } 699 | }, 700 | ajax: { 701 | name: "ajax", 702 | init: function ($this, name) { 703 | return { 704 | validatorName: name, 705 | url: $this.data("validation" + name + "Ajax"), 706 | lastValue: $this.val(), 707 | lastValid: true, 708 | lastFinished: true 709 | }; 710 | }, 711 | validate: function ($this, value, validator) { 712 | if (""+validator.lastValue === ""+value && validator.lastFinished === true) { 713 | return validator.lastValid === false; 714 | } 715 | 716 | if (validator.lastFinished === true) 717 | { 718 | validator.lastValue = value; 719 | validator.lastValid = true; 720 | validator.lastFinished = false; 721 | $.ajax({ 722 | url: validator.url, 723 | data: "value=" + value + "&field=" + $this.attr("name"), 724 | dataType: "json", 725 | success: function (data) { 726 | if (""+validator.lastValue === ""+data.value) { 727 | validator.lastValid = !!(data.valid); 728 | if (data.message) { 729 | validator.message = data.message; 730 | } 731 | validator.lastFinished = true; 732 | $this.data("validation" + validator.validatorName + "Message", validator.message); 733 | // Timeout is set to avoid problems with the events being considered 'already fired' 734 | setTimeout(function () { 735 | $this.trigger("change.validation"); 736 | }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst 737 | } 738 | }, 739 | failure: function () { 740 | validator.lastValid = true; 741 | validator.message = "ajax call failed"; 742 | validator.lastFinished = true; 743 | $this.data("validation" + validator.validatorName + "Message", validator.message); 744 | // Timeout is set to avoid problems with the events being considered 'already fired' 745 | setTimeout(function () { 746 | $this.trigger("change.validation"); 747 | }, 1); // doesn't need a long timeout, just long enough for the event bubble to burst 748 | } 749 | }); 750 | } 751 | 752 | return false; 753 | 754 | } 755 | }, 756 | regex: { 757 | name: "regex", 758 | init: function ($this, name) { 759 | return {regex: regexFromString($this.data("validation" + name + "Regex"))}; 760 | }, 761 | validate: function ($this, value, validator) { 762 | return (!validator.regex.test(value) && ! validator.negative) 763 | || (validator.regex.test(value) && validator.negative); 764 | } 765 | }, 766 | required: { 767 | name: "required", 768 | init: function ($this, name) { 769 | return {}; 770 | }, 771 | validate: function ($this, value, validator) { 772 | return !!(value.length === 0 && ! validator.negative) 773 | || !!(value.length > 0 && validator.negative); 774 | }, 775 | blockSubmit: true 776 | }, 777 | match: { 778 | name: "match", 779 | init: function ($this, name) { 780 | var element = $this.parents("form").first().find("[name=\"" + $this.data("validation" + name + "Match") + "\"]").first(); 781 | element.bind("validation.validation", function () { 782 | $this.trigger("change.validation", {submitting: true}); 783 | }); 784 | return {"element": element}; 785 | }, 786 | validate: function ($this, value, validator) { 787 | return (value !== validator.element.val() && ! validator.negative) 788 | || (value === validator.element.val() && validator.negative); 789 | }, 790 | blockSubmit: true 791 | }, 792 | max: { 793 | name: "max", 794 | init: function ($this, name) { 795 | return {max: $this.data("validation" + name + "Max")}; 796 | }, 797 | validate: function ($this, value, validator) { 798 | return (parseFloat(value, 10) > parseFloat(validator.max, 10) && ! validator.negative) 799 | || (parseFloat(value, 10) <= parseFloat(validator.max, 10) && validator.negative); 800 | } 801 | }, 802 | min: { 803 | name: "min", 804 | init: function ($this, name) { 805 | return {min: $this.data("validation" + name + "Min")}; 806 | }, 807 | validate: function ($this, value, validator) { 808 | return (parseFloat(value) < parseFloat(validator.min) && ! validator.negative) 809 | || (parseFloat(value) >= parseFloat(validator.min) && validator.negative); 810 | } 811 | }, 812 | maxlength: { 813 | name: "maxlength", 814 | init: function ($this, name) { 815 | return {maxlength: $this.data("validation" + name + "Maxlength")}; 816 | }, 817 | validate: function ($this, value, validator) { 818 | return ((value.length > validator.maxlength) && ! validator.negative) 819 | || ((value.length <= validator.maxlength) && validator.negative); 820 | } 821 | }, 822 | minlength: { 823 | name: "minlength", 824 | init: function ($this, name) { 825 | return {minlength: $this.data("validation" + name + "Minlength")}; 826 | }, 827 | validate: function ($this, value, validator) { 828 | return ((value.length < validator.minlength) && ! validator.negative) 829 | || ((value.length >= validator.minlength) && validator.negative); 830 | } 831 | }, 832 | maxchecked: { 833 | name: "maxchecked", 834 | init: function ($this, name) { 835 | var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]"); 836 | elements.bind("click.validation", function () { 837 | $this.trigger("change.validation", {includeEmpty: true}); 838 | }); 839 | return {maxchecked: $this.data("validation" + name + "Maxchecked"), elements: elements}; 840 | }, 841 | validate: function ($this, value, validator) { 842 | return (validator.elements.filter(":checked").length > validator.maxchecked && ! validator.negative) 843 | || (validator.elements.filter(":checked").length <= validator.maxchecked && validator.negative); 844 | }, 845 | blockSubmit: true 846 | }, 847 | minchecked: { 848 | name: "minchecked", 849 | init: function ($this, name) { 850 | var elements = $this.parents("form").first().find("[name=\"" + $this.attr("name") + "\"]"); 851 | elements.bind("click.validation", function () { 852 | $this.trigger("change.validation", {includeEmpty: true}); 853 | }); 854 | return {minchecked: $this.data("validation" + name + "Minchecked"), elements: elements}; 855 | }, 856 | validate: function ($this, value, validator) { 857 | return (validator.elements.filter(":checked").length < validator.minchecked && ! validator.negative) 858 | || (validator.elements.filter(":checked").length >= validator.minchecked && validator.negative); 859 | }, 860 | blockSubmit: true 861 | } 862 | }, 863 | builtInValidators: { 864 | email: { 865 | name: "Email", 866 | type: "shortcut", 867 | shortcut: "validemail" 868 | }, 869 | validemail: { 870 | name: "Validemail", 871 | type: "regex", 872 | regex: "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\\.[A-Za-z]{2,4}", 873 | message: "Not a valid email address" 874 | }, 875 | passwordagain: { 876 | name: "Passwordagain", 877 | type: "match", 878 | match: "password", 879 | message: "Does not match the given password" 880 | }, 881 | positive: { 882 | name: "Positive", 883 | type: "shortcut", 884 | shortcut: "number,positivenumber" 885 | }, 886 | negative: { 887 | name: "Negative", 888 | type: "shortcut", 889 | shortcut: "number,negativenumber" 890 | }, 891 | number: { 892 | name: "Number", 893 | type: "regex", 894 | regex: "([+-]?\\\d+(\\\.\\\d*)?([eE][+-]?[0-9]+)?)?", 895 | message: "Must be a number" 896 | }, 897 | integer: { 898 | name: "Integer", 899 | type: "regex", 900 | regex: "[+-]?\\\d+", 901 | message: "No decimal places allowed" 902 | }, 903 | positivenumber: { 904 | name: "Positivenumber", 905 | type: "min", 906 | min: 0, 907 | message: "Must be a positive number" 908 | }, 909 | negativenumber: { 910 | name: "Negativenumber", 911 | type: "max", 912 | max: 0, 913 | message: "Must be a negative number" 914 | }, 915 | required: { 916 | name: "Required", 917 | type: "required", 918 | message: "This is required" 919 | }, 920 | checkone: { 921 | name: "Checkone", 922 | type: "minchecked", 923 | minchecked: 1, 924 | message: "Check at least one option" 925 | } 926 | } 927 | }; 928 | 929 | var formatValidatorName = function (name) { 930 | return name 931 | .toLowerCase() 932 | .replace( 933 | /(^|\s)([a-z])/g , 934 | function(m,p1,p2) { 935 | return p1+p2.toUpperCase(); 936 | } 937 | ) 938 | ; 939 | }; 940 | 941 | var getValue = function ($this) { 942 | // Extract the value we're talking about 943 | var value = $this.val(); 944 | var type = $this.attr("type"); 945 | if (type === "checkbox") { 946 | value = ($this.is(":checked") ? value : ""); 947 | } 948 | if (type === "radio") { 949 | value = ($('input[name="' + $this.attr("name") + '"]:checked').length > 0 ? value : ""); 950 | } 951 | return value; 952 | }; 953 | 954 | function regexFromString(inputstring) { 955 | return new RegExp("^" + inputstring + "$"); 956 | } 957 | 958 | /** 959 | * Thanks to Jason Bunting via StackOverflow.com 960 | * 961 | * http://stackoverflow.com/questions/359788/how-to-execute-a-javascript-function-when-i-have-its-name-as-a-string#answer-359910 962 | * Short link: http://tinyurl.com/executeFunctionByName 963 | **/ 964 | function executeFunctionByName(functionName, context /*, args*/) { 965 | var args = Array.prototype.slice.call(arguments).splice(2); 966 | var namespaces = functionName.split("."); 967 | var func = namespaces.pop(); 968 | for(var i = 0; i < namespaces.length; i++) { 969 | context = context[namespaces[i]]; 970 | } 971 | return context[func].apply(this, args); 972 | } 973 | 974 | $.fn.jqBootstrapValidation = function( method ) { 975 | 976 | if ( defaults.methods[method] ) { 977 | return defaults.methods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); 978 | } else if ( typeof method === 'object' || ! method ) { 979 | return defaults.methods.init.apply( this, arguments ); 980 | } else { 981 | $.error( 'Method ' + method + ' does not exist on jQuery.jqBootstrapValidation' ); 982 | return null; 983 | } 984 | 985 | }; 986 | 987 | $.jqBootstrapValidation = function (options) { 988 | $(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments); 989 | }; 990 | 991 | })( jQuery ); 992 | 993 | // Floating label headings for the contact form 994 | $(function() { 995 | $("body").on("input propertychange", ".floating-label-form-group", function(e) { 996 | $(this).toggleClass("floating-label-form-group-with-value", !!$(e.target).val()); 997 | }).on("focus", ".floating-label-form-group", function() { 998 | $(this).addClass("floating-label-form-group-with-focus"); 999 | }).on("blur", ".floating-label-form-group", function() { 1000 | $(this).removeClass("floating-label-form-group-with-focus"); 1001 | }); 1002 | }); 1003 | 1004 | // Navigation Scripts to Show Header on Scroll-Up 1005 | jQuery(document).ready(function($) { 1006 | var MQL = 1170; 1007 | 1008 | //primary navigation slide-in effect 1009 | if ($(window).width() > MQL) { 1010 | var headerHeight = $('.navbar-custom').height(); 1011 | $(window).on('scroll', { 1012 | previousTop: 0 1013 | }, 1014 | function() { 1015 | var currentTop = $(window).scrollTop(); 1016 | //check if user is scrolling up 1017 | if (currentTop < this.previousTop) { 1018 | //if scrolling up... 1019 | if (currentTop > 0 && $('.navbar-custom').hasClass('is-fixed')) { 1020 | $('.navbar-custom').addClass('is-visible'); 1021 | } else { 1022 | $('.navbar-custom').removeClass('is-visible is-fixed'); 1023 | } 1024 | } else { 1025 | //if scrolling down... 1026 | $('.navbar-custom').removeClass('is-visible'); 1027 | if (currentTop > headerHeight && !$('.navbar-custom').hasClass('is-fixed')) $('.navbar-custom').addClass('is-fixed'); 1028 | } 1029 | this.previousTop = currentTop; 1030 | }); 1031 | } 1032 | }); 1033 | -------------------------------------------------------------------------------- /dist/js/clean-blog.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Clean Blog v1.0.0 (http://startbootstrap.com) 3 | * Copyright 2014 Start Bootstrap 4 | * Licensed under Apache 2.0 (https://github.com/IronSummitMedia/startbootstrap/blob/gh-pages/LICENSE) 5 | */ 6 | 7 | $(function(){$("input,textarea").jqBootstrapValidation({preventSubmit:!0,submitError:function(){},submitSuccess:function(a,b){b.preventDefault();var c=$("input#name").val(),d=$("input#email").val(),e=$("input#phone").val(),f=$("textarea#message").val(),g=c;g.indexOf(" ")>=0&&(g=c.split(" ").slice(0,-1).join(" ")),$.ajax({url:"././mail/contact_me.php",type:"POST",data:{name:c,phone:e,email:d,message:f},cache:!1,success:function(){$("#success").html("
    "),$("#success > .alert-success").html(""),$("#success > .alert-success").append("Your message has been sent. "),$("#success > .alert-success").append("
    "),$("#contactForm").trigger("reset")},error:function(){$("#success").html("
    "),$("#success > .alert-danger").html(""),$("#success > .alert-danger").append("Sorry "+g+", it seems that my mail server is not responding. Please try again later!"),$("#success > .alert-danger").append("
    "),$("#contactForm").trigger("reset")}})},filter:function(){return $(this).is(":visible")}}),$('a[data-toggle="tab"]').click(function(a){a.preventDefault(),$(this).tab("show")})}),$("#name").focus(function(){$("#success").html("")}),function(a){function b(a){return new RegExp("^"+a+"$")}function c(a,b){for(var c=Array.prototype.slice.call(arguments).splice(2),d=a.split("."),e=d.pop(),f=0;f'),e.find(".controls").append(h),d.push(h[0])),c.options.sniffHtml){var k="";if(void 0!==b.attr("pattern")&&(k="Not in the expected format",b.data("validationPatternMessage")&&(k=b.data("validationPatternMessage")),b.data("validationPatternMessage",k),b.data("validationPatternRegex",b.attr("pattern"))),void 0!==b.attr("max")||void 0!==b.attr("aria-valuemax")){var l=b.attr(void 0!==b.attr("max")?"max":"aria-valuemax");k="Too high: Maximum of '"+l+"'",b.data("validationMaxMessage")&&(k=b.data("validationMaxMessage")),b.data("validationMaxMessage",k),b.data("validationMaxMax",l)}if(void 0!==b.attr("min")||void 0!==b.attr("aria-valuemin")){var m=b.attr(void 0!==b.attr("min")?"min":"aria-valuemin");k="Too low: Minimum of '"+m+"'",b.data("validationMinMessage")&&(k=b.data("validationMinMessage")),b.data("validationMinMessage",k),b.data("validationMinMin",m)}void 0!==b.attr("maxlength")&&(k="Too long: Maximum of '"+b.attr("maxlength")+"' characters",b.data("validationMaxlengthMessage")&&(k=b.data("validationMaxlengthMessage")),b.data("validationMaxlengthMessage",k),b.data("validationMaxlengthMaxlength",b.attr("maxlength"))),void 0!==b.attr("minlength")&&(k="Too short: Minimum of '"+b.attr("minlength")+"' characters",b.data("validationMinlengthMessage")&&(k=b.data("validationMinlengthMessage")),b.data("validationMinlengthMessage",k),b.data("validationMinlengthMinlength",b.attr("minlength"))),(void 0!==b.attr("required")||void 0!==b.attr("aria-required"))&&(k=c.builtInValidators.required.message,b.data("validationRequiredMessage")&&(k=b.data("validationRequiredMessage")),b.data("validationRequiredMessage",k)),void 0!==b.attr("type")&&"number"===b.attr("type").toLowerCase()&&(k=c.builtInValidators.number.message,b.data("validationNumberMessage")&&(k=b.data("validationNumberMessage")),b.data("validationNumberMessage",k)),void 0!==b.attr("type")&&"email"===b.attr("type").toLowerCase()&&(k="Not a valid email address",b.data("validationValidemailMessage")?k=b.data("validationValidemailMessage"):b.data("validationEmailMessage")&&(k=b.data("validationEmailMessage")),b.data("validationValidemailMessage",k)),void 0!==b.attr("minchecked")&&(k="Not enough options checked; Minimum of '"+b.attr("minchecked")+"' required",b.data("validationMincheckedMessage")&&(k=b.data("validationMincheckedMessage")),b.data("validationMincheckedMessage",k),b.data("validationMincheckedMinchecked",b.attr("minchecked"))),void 0!==b.attr("maxchecked")&&(k="Too many options checked; Maximum of '"+b.attr("maxchecked")+"' required",b.data("validationMaxcheckedMessage")&&(k=b.data("validationMaxcheckedMessage")),b.data("validationMaxcheckedMessage",k),b.data("validationMaxcheckedMaxchecked",b.attr("maxchecked")))}void 0!==b.data("validation")&&(j=b.data("validation").split(",")),a.each(b.data(),function(a){var b=a.replace(/([A-Z])/g,",$1").split(",");"validation"===b[0]&&b[1]&&j.push(b[1])});var n=j,o=[];do a.each(j,function(a,b){j[a]=f(b)}),j=a.unique(j),o=[],a.each(n,function(d,e){if(void 0!==b.data("validation"+e+"Shortcut"))a.each(b.data("validation"+e+"Shortcut").split(","),function(a,b){o.push(b)});else if(c.builtInValidators[e.toLowerCase()]){var g=c.builtInValidators[e.toLowerCase()];"shortcut"===g.type.toLowerCase()&&a.each(g.shortcut.split(","),function(a,b){b=f(b),o.push(b),j.push(b)})}}),n=o;while(n.length>0);var p={};a.each(j,function(d,e){var g=b.data("validation"+e+"Message"),h=void 0!==g,i=!1;if(g=g?g:"'"+e+"' validation failed ",a.each(c.validatorTypes,function(c,d){void 0===p[c]&&(p[c]=[]),i||void 0===b.data("validation"+e+f(d.name))||(p[c].push(a.extend(!0,{name:f(d.name),message:g},d.init(b,e))),i=!0)}),!i&&c.builtInValidators[e.toLowerCase()]){var j=a.extend(!0,{},c.builtInValidators[e.toLowerCase()]);h&&(j.message=g);var k=j.type.toLowerCase();"shortcut"===k?i=!0:a.each(c.validatorTypes,function(c,d){void 0===p[c]&&(p[c]=[]),i||k!==c.toLowerCase()||(b.data("validation"+e+f(d.name),j[d.name.toLowerCase()]),p[k].push(a.extend(j,d.init(b,e))),i=!0)})}i||a.error("Cannot find validation info for '"+e+"'")}),h.data("original-contents",h.data("original-contents")?h.data("original-contents"):h.html()),h.data("original-role",h.data("original-role")?h.data("original-role"):h.attr("role")),e.data("original-classes",e.data("original-clases")?e.data("original-classes"):e.attr("class")),b.data("original-aria-invalid",b.data("original-aria-invalid")?b.data("original-aria-invalid"):b.attr("aria-invalid")),b.bind("validation.validation",function(d,e){var f=g(b),h=[];return a.each(p,function(d,g){(f||f.length||e&&e.includeEmpty||c.validatorTypes[d].blockSubmit&&e&&e.submitting)&&a.each(g,function(a,e){c.validatorTypes[d].validate(b,f,e)&&h.push(e.message)})}),h}),b.bind("getValidators.validation",function(){return p}),b.bind("submit.validation",function(){return b.triggerHandler("change.validation",{submitting:!0})}),b.bind(["keyup","focus","blur","click","keydown","keypress","change"].join(".validation ")+".validation",function(d,f){var j=g(b),k=[];e.find("input,textarea,select").each(function(c,d){var e=k.length;if(a.each(a(d).triggerHandler("validation.validation",f),function(a,b){k.push(b)}),k.length>e)a(d).attr("aria-invalid","true");else{var g=b.data("original-aria-invalid");a(d).attr("aria-invalid",void 0!==g?g:!1)}}),i.find("input,select,textarea").not(b).not('[name="'+b.attr("name")+'"]').trigger("validationLostFocus.validation"),k=a.unique(k.sort()),k.length?(e.removeClass("success error").addClass("warning"),h.html(c.options.semanticallyStrict&&1===k.length?k[0]+(c.options.prependExistingHelpBlock?h.data("original-contents"):""):'
    • '+k.join("
    • ")+"
    "+(c.options.prependExistingHelpBlock?h.data("original-contents"):""))):(e.removeClass("warning error success"),j.length>0&&e.addClass("success"),h.html(h.data("original-contents"))),"blur"===d.type&&e.removeClass("success")}),b.bind("validationLostFocus.validation",function(){e.removeClass("success")})})},destroy:function(){return this.each(function(){var b=a(this),c=b.parents(".form-group").first(),e=c.find(".help-block").first();b.unbind(".validation"),e.html(e.data("original-contents")),c.attr("class",c.data("original-classes")),b.attr("aria-invalid",b.data("original-aria-invalid")),e.attr("role",b.data("original-role")),d.indexOf(e[0])>-1&&e.remove()})},collectErrors:function(){var b={};return this.each(function(c,d){var e=a(d),f=e.attr("name"),g=e.triggerHandler("validation.validation",{includeEmpty:!0});b[f]=a.extend(!0,g,b[f])}),a.each(b,function(a,c){0===c.length&&delete b[a]}),b},hasErrors:function(){var b=[];return this.each(function(c,d){b=b.concat(a(d).triggerHandler("getValidators.validation")?a(d).triggerHandler("validation.validation",{submitting:!0}):[])}),b.length>0},override:function(b){e=a.extend(!0,e,b)}},validatorTypes:{callback:{name:"callback",init:function(a,b){return{validatorName:b,callback:a.data("validation"+b+"Callback"),lastValue:a.val(),lastValid:!0,lastFinished:!0}},validate:function(a,b,d){if(d.lastValue===b&&d.lastFinished)return!d.lastValid;if(d.lastFinished===!0){d.lastValue=b,d.lastValid=!0,d.lastFinished=!1;var e=d,f=a;c(d.callback,window,a,b,function(a){e.lastValue===a.value&&(e.lastValid=a.valid,a.message&&(e.message=a.message),e.lastFinished=!0,f.data("validation"+e.validatorName+"Message",e.message),setTimeout(function(){f.trigger("change.validation")},1))})}return!1}},ajax:{name:"ajax",init:function(a,b){return{validatorName:b,url:a.data("validation"+b+"Ajax"),lastValue:a.val(),lastValid:!0,lastFinished:!0}},validate:function(b,c,d){return""+d.lastValue==""+c&&d.lastFinished===!0?d.lastValid===!1:(d.lastFinished===!0&&(d.lastValue=c,d.lastValid=!0,d.lastFinished=!1,a.ajax({url:d.url,data:"value="+c+"&field="+b.attr("name"),dataType:"json",success:function(a){""+d.lastValue==""+a.value&&(d.lastValid=!!a.valid,a.message&&(d.message=a.message),d.lastFinished=!0,b.data("validation"+d.validatorName+"Message",d.message),setTimeout(function(){b.trigger("change.validation")},1))},failure:function(){d.lastValid=!0,d.message="ajax call failed",d.lastFinished=!0,b.data("validation"+d.validatorName+"Message",d.message),setTimeout(function(){b.trigger("change.validation")},1)}})),!1)}},regex:{name:"regex",init:function(a,c){return{regex:b(a.data("validation"+c+"Regex"))}},validate:function(a,b,c){return!c.regex.test(b)&&!c.negative||c.regex.test(b)&&c.negative}},required:{name:"required",init:function(){return{}},validate:function(a,b,c){return!(0!==b.length||c.negative)||!!(b.length>0&&c.negative)},blockSubmit:!0},match:{name:"match",init:function(a,b){var c=a.parents("form").first().find('[name="'+a.data("validation"+b+"Match")+'"]').first();return c.bind("validation.validation",function(){a.trigger("change.validation",{submitting:!0})}),{element:c}},validate:function(a,b,c){return b!==c.element.val()&&!c.negative||b===c.element.val()&&c.negative},blockSubmit:!0},max:{name:"max",init:function(a,b){return{max:a.data("validation"+b+"Max")}},validate:function(a,b,c){return parseFloat(b,10)>parseFloat(c.max,10)&&!c.negative||parseFloat(b,10)<=parseFloat(c.max,10)&&c.negative}},min:{name:"min",init:function(a,b){return{min:a.data("validation"+b+"Min")}},validate:function(a,b,c){return parseFloat(b)=parseFloat(c.min)&&c.negative}},maxlength:{name:"maxlength",init:function(a,b){return{maxlength:a.data("validation"+b+"Maxlength")}},validate:function(a,b,c){return b.length>c.maxlength&&!c.negative||b.length<=c.maxlength&&c.negative}},minlength:{name:"minlength",init:function(a,b){return{minlength:a.data("validation"+b+"Minlength")}},validate:function(a,b,c){return b.length=c.minlength&&c.negative}},maxchecked:{name:"maxchecked",init:function(a,b){var c=a.parents("form").first().find('[name="'+a.attr("name")+'"]');return c.bind("click.validation",function(){a.trigger("change.validation",{includeEmpty:!0})}),{maxchecked:a.data("validation"+b+"Maxchecked"),elements:c}},validate:function(a,b,c){return c.elements.filter(":checked").length>c.maxchecked&&!c.negative||c.elements.filter(":checked").length<=c.maxchecked&&c.negative},blockSubmit:!0},minchecked:{name:"minchecked",init:function(a,b){var c=a.parents("form").first().find('[name="'+a.attr("name")+'"]');return c.bind("click.validation",function(){a.trigger("change.validation",{includeEmpty:!0})}),{minchecked:a.data("validation"+b+"Minchecked"),elements:c}},validate:function(a,b,c){return c.elements.filter(":checked").length=c.minchecked&&c.negative},blockSubmit:!0}},builtInValidators:{email:{name:"Email",type:"shortcut",shortcut:"validemail"},validemail:{name:"Validemail",type:"regex",regex:"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}",message:"Not a valid email address"},passwordagain:{name:"Passwordagain",type:"match",match:"password",message:"Does not match the given password"},positive:{name:"Positive",type:"shortcut",shortcut:"number,positivenumber"},negative:{name:"Negative",type:"shortcut",shortcut:"number,negativenumber"},number:{name:"Number",type:"regex",regex:"([+-]?\\d+(\\.\\d*)?([eE][+-]?[0-9]+)?)?",message:"Must be a number"},integer:{name:"Integer",type:"regex",regex:"[+-]?\\d+",message:"No decimal places allowed"},positivenumber:{name:"Positivenumber",type:"min",min:0,message:"Must be a positive number"},negativenumber:{name:"Negativenumber",type:"max",max:0,message:"Must be a negative number"},required:{name:"Required",type:"required",message:"This is required"},checkone:{name:"Checkone",type:"minchecked",minchecked:1,message:"Check at least one option"}}},f=function(a){return a.toLowerCase().replace(/(^|\s)([a-z])/g,function(a,b,c){return b+c.toUpperCase()})},g=function(b){var c=b.val(),d=b.attr("type");return"checkbox"===d&&(c=b.is(":checked")?c:""),"radio"===d&&(c=a('input[name="'+b.attr("name")+'"]:checked').length>0?c:""),c};a.fn.jqBootstrapValidation=function(b){return e.methods[b]?e.methods[b].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof b&&b?(a.error("Method "+b+" does not exist on jQuery.jqBootstrapValidation"),null):e.methods.init.apply(this,arguments)},a.jqBootstrapValidation=function(){a(":input").not("[type=image],[type=submit]").jqBootstrapValidation.apply(this,arguments)}}(jQuery),$(function(){$("body").on("input propertychange",".floating-label-form-group",function(a){$(this).toggleClass("floating-label-form-group-with-value",!!$(a.target).val())}).on("focus",".floating-label-form-group",function(){$(this).addClass("floating-label-form-group-with-focus")}).on("blur",".floating-label-form-group",function(){$(this).removeClass("floating-label-form-group-with-focus")})}),jQuery(document).ready(function(a){var b=1170;if(a(window).width()>b){var c=a(".navbar-custom").height();a(window).on("scroll",{previousTop:0},function(){var b=a(window).scrollTop();b0&&a(".navbar-custom").hasClass("is-fixed")?a(".navbar-custom").addClass("is-visible"):a(".navbar-custom").removeClass("is-visible is-fixed"):(a(".navbar-custom").removeClass("is-visible"),b>c&&!a(".navbar-custom").hasClass("is-fixed")&&a(".navbar-custom").addClass("is-fixed")),this.previousTop=b})}}); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | React Blog 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | 34 | 35 |
    36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tonyspiro.com-react", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "dependencies": { 7 | "babel-core": "^5.8.25", 8 | "babel-loader": "^5.3.2", 9 | "cosmicjs": "^2.0.0", 10 | "events": "^1.1.0", 11 | "express": "^4.13.3", 12 | "flux": "^2.1.1", 13 | "history": "^1.12.5", 14 | "lodash": "^3.10.1", 15 | "nodemon": "^1.7.1", 16 | "react": "^0.14.0", 17 | "react-dom": "^0.14.0", 18 | "react-router": "^1.0.0-rc3", 19 | "webpack": "^1.12.2", 20 | "webpack-dev-server": "^1.12.0" 21 | }, 22 | "devDependencies": {}, 23 | "scripts": { 24 | "watch": "webpack --watch", 25 | "development": "webpack-dev-server", 26 | "production": "webpack -p", 27 | "start": "npm run production; node server.js", 28 | "test": "echo \"Error: no test specified\" && exit 1" 29 | }, 30 | "author": "", 31 | "license": "ISC" 32 | } 33 | -------------------------------------------------------------------------------- /pages/Blog.jsx: -------------------------------------------------------------------------------- 1 | // Home.jsx 2 | import React from 'react' 3 | 4 | // Utilities 5 | import AppStore from '../stores/AppStore'; 6 | import AppDispatcher from '../dispatcher/AppDispatcher'; 7 | 8 | // Components 9 | import Header from '../components/Header'; 10 | import BlogList from '../components/BlogList'; 11 | import BlogSingle from '../components/BlogSingle'; 12 | 13 | class Blog extends React.Component{ 14 | 15 | getPage(){ 16 | 17 | let pages = AppStore.data.pages; 18 | let pages_object = _.indexBy(pages, 'slug'); 19 | let page = pages_object['home']; 20 | 21 | // Get page info 22 | let metafields = page.metafields; 23 | let hero = _.findWhere(metafields, { key: 'hero' }); 24 | page.hero = 'https://cosmicjs.com/uploads/' + hero.value; 25 | 26 | let headline = _.findWhere(metafields, { key: 'headline' }); 27 | page.headline = headline.value; 28 | 29 | let subheadline = _.findWhere(metafields, { key: 'subheadline' }); 30 | page.subheadline = subheadline.value; 31 | 32 | if(!this.props.params.slug){ 33 | 34 | page.main_content = ; 35 | 36 | } else { 37 | 38 | page.main_content = ; 39 | 40 | } 41 | 42 | return page; 43 | } 44 | 45 | render(){ 46 | 47 | let globals = AppStore.data.globals; 48 | let pages = AppStore.data.pages; 49 | let page = this.getPage(); 50 | 51 | return ( 52 |
    53 |
    54 |
    55 |
    56 |
    57 | { page.main_content } 58 |
    59 |
    60 |
    61 |
    62 | ); 63 | } 64 | } 65 | 66 | export default Blog; -------------------------------------------------------------------------------- /pages/Default.jsx: -------------------------------------------------------------------------------- 1 | // Default.jsx 2 | import React from 'react' 3 | 4 | // Utilities 5 | import AppStore from '../stores/AppStore'; 6 | import AppDispatcher from '../dispatcher/AppDispatcher'; 7 | 8 | // Components 9 | import Header from '../components/Header'; 10 | import BlogList from '../components/BlogList'; 11 | 12 | class Home extends React.Component{ 13 | 14 | getPage(){ 15 | 16 | let pages = AppStore.data.pages; 17 | 18 | // Get current page slug 19 | let current_slug = this.props.route.path; 20 | let pages_object = _.indexBy(pages, 'slug'); 21 | let page = pages_object[current_slug]; 22 | 23 | // Get page info 24 | let metafields = page.metafields; 25 | let hero = _.findWhere(metafields, { key: 'hero' }); 26 | page.hero = 'https://cosmicjs.com/uploads/' + hero.value; 27 | 28 | let headline = _.findWhere(metafields, { key: 'headline' }); 29 | page.headline = headline.value; 30 | 31 | let subheadline = _.findWhere(metafields, { key: 'subheadline' }); 32 | page.subheadline = subheadline.value; 33 | page.main_content =
    ; 34 | 35 | return page; 36 | } 37 | 38 | render(){ 39 | 40 | let globals = AppStore.data.globals; 41 | let pages = AppStore.data.pages; 42 | let page = this.getPage(); 43 | 44 | return ( 45 |
    46 |
    47 |
    48 |
    49 |
    50 | { page.main_content } 51 |
    52 |
    53 |
    54 |
    55 | ); 56 | } 57 | } 58 | 59 | export default Home; -------------------------------------------------------------------------------- /pages/NoMatch.jsx: -------------------------------------------------------------------------------- 1 | // NoMatch.jsx 2 | import React from 'react'; 3 | import { Link } from 'react-router'; 4 | 5 | // Utilities 6 | import AppStore from '../stores/AppStore'; 7 | import AppDispatcher from '../dispatcher/AppDispatcher'; 8 | 9 | // Components 10 | import Header from '../components/Header'; 11 | import BlogList from '../components/BlogList'; 12 | 13 | class Home extends React.Component{ 14 | 15 | getPage(){ 16 | 17 | let pages = AppStore.data.pages; 18 | 19 | // Get home page info 20 | let pages_object = _.indexBy(pages, 'slug'); 21 | let page = pages_object['home']; 22 | 23 | // Get page info 24 | let metafields = page.metafields; 25 | let hero = _.findWhere(metafields, { key: 'hero' }); 26 | page.hero = 'https://cosmicjs.com/uploads/' + hero.value; 27 | 28 | let headline = _.findWhere(metafields, { key: 'headline' }); 29 | page.headline = headline.value; 30 | 31 | let subheadline = _.findWhere(metafields, { key: 'subheadline' }); 32 | page.subheadline = subheadline.value; 33 | 34 | return page; 35 | } 36 | 37 | render(){ 38 | 39 | let globals = AppStore.data.globals; 40 | let pages = AppStore.data.pages; 41 | let page = this.getPage(); 42 | 43 | return ( 44 |
    45 |
    46 |
    47 |
    48 |
    49 |
    50 | Whoa! Looks like you stumbled down a worm hole! 51 |
    52 |
    53 | Take me home 54 |
    55 |
    56 |
    57 |
    58 |
    59 | ); 60 | } 61 | } 62 | 63 | export default Home; -------------------------------------------------------------------------------- /pages/Work.jsx: -------------------------------------------------------------------------------- 1 | // Work.jsx 2 | import React from 'react' 3 | 4 | // Utilities 5 | import AppStore from '../stores/AppStore'; 6 | import AppDispatcher from '../dispatcher/AppDispatcher'; 7 | 8 | // Components 9 | import Header from '../components/Header'; 10 | import WorkList from '../components/WorkList'; 11 | import WorkSingle from '../components/WorkSingle'; 12 | 13 | class Work extends React.Component{ 14 | 15 | getPage(){ 16 | 17 | let pages = AppStore.data.pages; 18 | let pages_object = _.indexBy(pages, 'slug'); 19 | let page = pages_object['work']; 20 | 21 | // Get page info 22 | let metafields = page.metafields; 23 | let hero = _.findWhere(metafields, { key: 'hero' }); 24 | page.hero = 'https://cosmicjs.com/uploads/' + hero.value; 25 | 26 | let headline = _.findWhere(metafields, { key: 'headline' }); 27 | page.headline = headline.value; 28 | 29 | let subheadline = _.findWhere(metafields, { key: 'subheadline' }); 30 | page.subheadline = subheadline.value; 31 | 32 | if(!this.props.params.slug){ 33 | 34 | page.main_content = ; 35 | 36 | } else { 37 | 38 | page.main_content = ; 39 | 40 | } 41 | 42 | return page; 43 | } 44 | 45 | render(){ 46 | 47 | let globals = AppStore.data.globals; 48 | let pages = AppStore.data.pages; 49 | let page = this.getPage(); 50 | 51 | return ( 52 |
    53 |
    54 |
    55 |
    56 |
    57 | { page.main_content } 58 |
    59 |
    60 |
    61 |
    62 | ); 63 | } 64 | } 65 | 66 | export default Work; -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | const express = require('express') 3 | const path = require('path') 4 | const port = process.env.PORT || 8000 5 | const app = express() 6 | 7 | // serve static assets normally 8 | app.use(express.static(__dirname)) 9 | 10 | // handle every other route with index.html, which will contain 11 | // a script tag to your application's JavaScript file(s). 12 | app.get('*', function (request, response){ 13 | response.sendFile(path.resolve(__dirname, '', 'index.html')) 14 | }) 15 | 16 | app.listen(port) 17 | console.log("server started on port " + port) 18 | -------------------------------------------------------------------------------- /stores/AppStore.js: -------------------------------------------------------------------------------- 1 | // AppStore.js 2 | import {EventEmitter} from 'events'; 3 | import _ from 'lodash'; 4 | import config from '../config'; 5 | import Cosmic from 'cosmicjs'; 6 | 7 | let AppStore = _.extend({}, EventEmitter.prototype, { 8 | 9 | // Initial data 10 | data: { 11 | globals: {}, 12 | pages: [], 13 | item_num: 5 14 | }, 15 | 16 | init: function(payload) { 17 | 18 | let _this = this; 19 | let pages = {}; 20 | 21 | _this.data.ready = false; 22 | _this.data.pages = []; 23 | 24 | Cosmic.getObjects(config, function(err, response){ 25 | 26 | let objects = response.objects; 27 | 28 | /* Globals 29 | ======================== */ 30 | let globals = _this.data.globals; 31 | globals.text = response.object['text']; 32 | let metafields = globals.text.metafields; 33 | let menu_title = _.findWhere(metafields, { key: 'menu-title' }); 34 | globals.text.menu_title = menu_title.value; 35 | 36 | let footer_text = _.findWhere(metafields, { key: 'footer-text' }); 37 | globals.text.footer_text = footer_text.value; 38 | 39 | let site_title = _.findWhere(metafields, { key: 'site-title' }); 40 | globals.text.site_title = site_title.value; 41 | 42 | // Social 43 | globals.social = response.object['social']; 44 | metafields = globals.social.metafields; 45 | let twitter = _.findWhere(metafields, { key: 'twitter' }); 46 | globals.social.twitter = twitter.value; 47 | let facebook = _.findWhere(metafields, { key: 'facebook' }); 48 | globals.social.facebook = facebook.value; 49 | let github = _.findWhere(metafields, { key: 'github' }); 50 | globals.social.github = github.value; 51 | 52 | _this.data.globals = globals; 53 | 54 | /* Pages 55 | ======================== */ 56 | let pages = objects.type.page; 57 | _this.data.pages = pages; 58 | 59 | /* Articles 60 | ======================== */ 61 | let articles = objects.type['post']; 62 | articles = _.sortBy(articles, 'order'); 63 | _this.data.articles = articles; 64 | 65 | /* Work Items 66 | ======================== */ 67 | let work_items = objects.type['work']; 68 | work_items = _.sortBy(work_items, 'order'); 69 | _this.data.work_items = work_items; 70 | 71 | // Emit change 72 | _this.data.ready = true; 73 | _this.emitChange(); 74 | 75 | }); 76 | 77 | }, 78 | 79 | getMoreItems: function(){ 80 | 81 | this.data.loading = true; 82 | this.emitChange(); 83 | 84 | // For effect 85 | let _this = this; 86 | setTimeout(function(){ 87 | let item_num = _this.data.item_num; 88 | let more_item_num = item_num + 5; 89 | _this.data.item_num = more_item_num; 90 | _this.data.loading = false; 91 | _this.emitChange(); 92 | }, 300); 93 | }, 94 | 95 | // Emit Change event 96 | emitChange: function(){ 97 | this.emit('change'); 98 | }, 99 | 100 | // Add change listener 101 | addChangeListener: function(callback){ 102 | this.on('change', callback); 103 | }, 104 | 105 | // Remove change listener 106 | removeChangeListener: function(callback) { 107 | this.removeListener('change', callback); 108 | } 109 | 110 | }); 111 | 112 | export default AppStore; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './app.js', 3 | output: { path: __dirname, filename: 'dist/bundle.js' }, 4 | module: { 5 | loaders: [ 6 | { test: /.jsx?$/, loader: 'babel-loader', exclude: /node_modules/ } 7 | ] 8 | }, 9 | resolve : { 10 | extensions: ['', '.js', '.jsx'] 11 | } 12 | }; --------------------------------------------------------------------------------