├── .babelrc
├── .gitignore
├── CHECKS
├── README.md
├── actions
└── actions.js
├── app-client.js
├── app-server.js
├── bucket.json
├── components
├── App.js
├── Pages
│ ├── Blog.js
│ ├── Default.js
│ ├── NoMatch.js
│ └── Work.js
└── Partials
│ ├── BlogList.js
│ ├── BlogSingle.js
│ ├── Footer.js
│ ├── Header.js
│ ├── Loading.js
│ ├── Nav.js
│ ├── WorkList.js
│ └── WorkSingle.js
├── config.js
├── dispatcher
└── AppDispatcher.js
├── package-lock.json
├── package.json
├── public
├── 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
├── routes.js
├── stores
└── AppStore.js
├── views
└── index.html
├── webpack.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets":[
3 | "es2017", "react"
4 | ]
5 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | npm-debug.log
4 | public/dist
5 | public/index.html
--------------------------------------------------------------------------------
/CHECKS:
--------------------------------------------------------------------------------
1 | WAIT=30
2 | ATTEMPTS=10
3 | / DOCTYPE
4 |
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Universal Blog
2 | ##### [View a demo here](https://cosmicjs.com/apps/react-universal)
3 | [Sign up for Cosmic JS](https://cosmicjs.com/) to start managing content for your websites and applications faster and easier.
4 |
5 | ##### About
6 | The React Universal Blog is a portfolio blog app that renders html on the server to make all pages visible to search engines. Then after initial load from the server, it is converted to a single page application to allow for fast navigation between pages.
7 |
8 | ##### Notes
9 | I tried to keep the organization as simple as possible. There are no client, server or shared component folders. All components are shared and the only difference between client and server are the entry points (app-client.js, app-server.js and app.js).
10 |
11 | It uses the following:
12 |
13 | 1. [React](http://facebook.github.io/react/) for UI views
14 | 2. [Express](http://expressjs.com/) for server side rendering
15 | 3. [React Router](https://github.com/rackt/react-router) for routing
16 | 4. [React Hot Loader](https://github.com/gaearon/react-hot-loader) for hot loading in development
17 | 5. [Flux](https://facebook.github.io/flux/) for data flow
18 | 6. [Cosmic JS](https://cosmicjs.com) for content management
19 | ##### Install
20 | ```
21 | git clone https://github.com/tonyspiro/react-universal-blog
22 | cd react-universal-blog
23 | npm install
24 | ```
25 | ##### Run development
26 | ```
27 | npm run development
28 | ```
29 | Go to [http://localhost:8080](http://localhost:8080)
30 | ##### Run production
31 | ```
32 | npm start
33 | ```
34 | Go to [http://localhost:3000](http://localhost:3000)
35 | ##### Configure your Cosmic JS bucket
36 | After setting up your bucket on [Cosmic JS](https://cosmicjs.com), edit the ```config.js``` file and edit the slug to point to the slug of your bucket:
37 | ```javascript
38 | // config.js
39 | export default {
40 | bucket: {
41 | slug: 'react-universal-blog'
42 | }
43 | }
44 | ```
45 | Cosmic JS makes a great [React CMS](https://cosmicjs.com/knowledge-base/react-cms) for your React apps.
46 |
--------------------------------------------------------------------------------
/actions/actions.js:
--------------------------------------------------------------------------------
1 | // actions.js
2 | import config from '../config'
3 | import Cosmic from 'cosmicjs'
4 | import _ from 'lodash'
5 |
6 | // AppStore
7 | import AppStore from '../stores/AppStore'
8 |
9 | export function getStore(callback){
10 |
11 | let pages = {}
12 |
13 | Cosmic.getObjects(config, function(err, response){
14 |
15 | let objects = response.objects
16 |
17 | /* Globals
18 | ======================== */
19 | let globals = AppStore.data.globals
20 | globals.text = response.object['text']
21 | let metafields = globals.text.metafields
22 | let menu_title = _.find(metafields, { key: 'menu-title' })
23 | globals.text.menu_title = menu_title.value
24 |
25 | let footer_text = _.find(metafields, { key: 'footer-text' })
26 | globals.text.footer_text = footer_text.value
27 |
28 | let site_title = _.find(metafields, { key: 'site-title' })
29 | globals.text.site_title = site_title.value
30 |
31 | // Social
32 | globals.social = response.object['social']
33 | metafields = globals.social.metafields
34 | let twitter = _.find(metafields, { key: 'twitter' })
35 | globals.social.twitter = twitter.value
36 | let facebook = _.find(metafields, { key: 'facebook' })
37 | globals.social.facebook = facebook.value
38 | let github = _.find(metafields, { key: 'github' })
39 | globals.social.github = github.value
40 |
41 | // Nav
42 | const nav_items = response.object['nav'].metafields
43 | globals.nav_items = nav_items
44 |
45 | AppStore.data.globals = globals
46 |
47 | /* Pages
48 | ======================== */
49 | let pages = objects.type.page
50 | AppStore.data.pages = pages
51 |
52 | /* Articles
53 | ======================== */
54 | let articles = objects.type['post']
55 | articles = _.sortBy(articles, 'order')
56 | AppStore.data.articles = articles
57 |
58 | /* Work Items
59 | ======================== */
60 | let work_items = objects.type['work']
61 | work_items = _.sortBy(work_items, 'order')
62 | AppStore.data.work_items = work_items
63 |
64 | // Emit change
65 | AppStore.data.ready = true
66 | AppStore.emitChange()
67 |
68 | // Trigger callback (from server)
69 | if(callback){
70 | callback(false, AppStore)
71 | }
72 |
73 | })
74 | }
75 |
76 | export function getPageData(page_slug, post_slug){
77 |
78 | if(!page_slug || page_slug === 'blog')
79 | page_slug = 'home'
80 |
81 | // Get page info
82 | const data = AppStore.data
83 | const pages = data.pages
84 | const page = _.find(pages, { slug: page_slug })
85 | const metafields = page.metafields
86 | if(metafields){
87 | const hero = _.find(metafields, { key: 'hero' })
88 | page.hero = config.bucket.media_url + '/' + hero.value
89 |
90 | const headline = _.find(metafields, { key: 'headline' })
91 | page.headline = headline.value
92 |
93 | const subheadline = _.find(metafields, { key: 'subheadline' })
94 | page.subheadline = subheadline.value
95 | }
96 |
97 | if(post_slug){
98 | if(page_slug === 'home'){
99 | const articles = data.articles
100 | const article = _.find(articles, { slug: post_slug })
101 | page.title = article.title
102 | }
103 | if(page_slug === 'work'){
104 | const work_items = data.work_items
105 | const work_item = _.find(work_items, { slug: post_slug })
106 | page.title = work_item.title
107 | }
108 | }
109 | AppStore.data.page = page
110 | AppStore.emitChange()
111 | }
112 |
113 | export function getMoreItems(){
114 |
115 | AppStore.data.loading = true
116 | AppStore.emitChange()
117 |
118 | setTimeout(function(){
119 | let item_num = AppStore.data.item_num
120 | let more_item_num = item_num + 5
121 | AppStore.data.item_num = more_item_num
122 | AppStore.data.loading = false
123 | AppStore.emitChange()
124 | }, 300)
125 | }
--------------------------------------------------------------------------------
/app-client.js:
--------------------------------------------------------------------------------
1 | // app-client.js
2 | import React from 'react'
3 | import { render } from 'react-dom'
4 | import { Router } from 'react-router'
5 | import createBrowserHistory from 'history/lib/createBrowserHistory'
6 | const history = createBrowserHistory()
7 |
8 | // Routes
9 | import routes from './routes'
10 |
11 | const Routes = (
12 |
13 | { routes }
14 |
15 | )
16 |
17 | const app = document.getElementById('app')
18 | render(Routes, app)
19 |
--------------------------------------------------------------------------------
/app-server.js:
--------------------------------------------------------------------------------
1 | // app-server.js
2 | import React from 'react'
3 | import { match, RoutingContext, Route, IndexRoute } from 'react-router'
4 | import ReactDOMServer from 'react-dom/server'
5 | import express from 'express'
6 | import hogan from 'hogan-express'
7 | import config from './config'
8 |
9 | // Actions
10 | import { getStore, getPageData } from './actions/actions'
11 |
12 | // Routes
13 | import routes from './routes'
14 |
15 | // Express
16 | const app = express()
17 | app.engine('html', hogan)
18 | app.set('views', __dirname + '/views')
19 | app.use('/', express.static(__dirname + '/public/'))
20 | app.set('port', (process.env.PORT || 3000))
21 |
22 | app.get('*',(req, res) => {
23 |
24 | getStore(function(err, AppStore){
25 |
26 | if(err){
27 | return res.status(500).end('error')
28 | }
29 |
30 | match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
31 |
32 | // Get page data for template
33 | const slug_arr = req.url.split('/')
34 | let page_slug = slug_arr[1]
35 | let post_slug
36 | if(page_slug === 'blog' || page_slug === 'work')
37 | post_slug = slug_arr[2]
38 | getPageData(page_slug, post_slug)
39 | const page = AppStore.data.page
40 | res.locals.page = page
41 | res.locals.site = config.site
42 |
43 | // Get React markup
44 | const reactMarkup = ReactDOMServer.renderToStaticMarkup( )
45 | res.locals.reactMarkup = reactMarkup
46 |
47 | if (error) {
48 |
49 | res.status(500).send(error.message)
50 |
51 | } else if (redirectLocation) {
52 |
53 | res.redirect(302, redirectLocation.pathname + redirectLocation.search)
54 |
55 | } else if (renderProps) {
56 |
57 | // Success!
58 | res.status(200).render('index.html')
59 |
60 | } else {
61 |
62 | res.status(404).render('index.html')
63 |
64 | }
65 | })
66 |
67 | })
68 | })
69 |
70 | app.listen(app.get('port'))
71 |
72 | console.info('==> ✅ Server is listening in ' + process.env.NODE_ENV + ' mode')
73 | console.info('==> 🌎 Go to http://localhost:%s', app.get('port'))
74 |
--------------------------------------------------------------------------------
/bucket.json:
--------------------------------------------------------------------------------
1 | {
2 | "bucket": {
3 | "_id": "563c1dbe1ae7fe0f41000036",
4 | "slug": "react-universal-blog",
5 | "title": "React Universal Blog",
6 | "object_types": [
7 | {
8 | "title": "Pages",
9 | "slug": "page",
10 | "singular": "Page",
11 | "order": 2
12 | },
13 | {
14 | "title": "Posts",
15 | "slug": "post",
16 | "singular": "Post",
17 | "order": 3
18 | },
19 | {
20 | "title": "Work",
21 | "slug": "work",
22 | "singular": "work",
23 | "order": 4
24 | },
25 | {
26 | "title": "Globals",
27 | "slug": "globals",
28 | "singular": "globals",
29 | "help_text": null,
30 | "metafields": [],
31 | "options": {
32 | "slug_field": 0,
33 | "content_editor": 0,
34 | "add_metafields": 0,
35 | "metafields_title": 0,
36 | "metafields_key": 0
37 | },
38 | "order": 1
39 | }
40 | ],
41 | "links": [
42 | {
43 | "title": "GitHub Repo",
44 | "link": "https://github.com/tonyspiro/react-universal-blog"
45 | },
46 | {
47 | "title": "Preview",
48 | "link": "http://react-universal-blog.cosmicapp.co/"
49 | }
50 | ],
51 | "objects": [
52 | {
53 | "_id": "563c1def1ae7fe0f41000038",
54 | "bucket": "563c1dbe1ae7fe0f41000036",
55 | "slug": "text",
56 | "title": "Text",
57 | "content": "
",
58 | "metafields": [
59 | {
60 | "edit": 1,
61 | "value": "React Universal Blog",
62 | "key": "site-title",
63 | "title": "Site Title",
64 | "type": "text",
65 | "children": false
66 | },
67 | {
68 | "edit": 1,
69 | "value": "Cosmic JS",
70 | "key": "menu-title",
71 | "title": "Menu Title",
72 | "type": "text",
73 | "children": false
74 | },
75 | {
76 | "edit": 1,
77 | "value": "Copyright © Cosmic JS 2015",
78 | "key": "footer-text",
79 | "title": "Footer Text",
80 | "type": "text",
81 | "children": false
82 | }
83 | ],
84 | "type_slug": "globals",
85 | "created": "2015-11-06T03:26:39.533Z",
86 | "user_id": "55767c3ffbcf5cbb13000001",
87 | "options": {
88 | "slug_field": 0,
89 | "content_editor": 0,
90 | "add_metafields": 0,
91 | "metafields_title": 0,
92 | "metafields_key": 0
93 | },
94 | "order": null,
95 | "modified": "2015-11-06T03:41:04.284Z"
96 | },
97 | {
98 | "_id": "563c1def1ae7fe0f4100003a",
99 | "bucket": "563c1dbe1ae7fe0f41000036",
100 | "slug": "social",
101 | "title": "Social",
102 | "content": "
",
103 | "metafields": [
104 | {
105 | "edit": 1,
106 | "value": "https://twitter.com/cosmic_js",
107 | "key": "twitter",
108 | "title": "Twitter",
109 | "type": "text",
110 | "children": false
111 | },
112 | {
113 | "edit": 1,
114 | "value": "https://facebook.com/cosmicjs",
115 | "key": "facebook",
116 | "title": "Facebook",
117 | "type": "text",
118 | "children": false
119 | },
120 | {
121 | "edit": 1,
122 | "value": "https://github.com/tonyspiro/react-universal-blog",
123 | "key": "github",
124 | "title": "Github",
125 | "type": "text",
126 | "children": false
127 | }
128 | ],
129 | "type_slug": "globals",
130 | "created": "2015-11-06T03:26:39.535Z",
131 | "user_id": "55767c3ffbcf5cbb13000001",
132 | "options": {
133 | "slug_field": 0,
134 | "content_editor": 0,
135 | "add_metafields": 0,
136 | "metafields_title": 0,
137 | "metafields_key": 0
138 | },
139 | "order": null,
140 | "modified": "2015-11-09T03:08:48.088Z"
141 | },
142 | {
143 | "_id": "563c1fb51ae7fe0f41000086",
144 | "slug": "hello-universe",
145 | "title": "Hello Universe!",
146 | "content": "This is the first post on your React Universal Blog. You can add as many of these as you like! Isn't that exciting?! This cat thinks so.
",
147 | "metafields": [],
148 | "bucket": "563c1dbe1ae7fe0f41000036",
149 | "type_slug": "post",
150 | "created": "2015-11-06T03:34:13.437Z",
151 | "user_id": "55767c3ffbcf5cbb13000001",
152 | "options": {
153 | "slug_field": 0,
154 | "content_editor": 1,
155 | "add_metafields": 1,
156 | "metafields_title": 1,
157 | "metafields_key": 1
158 | },
159 | "modified": "2016-07-27T19:30:56.547Z",
160 | "status": "published"
161 | },
162 | {
163 | "_id": "563c20ad1ae7fe0f41000087",
164 | "slug": "work-item-title",
165 | "title": "Work Item Title",
166 | "content": "This is an interesting work item isn't it? Lumberg says, \"...yeah\"
",
167 | "metafields": [],
168 | "bucket": "563c1dbe1ae7fe0f41000036",
169 | "type_slug": "work",
170 | "created": "2015-11-06T03:38:21.580Z",
171 | "user_id": "55767c3ffbcf5cbb13000001",
172 | "options": {
173 | "slug_field": 0,
174 | "content_editor": 1,
175 | "add_metafields": 1,
176 | "metafields_title": 1,
177 | "metafields_key": 1
178 | },
179 | "modified": "2016-07-27T19:32:03.060Z",
180 | "status": "published"
181 | },
182 | {
183 | "_id": "567ad73780fb306b3a00000c",
184 | "slug": "nav",
185 | "title": "Nav",
186 | "content": "
",
187 | "metafields": [
188 | {
189 | "edit": 1,
190 | "value": "",
191 | "key": "home",
192 | "title": "Home",
193 | "type": "text",
194 | "children": false
195 | },
196 | {
197 | "edit": 1,
198 | "value": "work",
199 | "key": "work",
200 | "title": "Work",
201 | "type": "text",
202 | "children": false
203 | },
204 | {
205 | "edit": 1,
206 | "value": "about",
207 | "key": "about",
208 | "title": "About",
209 | "type": "text",
210 | "children": false
211 | },
212 | {
213 | "edit": 1,
214 | "value": "contact",
215 | "key": "contact",
216 | "title": "Contact",
217 | "type": "text",
218 | "children": false
219 | }
220 | ],
221 | "bucket": "563c1dbe1ae7fe0f41000036",
222 | "type_slug": "globals",
223 | "created": "2015-12-23T17:17:43.844Z",
224 | "user_id": "55767c3ffbcf5cbb13000001",
225 | "options": {
226 | "slug_field": 0,
227 | "content_editor": 0,
228 | "add_metafields": 0,
229 | "metafields_title": 0,
230 | "metafields_key": 0
231 | },
232 | "modified": "2015-12-23T17:36:19.442Z"
233 | },
234 | {
235 | "_id": "563c1def1ae7fe0f41000043",
236 | "bucket": "563c1dbe1ae7fe0f41000036",
237 | "slug": "home",
238 | "title": "Home",
239 | "content": "This is some home page content...
",
240 | "metafields": [
241 | {
242 | "edit": 1,
243 | "value": "45904530-8449-11e5-adfe-014902af5eff-space_11.jpg",
244 | "key": "hero",
245 | "title": "Hero",
246 | "type": "file",
247 | "children": false
248 | },
249 | {
250 | "edit": 1,
251 | "value": "React Universal",
252 | "key": "headline",
253 | "title": "Headline",
254 | "type": "text",
255 | "children": false
256 | },
257 | {
258 | "edit": 1,
259 | "value": "A Cosmic JS App Built with React",
260 | "key": "subheadline",
261 | "title": "Subheadline",
262 | "type": "text",
263 | "children": false
264 | }
265 | ],
266 | "type_slug": "page",
267 | "created": "2015-11-06T03:26:39.539Z",
268 | "user_id": "55767c3ffbcf5cbb13000001",
269 | "options": {
270 | "slug_field": 0,
271 | "content_editor": 1,
272 | "add_metafields": 1,
273 | "metafields_title": 1,
274 | "metafields_key": 1
275 | },
276 | "order": 1,
277 | "modified": "2015-12-29T03:57:55.322Z"
278 | },
279 | {
280 | "_id": "563c1def1ae7fe0f41000044",
281 | "bucket": "563c1dbe1ae7fe0f41000036",
282 | "slug": "work",
283 | "title": "Work",
284 | "content": "
",
285 | "metafields": [
286 | {
287 | "edit": 1,
288 | "value": "699270f0-77bc-11e5-bea6-3f1da2fe3a09-6a6b6eb0-6f5f-11e5-9c06-3fa77abead52-outer-space-1920x1200-hd-wallpaper (1).jpg",
289 | "key": "hero",
290 | "title": "Hero",
291 | "type": "file",
292 | "children": false
293 | },
294 | {
295 | "edit": 1,
296 | "value": "Work Stuff",
297 | "key": "headline",
298 | "title": "Headline",
299 | "type": "text",
300 | "children": false
301 | },
302 | {
303 | "edit": 1,
304 | "value": "All work and no play make for a dull demo",
305 | "key": "subheadline",
306 | "title": "Subheadline",
307 | "type": "text",
308 | "children": false
309 | }
310 | ],
311 | "type_slug": "page",
312 | "created": "2015-11-06T03:26:39.540Z",
313 | "user_id": "55767c3ffbcf5cbb13000001",
314 | "options": null,
315 | "order": 2,
316 | "modified": "2015-11-06T06:14:09.592Z"
317 | },
318 | {
319 | "_id": "563c1def1ae7fe0f41000047",
320 | "bucket": "563c1dbe1ae7fe0f41000036",
321 | "slug": "about",
322 | "title": "About",
323 | "content": "Hi, I'm a React Universal Blog powered by the Cosmic JS content platform. You can enjoy me on the server or the browser. If you are a Google robot, feel free to navigate through all of my pages and add them to your search index! If you are a human, enjoy browsing my pages super fast because I'm also a single page application!
",
324 | "metafields": [
325 | {
326 | "edit": 1,
327 | "value": "99e55210-8449-11e5-adfe-014902af5eff-steamworkshop_webupload_previewfile_327002106_preview.jpg",
328 | "key": "hero",
329 | "title": "Hero",
330 | "type": "file",
331 | "children": false
332 | },
333 | {
334 | "edit": 1,
335 | "value": "About Me",
336 | "key": "headline",
337 | "title": "Headline",
338 | "type": "text",
339 | "children": false
340 | },
341 | {
342 | "edit": 1,
343 | "value": "I'll tell you some stuff then I'll ask about you",
344 | "key": "subheadline",
345 | "title": "Subheadline",
346 | "type": "text",
347 | "children": false
348 | }
349 | ],
350 | "type_slug": "page",
351 | "created": "2015-11-06T03:26:39.542Z",
352 | "user_id": "55767c3ffbcf5cbb13000001",
353 | "options": null,
354 | "order": 3,
355 | "modified": "2015-11-06T06:19:34.580Z"
356 | },
357 | {
358 | "_id": "563c1def1ae7fe0f41000049",
359 | "bucket": "563c1dbe1ae7fe0f41000036",
360 | "slug": "contact",
361 | "title": "Contact",
362 | "content": "",
363 | "metafields": [
364 | {
365 | "edit": 1,
366 | "value": "0e2445e0-844b-11e5-adfe-014902af5eff-deep_space_nebula_by_hameed.jpg",
367 | "key": "hero",
368 | "title": "Hero",
369 | "type": "file",
370 | "children": false
371 | },
372 | {
373 | "edit": 1,
374 | "value": "Contact Me",
375 | "key": "headline",
376 | "title": "Headline",
377 | "type": "text",
378 | "children": false
379 | },
380 | {
381 | "edit": 1,
382 | "value": "If you need anything, sugar, advice, I try to be a good neighbor",
383 | "key": "subheadline",
384 | "title": "Subheadline",
385 | "type": "text",
386 | "children": false
387 | }
388 | ],
389 | "type_slug": "page",
390 | "created": "2015-11-06T03:26:39.543Z",
391 | "user_id": "55767c3ffbcf5cbb13000001",
392 | "options": null,
393 | "order": 4,
394 | "modified": "2015-11-06T06:08:00.568Z"
395 | }
396 | ],
397 | "media": [
398 | {
399 | "_id": "563c3d8e1ae7fe0f4100008a",
400 | "name": "0a253dc0-8449-11e5-adfe-014902af5eff-steamworkshop_webupload_previewfile_327002106_preview.jpg",
401 | "original_name": "steamworkshop_webupload_previewfile_327002106_preview.jpg",
402 | "size": 145499,
403 | "type": "image/jpeg",
404 | "bucket": "563c1dbe1ae7fe0f41000036",
405 | "created": "2015-11-06T05:41:34.264Z",
406 | "folder": null,
407 | "location": "https://cosmicjs.com/uploads"
408 | },
409 | {
410 | "_id": "563c3df11ae7fe0f4100008b",
411 | "name": "45904530-8449-11e5-adfe-014902af5eff-space_11.jpg",
412 | "original_name": "space_11.jpg",
413 | "size": 186217,
414 | "type": "image/jpeg",
415 | "bucket": "563c1dbe1ae7fe0f41000036",
416 | "created": "2015-11-06T05:43:13.933Z",
417 | "folder": null,
418 | "location": "https://cosmicjs.com/uploads"
419 | },
420 | {
421 | "_id": "563c3e7f1ae7fe0f4100008d",
422 | "name": "99e55210-8449-11e5-adfe-014902af5eff-steamworkshop_webupload_previewfile_327002106_preview.jpg",
423 | "original_name": "steamworkshop_webupload_previewfile_327002106_preview.jpg",
424 | "size": 145499,
425 | "type": "image/jpeg",
426 | "bucket": "563c1dbe1ae7fe0f41000036",
427 | "created": "2015-11-06T05:45:35.418Z",
428 | "folder": null,
429 | "location": "https://cosmicjs.com/uploads"
430 | },
431 | {
432 | "_id": "563c40ef1ae7fe0f4100008e",
433 | "name": "0e2445e0-844b-11e5-adfe-014902af5eff-deep_space_nebula_by_hameed.jpg",
434 | "original_name": "deep_space_nebula_by_hameed.jpg",
435 | "size": 276879,
436 | "type": "image/jpeg",
437 | "bucket": "563c1dbe1ae7fe0f41000036",
438 | "created": "2015-11-06T05:55:59.945Z",
439 | "folder": null,
440 | "location": "https://cosmicjs.com/uploads"
441 | },
442 | {
443 | "_id": "57990bc3ed7a775367002aee",
444 | "name": "88c45370-5430-11e6-a6a3-41ff49de9ecd-download.jpeg",
445 | "original_name": "download.jpeg",
446 | "size": 124142,
447 | "type": "image/jpeg",
448 | "bucket": "563c1dbe1ae7fe0f41000036",
449 | "created": "2016-07-27T19:30:11.116Z",
450 | "folder": null,
451 | "location": "https://cosmicjs.com/uploads"
452 | },
453 | {
454 | "_id": "57990c15ed7a775367002b02",
455 | "name": "b9d4e740-5430-11e6-a6a3-41ff49de9ecd-download (1).jpeg",
456 | "original_name": "download (1).jpeg",
457 | "size": 561637,
458 | "type": "image/jpeg",
459 | "bucket": "563c1dbe1ae7fe0f41000036",
460 | "created": "2016-07-27T19:31:33.444Z",
461 | "folder": null,
462 | "location": "https://cosmicjs.com/uploads"
463 | }
464 | ],
465 | "media_folders": null,
466 | "plugins": null
467 | }
468 | }
469 |
--------------------------------------------------------------------------------
/components/App.js:
--------------------------------------------------------------------------------
1 | // App.js
2 | import React, { Component } from 'react'
3 |
4 | // Dispatcher
5 | import AppDispatcher from '../dispatcher/AppDispatcher'
6 |
7 | // Store
8 | import AppStore from '../stores/AppStore'
9 |
10 | // Components
11 | import Nav from './Partials/Nav'
12 | import Footer from './Partials/Footer'
13 | import Loading from './Partials/Loading'
14 |
15 | export default class App extends Component {
16 |
17 | // Add change listeners to stores
18 | componentDidMount(){
19 | AppStore.addChangeListener(this._onChange.bind(this))
20 | }
21 |
22 | // Remove change listeners from stores
23 | componentWillUnmount(){
24 | AppStore.removeChangeListener(this._onChange.bind(this))
25 | }
26 |
27 | getStore(){
28 | AppDispatcher.dispatch({
29 | action: 'get-app-store'
30 | })
31 | }
32 |
33 | _onChange(){
34 | this.setState(AppStore)
35 | }
36 |
37 | render(){
38 |
39 | const data = AppStore.data
40 |
41 | // Show loading for browser
42 | if(!data.ready){
43 |
44 | document.body.className = ''
45 | this.getStore()
46 |
47 | let style = {
48 | marginTop: 120
49 | }
50 | return (
51 |
52 |
53 |
54 | )
55 | }
56 |
57 | // Server first
58 | const Routes = React.cloneElement(this.props.children, { data: data })
59 |
60 | return (
61 |
62 |
63 | { Routes }
64 |
65 |
66 | )
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/components/Pages/Blog.js:
--------------------------------------------------------------------------------
1 | // Blog.js
2 | import React, { Component } from 'react'
3 | import _ from 'lodash'
4 | import config from '../../config'
5 |
6 | // Components
7 | import Header from '../Partials/Header'
8 | import BlogList from '../Partials/BlogList'
9 | import BlogSingle from '../Partials/BlogSingle'
10 |
11 | // Dispatcher
12 | import AppDispatcher from '../../dispatcher/AppDispatcher'
13 |
14 | export default class Blog extends Component {
15 |
16 | componentWillMount(){
17 | this.getPageData()
18 | }
19 |
20 | componentDidMount(){
21 | const data = this.props.data
22 | document.title = config.site.title + ' | ' + data.page.title
23 | }
24 |
25 | getPageData(){
26 | AppDispatcher.dispatch({
27 | action: 'get-page-data',
28 | page_slug: 'blog',
29 | post_slug: this.props.params.slug
30 | })
31 | }
32 |
33 | getMoreArticles(){
34 | AppDispatcher.dispatch({
35 | action: 'get-more-items'
36 | })
37 | }
38 |
39 | render(){
40 |
41 | const data = this.props.data
42 | const globals = data.globals
43 | const pages = data.pages
44 | let main_content
45 |
46 | if(!this.props.params.slug){
47 |
48 | main_content =
49 |
50 | } else {
51 |
52 | const articles = data.articles
53 |
54 | // Get current page slug
55 | const slug = this.props.params.slug
56 | const articles_object = _.keyBy(articles, 'slug')
57 | const article = articles_object[slug]
58 | main_content =
59 |
60 | }
61 |
62 | return (
63 |
64 |
65 |
66 |
67 |
68 | { main_content }
69 |
70 |
71 |
72 |
73 | )
74 | }
75 | }
--------------------------------------------------------------------------------
/components/Pages/Default.js:
--------------------------------------------------------------------------------
1 | // Default.js
2 | import React, { Component } from 'react'
3 | import { Link } from 'react-router'
4 | import config from '../../config'
5 |
6 | // Components
7 | import Header from '../Partials/Header'
8 |
9 | // Dispatcher
10 | import AppDispatcher from '../../dispatcher/AppDispatcher'
11 |
12 | export default class Default extends Component {
13 |
14 | componentWillMount(){
15 | this.getPageData()
16 | }
17 |
18 | componentDidUpdate(){
19 | const data = this.props.data
20 | document.title = config.site.title + ' | ' + data.page.title
21 |
22 | // Updated
23 | const page = data.page
24 | const page_slug = this.getSlug()
25 | if(page.slug !== page_slug)
26 | this.getPageData(page_slug)
27 | }
28 |
29 | getSlug(){
30 | return this.props.location.pathname.replace('/','')
31 | }
32 |
33 | getPageData(){
34 | const page_slug = this.getSlug()
35 | AppDispatcher.dispatch({
36 | action: 'get-page-data',
37 | page_slug: page_slug
38 | })
39 | }
40 |
41 | render(){
42 |
43 | const slug = this.getSlug()
44 | const data = this.props.data
45 | const page = data.page
46 |
47 | let main_content =
48 |
49 | return (
50 |
51 |
52 |
53 |
54 |
55 | { main_content }
56 |
57 |
58 |
59 |
60 | )
61 | }
62 | }
--------------------------------------------------------------------------------
/components/Pages/NoMatch.js:
--------------------------------------------------------------------------------
1 | // NoMatch.js
2 | import React, { Component } from 'react'
3 | import { Link } from 'react-router'
4 | import config from '../../config'
5 |
6 | // Components
7 | import Header from '../Partials/Header'
8 |
9 | // Dispatcher
10 | import AppDispatcher from '../../dispatcher/AppDispatcher'
11 |
12 | export default class NoMatch extends Component {
13 |
14 | componentWillMount(){
15 | this.getPageData()
16 | }
17 |
18 | componentDidMount(){
19 | const data = this.props.data
20 | document.title = config.site.title + ' | Page Not Found'
21 | }
22 |
23 | getPageData(){
24 | AppDispatcher.dispatch({
25 | action: 'get-page-data',
26 | slug: 'home'
27 | })
28 | }
29 |
30 | render(){
31 |
32 | const data = this.props.data
33 | const page = data.page
34 |
35 | return (
36 |
37 |
38 |
39 |
40 |
41 |
42 | Whoa! Looks like you stumbled down a worm hole!
43 | If this is a new page that you've added in Cosmic JS, make sure you add it to your routes.js
file!
44 |
45 |
46 | Take me home
47 |
48 |
49 |
50 |
51 |
52 | )
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/components/Pages/Work.js:
--------------------------------------------------------------------------------
1 | // Work.js
2 | import React, { Component } from 'react'
3 | import _ from 'lodash'
4 | import config from '../../config'
5 |
6 | // Components
7 | import Header from '../Partials/Header'
8 | import WorkList from '../Partials/WorkList'
9 | import WorkSingle from '../Partials/WorkSingle'
10 |
11 | // Dispatcher
12 | import AppDispatcher from '../../dispatcher/AppDispatcher'
13 |
14 | export default class Work extends Component {
15 |
16 | componentWillMount(){
17 | this.getPageData()
18 | }
19 |
20 | componentDidMount(){
21 | const data = this.props.data
22 | document.title = config.site.title + ' | ' + data.page.title
23 | }
24 |
25 | getPageData(){
26 | AppDispatcher.dispatch({
27 | action: 'get-page-data',
28 | page_slug: 'work',
29 | post_slug: this.props.params.slug
30 | })
31 | }
32 |
33 | getMoreWorkItems(){
34 | AppDispatcher.dispatch({
35 | action: 'get-more-items'
36 | })
37 | }
38 |
39 | render(){
40 |
41 | const data = this.props.data
42 | const globals = data.globals
43 | const pages = data.pages
44 | let main_content
45 |
46 | if(!this.props.params.slug){
47 |
48 | main_content =
49 |
50 | } else {
51 |
52 | const work_items = data.work_items
53 |
54 | // Get current page slug
55 | const slug = this.props.params.slug
56 | const work_items_object = _.indexBy(work_items, 'slug')
57 | const work_item = work_items_object[slug]
58 |
59 | main_content =
60 |
61 | }
62 |
63 | return (
64 |
65 |
66 |
67 |
68 |
69 | { main_content }
70 |
71 |
72 |
73 |
74 | )
75 | }
76 | }
--------------------------------------------------------------------------------
/components/Partials/BlogList.js:
--------------------------------------------------------------------------------
1 | // BlogList.js
2 | import React, { Component } from 'react'
3 | import _ from 'lodash'
4 | import { Link } from 'react-router'
5 |
6 | export default class BlogList extends Component {
7 |
8 | scrollTop(){
9 | $('html, body').animate({
10 | scrollTop: $("#main-content").offset().top
11 | }, 500)
12 | }
13 |
14 | render(){
15 |
16 | let data = this.props.data
17 | let item_num = data.item_num
18 | let articles = data.articles
19 |
20 | let load_more
21 | let show_more_text = 'Show More Articles'
22 |
23 | if(data.loading){
24 | show_more_text = 'Loading...'
25 | }
26 |
27 | if(articles && item_num <= articles.length){
28 | load_more = (
29 |
30 |
31 | { show_more_text }
32 |
33 |
34 | )
35 | }
36 |
37 | articles = _.take(articles, item_num)
38 |
39 | let articles_html = articles.map(( article ) => {
40 | let date_obj = new Date(article.created)
41 | let created = (date_obj.getMonth()+1) + '/' + date_obj.getDate() + '/' + date_obj.getFullYear()
42 | return (
43 |
44 |
45 |
46 | { article.title }
47 |
48 |
Posted by Cosmic JS on { created }
49 |
50 |
51 |
52 | )
53 | })
54 |
55 | return (
56 |
57 |
{ articles_html }
58 | { load_more }
59 |
60 | )
61 | }
62 | }
--------------------------------------------------------------------------------
/components/Partials/BlogSingle.js:
--------------------------------------------------------------------------------
1 | // BlogSingle.js
2 | import React, { Component } from 'react'
3 | import { Link } from 'react-router'
4 |
5 | export default class BlogSingle extends Component {
6 |
7 | render(){
8 |
9 | const article = this.props.article
10 |
11 | const style = {
12 | marginBottom: 20
13 | }
14 |
15 | return (
16 |
17 |
<< Back to Article List
18 |
{ article.title }
19 |
20 |
21 | )
22 | }
23 | }
--------------------------------------------------------------------------------
/components/Partials/Footer.js:
--------------------------------------------------------------------------------
1 | // Footer.js
2 | import React, { Component } from 'react'
3 |
4 | export default class Footer extends Component {
5 |
6 | render(){
7 |
8 | const data = this.props.data
9 | let footer_text
10 | if(data.globals.text){
11 | footer_text = data.globals.text.footer_text
12 | }
13 |
14 | let twitter
15 | let facebook
16 | let github
17 | if(data.globals.social){
18 | twitter = data.globals.social.twitter
19 | facebook = data.globals.social.facebook
20 | github = data.globals.social.github
21 | }
22 |
23 | return (
24 |
59 | )
60 | }
61 | }
--------------------------------------------------------------------------------
/components/Partials/Header.js:
--------------------------------------------------------------------------------
1 | // Header.js
2 | import React, { Component } from 'react'
3 |
4 | export default class Header extends Component {
5 |
6 | render(){
7 |
8 | const data = this.props.data
9 | const hero = data.page.hero
10 | const headline = data.page.headline
11 | const subheadline = data.page.subheadline
12 |
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
{ headline }
21 |
22 | { subheadline }
23 |
24 |
25 |
26 |
27 |
28 |
29 | )
30 | }
31 | }
--------------------------------------------------------------------------------
/components/Partials/Loading.js:
--------------------------------------------------------------------------------
1 | // Loading.js
2 | import React, { Component } from 'react'
3 |
4 | export default class Loading extends Component {
5 | render(){
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | )
22 | }
23 | }
--------------------------------------------------------------------------------
/components/Partials/Nav.js:
--------------------------------------------------------------------------------
1 | // Nav.js
2 | import React, { Component } from 'react'
3 | import { Link } from 'react-router'
4 |
5 | export default class Nav extends Component {
6 |
7 | handleClick(){
8 | $('.navbar-collapse').removeClass('in')
9 | $('html,body').scrollTop(0)
10 | }
11 |
12 | render(){
13 |
14 | const data = this.props.data
15 | const nav_items = data.globals.nav_items
16 |
17 | // Prevent initial null
18 | if(!nav_items){
19 | return
20 | }
21 |
22 | const menu_items = nav_items.map(( nav_item ) => {
23 | return (
24 |
25 | { nav_item.title }
26 |
27 | )
28 | })
29 |
30 | return (
31 |
32 |
33 |
34 |
35 | Toggle navigation
36 |
37 |
38 |
39 |
40 |
Cosmic JS
41 |
42 |
43 |
48 |
49 |
50 | )
51 | }
52 | }
--------------------------------------------------------------------------------
/components/Partials/WorkList.js:
--------------------------------------------------------------------------------
1 | // WorkList.js
2 | import React, { Component } from 'react'
3 | import _ from 'lodash'
4 | import { Link } from 'react-router'
5 |
6 | // Dispatcher
7 | import AppDispatcher from '../../dispatcher/AppDispatcher'
8 |
9 | export default class WorkList extends Component {
10 |
11 | scrollTop(){
12 | $('html, body').animate({
13 | scrollTop: $("#main-content").offset().top
14 | }, 500)
15 | }
16 |
17 | render(){
18 |
19 | let data = this.props.data
20 | let item_num = data.item_num
21 | let work_items = data.work_items
22 |
23 | let load_more
24 | let show_more_text = 'Show More Work'
25 |
26 | if(data.loading){
27 | show_more_text = 'Loading...'
28 | }
29 |
30 | if(work_items && item_num <= work_items.length){
31 | load_more = (
32 |
33 |
34 | { show_more_text }
35 |
36 |
37 | )
38 | }
39 |
40 | work_items = _.take(work_items, item_num)
41 |
42 | let articles_html = work_items.map(( work_item ) => {
43 | let date_obj = new Date(work_item.created)
44 | let created = (date_obj.getMonth()+1) + '/' + date_obj.getDate() + '/' + date_obj.getFullYear()
45 | return (
46 |
47 |
48 |
49 | { work_item.title }
50 |
51 |
Posted by Cosmic JS on { created }
52 |
53 |
54 |
55 | )
56 | })
57 |
58 | return (
59 |
60 |
{ articles_html }
61 | { load_more }
62 |
63 | )
64 | }
65 | }
--------------------------------------------------------------------------------
/components/Partials/WorkSingle.js:
--------------------------------------------------------------------------------
1 | // WorkSingle.js
2 | import React, { Component } from 'react'
3 | import _ from 'lodash'
4 | import { Link } from 'react-router'
5 |
6 | export default class WorkSingle extends Component {
7 |
8 | render(){
9 |
10 | const work_item = this.props.work_item
11 |
12 | const style = {
13 | marginBottom: 20
14 | }
15 |
16 | return (
17 |
18 |
<< Back to Work List
19 |
{ work_item.title }
20 |
21 |
22 | )
23 | }
24 | }
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | // config.js
2 | export default {
3 | site: {
4 | title: 'React Universal Blog'
5 | },
6 | bucket: {
7 | slug: process.env.COSMIC_BUCKET || 'react-universal-blog',
8 | media_url: 'https://cosmicjs.com/uploads',
9 | read_key: process.env.COSMIC_READ_KEY || '',
10 | write_key: process.env.COSMIC_WRITE_KEY || ''
11 | },
12 | }
13 |
--------------------------------------------------------------------------------
/dispatcher/AppDispatcher.js:
--------------------------------------------------------------------------------
1 | // AppDispatcher.js
2 | import { Dispatcher } from 'flux'
3 | import { getStore, getPageData, getMoreItems } from '../actions/actions'
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 'get-app-store':
15 | getStore()
16 | break
17 |
18 | case 'get-page-data':
19 | getPageData(payload.page_slug, payload.post_slug)
20 | break
21 |
22 | case 'get-more-items':
23 | getMoreItems()
24 | break
25 |
26 | default:
27 | return true
28 |
29 | }
30 |
31 | return true
32 |
33 | })
34 |
35 | export default AppDispatcher
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-universal-blog",
3 | "version": "1.0.0",
4 | "engines": {
5 | "node": "4.1.2",
6 | "npm": "3.5.2"
7 | },
8 | "description": "",
9 | "main": "app-server.js",
10 | "dependencies": {
11 | "babel-cli": "^6.26.0",
12 | "babel-loader": "^7.1.2",
13 | "babel-preset-es2015": "^6.24.1",
14 | "babel-preset-es2017": "^6.24.1",
15 | "babel-preset-react": "^6.24.1",
16 | "babel-register": "^6.26.0",
17 | "cosmicjs": "^2.4.0",
18 | "flux": "^3.1.3",
19 | "history": "1.13.0",
20 | "hogan-express": "^0.5.2",
21 | "html-webpack-plugin": "^2.30.1",
22 | "path": "^0.12.7",
23 | "react": "^15.6.1",
24 | "react-dom": "^15.6.1",
25 | "react-router": "1.0.1",
26 | "webpack": "^3.5.6",
27 | "webpack-dev-server": "^2.7.1"
28 | },
29 | "scripts": {
30 | "start": "npm run production",
31 | "production": "rm -rf public/index.html && NODE_ENV=production webpack -p && NODE_ENV=production babel-node app-server.js --presets es2015",
32 | "webpack-dev-server": "NODE_ENV=development PORT=8080 webpack-dev-server --content-base public/ --hot --inline --devtool inline-source-map --history-api-fallback",
33 | "development": "cp views/index.html public/index.html && NODE_ENV=development webpack && npm run webpack-dev-server"
34 | },
35 | "author": "",
36 | "license": "ISC",
37 | "devDependencies": {
38 | "react-hot-loader": "^1.3.0"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/public/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 |
--------------------------------------------------------------------------------
/public/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 |
--------------------------------------------------------------------------------
/public/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 | }
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmicjs/cosmicapp-react-universal-blog/cd2e97e3e90d550f7a63b853ee36024bd15e18ce/public/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmicjs/cosmicapp-react-universal-blog/cd2e97e3e90d550f7a63b853ee36024bd15e18ce/public/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmicjs/cosmicapp-react-universal-blog/cd2e97e3e90d550f7a63b853ee36024bd15e18ce/public/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmicjs/cosmicapp-react-universal-blog/cd2e97e3e90d550f7a63b853ee36024bd15e18ce/public/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/public/img/about-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmicjs/cosmicapp-react-universal-blog/cd2e97e3e90d550f7a63b853ee36024bd15e18ce/public/img/about-bg.jpg
--------------------------------------------------------------------------------
/public/img/contact-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmicjs/cosmicapp-react-universal-blog/cd2e97e3e90d550f7a63b853ee36024bd15e18ce/public/img/contact-bg.jpg
--------------------------------------------------------------------------------
/public/img/home-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmicjs/cosmicapp-react-universal-blog/cd2e97e3e90d550f7a63b853ee36024bd15e18ce/public/img/home-bg.jpg
--------------------------------------------------------------------------------
/public/img/post-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmicjs/cosmicapp-react-universal-blog/cd2e97e3e90d550f7a63b853ee36024bd15e18ce/public/img/post-bg.jpg
--------------------------------------------------------------------------------
/public/img/post-sample-image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosmicjs/cosmicapp-react-universal-blog/cd2e97e3e90d550f7a63b853ee36024bd15e18ce/public/img/post-sample-image.jpg
--------------------------------------------------------------------------------
/public/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('
').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27|32)/.test(b.which)&&!/input|textarea/i.test(b.target.tagName)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g&&27!=b.which||g&&27==b.which)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(b.target);38==b.which&&j>0&&j--,40==b.which&&j ').appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.handleUpdate=function(){this.adjustDialog()},c.prototype.adjustDialog=function(){var a=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',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);
--------------------------------------------------------------------------------
/public/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("×")
42 | .append(" ");
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("×")
55 | .append(" ");
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 |
--------------------------------------------------------------------------------
/public/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("×").append(" "),$("#success > .alert-success").append("Your message has been sent. "),$("#success > .alert-success").append("
"),$("#contactForm").trigger("reset")},error:function(){$("#success").html(""),$("#success > .alert-danger").html("×").append(" "),$("#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"):""):'"+(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})}});
--------------------------------------------------------------------------------
/routes.js:
--------------------------------------------------------------------------------
1 | // routes.js
2 | import React from 'react'
3 | import { Route, IndexRoute } from 'react-router'
4 |
5 | // Store
6 | import AppStore from './stores/AppStore'
7 |
8 | // Main component
9 | import App from './components/App'
10 |
11 | // Pages
12 | import Blog from './components/Pages/Blog'
13 | import Default from './components/Pages/Default'
14 | import Work from './components/Pages/Work'
15 | import NoMatch from './components/Pages/NoMatch'
16 |
17 | export default (
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 |
--------------------------------------------------------------------------------
/stores/AppStore.js:
--------------------------------------------------------------------------------
1 | // AppStore.js
2 | import { EventEmitter } from 'events'
3 | import _ from 'lodash'
4 |
5 | export default _.extend({}, EventEmitter.prototype, {
6 |
7 | // Initial data
8 | data: {
9 | ready: false,
10 | globals: {},
11 | pages: [],
12 | item_num: 5
13 | },
14 |
15 | // Emit Change event
16 | emitChange: function(){
17 | this.emit('change')
18 | },
19 |
20 | // Add change listener
21 | addChangeListener: function(callback){
22 | this.on('change', callback)
23 | },
24 |
25 | // Remove change listener
26 | removeChangeListener: function(callback) {
27 | this.removeListener('change', callback)
28 | }
29 |
30 | })
--------------------------------------------------------------------------------
/views/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{ site.title }}{{# page }} | {{ page.title }}{{/ page }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 | {{{ reactMarkup }}}
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | // webpack.config.js
2 | var webpack = require('webpack')
3 |
4 | module.exports = {
5 | devtool: 'eval',
6 | entry: './app-client.js',
7 | output: {
8 | path: __dirname + '/public/dist',
9 | filename: 'bundle.js',
10 | publicPath: '/dist/'
11 | },
12 | module: {
13 | loaders: [
14 | { test: /\.js$/, loaders: 'babel-loader', exclude: /node_modules/ },
15 | { test: /\.jsx$/, loaders: 'babel-loader', exclude: /node_modules/ }
16 | ]
17 | },
18 | plugins: [
19 | new webpack.DefinePlugin({
20 | 'process.env.COSMIC_BUCKET': JSON.stringify(process.env.COSMIC_BUCKET),
21 | 'process.env.COSMIC_READ_KEY': JSON.stringify(process.env.COSMIC_READ_KEY),
22 | 'process.env.COSMIC_WRITE_KEY': JSON.stringify(process.env.COSMIC_WRITE_KEY)
23 | })
24 | ]
25 | };
26 |
--------------------------------------------------------------------------------