diff --git a/package.json b/package.json
index c2ac254b..95c268bc 100644
--- a/package.json
+++ b/package.json
@@ -21,14 +21,16 @@
"angular": "^1.5.3",
"angular-route": "^1.5.3",
"angular-spinner": "^0.8.1",
- "angulartics": "^1.2.1",
- "angulartics-google-analytics": "^0.1.4",
+ "angulartics": "^1.0.0",
+ "angulartics-google-analytics": "^0.3.0",
+ "async-file": "^2.0.2",
"babel-core": "^6.7.4",
"babel-polyfill": "^6.7.4",
"babel-preset-es2015": "^6.5.0",
"babel-preset-stage-0": "^6.5.0",
"co": "^4.4.0",
"ejson": "^2.1.2",
+ "handlebars": "^4.0.5",
"hudozka-api-v1": "file:modules/hudozka-api-v1",
"hudozka-data": "file:modules/hudozka-data",
"hudozka-middlewares": "file:modules/hudozka-middlewares",
diff --git a/src/client/api/API.js b/src/client/api/API.js
index d6f79aae..0f8fdee8 100644
--- a/src/client/api/API.js
+++ b/src/client/api/API.js
@@ -5,6 +5,7 @@ import APITeacher from './APITeacher';
import APISchedule from './APISchedule';
import APIEvent from './APIEvent';
import APIArticle from './APIArticle';
+import APIPage from './APIPage';
export default class API {
constructor(request) {
@@ -15,5 +16,6 @@ export default class API {
this.schedule = new APISchedule(request);
this.event = new APIEvent(request);
this.article = new APIArticle(request);
+ this.page = new APIPage(request);
}
}
diff --git a/src/client/api/APIPage.js b/src/client/api/APIPage.js
new file mode 100644
index 00000000..200e54aa
--- /dev/null
+++ b/src/client/api/APIPage.js
@@ -0,0 +1,7 @@
+import EndPoint from './EndPoint';
+
+export default class APIPage extends EndPoint {
+ url(url) {
+ return this.get(url);
+ }
+}
diff --git a/src/client/app.js b/src/client/app.js
index 61a291c9..6412f4c8 100644
--- a/src/client/app.js
+++ b/src/client/app.js
@@ -1,3 +1,4 @@
+import {getInitialData} from './core/init';
import Navigation from './components/Navigation';
import MainMenu from './components/MainMenu';
import Breadcrumbs from './components/Breadcrumbs';
@@ -20,6 +21,7 @@ import ArticlePageControl from './components/ArticlePageControl';
import Album from './components/Album';
import Parallax from './components/Parallax';
+import Page from './components/CloudPage';
import PageSchool from './components/PageSchool';
import PageEvents from './components/PageEvents';
import PageArticle from './components/PageArticle';
@@ -61,6 +63,21 @@ app.config(($locationProvider, $routeProvider) => {
$locationProvider.hashPrefix('!');
$locationProvider.html5Mode(true);
+ const pages = getInitialData().pages
+ .map(i => i.url)
+ .map(i => ({
+ name: i,
+ template: '',
+ resolveAs: '$',
+ resolve: {
+ data: (api) => {
+ return api.page
+ .url(location.pathname)
+ .then(i => i.data)
+ }
+ }
+ }));
+
let routers = [
{
name: '/',
@@ -191,6 +208,7 @@ app.config(($locationProvider, $routeProvider) => {
}
}
]
+ .concat(pages)
.map(i => {
let path = i.name;
delete i.name;
@@ -264,6 +282,7 @@ app.run(($location, $rootScope, $http) => {
GalleryItem,
AlbumCollection,
TeacherProfile,
+ Page,
PageSchool,
PageArticle,
PageEvents,
diff --git a/src/client/components/CloudPage.js b/src/client/components/CloudPage.js
new file mode 100644
index 00000000..1b434b80
--- /dev/null
+++ b/src/client/components/CloudPage.js
@@ -0,0 +1,15 @@
+import template from '../../templates/components/cloud-page.html';
+
+export default function (app) {
+ app.component('cloudPage', {
+ bindings: {
+ page: ' route.get('/', index());
-export function routes(){
- return compose([
- main(),
- sitemap(),
- schedule(),
- news(),
- gallery(),
- documents(),
- teachers(),
- events(),
- articles(),
- error404()
- ]);
+export function routes() {
+ return compose([
+ main(),
+ sitemap(),
+ schedule(),
+ news(),
+ gallery(),
+ documents(),
+ teachers(),
+ events(),
+ articles(),
+ pages(),
+ error404()
+ ]);
}
export function queryObject() {
- let toTrue = map(true, i => i === 'true');
- let toFalse = map(false, i => i === 'false');
- const processors = [toTrue, toFalse];
+ let toTrue = map(true, i => i === 'true');
+ let toFalse = map(false, i => i === 'false');
+ const processors = [toTrue, toFalse];
- return async (ctx, next) => {
- let query = ctx.query;
- ctx.query = Object.keys(query)
- .reduce((query, key) => assign(
- query,
- key,
- processors
- .reduce((value, fn) => fn(value), query[key])
- ), query);
+ return async(ctx, next) => {
+ let query = ctx.query;
+ ctx.query = Object
+ .keys(query)
+ .reduce((query, key) => assign(
+ query,
+ key,
+ processors
+ .reduce((value, fn) => fn(value), query[key])
+ ), query);
- await next();
- }
+ await next();
+ }
}
export function accepts(routes) {
- return async function (ctx) {
- let request = ctx.request;
- let types = Object.keys(routes)
- .filter(type => request.accepts(type));
+ return async function (ctx) {
+ let request = ctx.request;
+ let types = Object.keys(routes)
+ .filter(type => request.accepts(type));
- if (types.length) {
- let fn = routes[types[0]];
+ if (types.length) {
+ let fn = routes[types[0]];
- await fn.apply(ctx, arguments);
- } else {
- ctx.status = 406;
- }
- };
+ await fn.apply(ctx, arguments);
+ } else {
+ ctx.status = 406;
+ }
+ };
}
export function index(fn) {
- let filename = config['defaultIndex'];
+ return async function (ctx) {
+ let test = true;
+ if (fn) test = await fn.apply(ctx, arguments);
+ if (!test) ctx.status = 404;
- return async function (ctx){
- let test = true;
- if (fn) test = await fn.apply(ctx, arguments);
+ const data = await getInitialData();
+ const source = await readFile(indexFile, 'utf-8');
+ const template = handlebars.compile(source);
- if (!test) ctx.status = 404;
-
- ctx.type = 'text/html';
- ctx.body = fs.createReadStream(filename);
- }
+ ctx.type = 'text/html';
+ ctx.body = template({data: JSON.stringify(data)});
+ }
}
-export function json(fn){
- return accepts({
- 'text/html': index(),
- 'text/plain': index(),
- 'application/json': fn
- });
+export function json(fn) {
+ return accepts({
+ 'text/html': index(),
+ 'text/plain': index(),
+ 'application/json': fn
+ });
+}
+
+async function getInitialData() {
+ const pages = await getPageUrls();
+ return {pages: pages};
}
diff --git a/src/routes/pages.js b/src/routes/pages.js
new file mode 100644
index 00000000..f6cec2fc
--- /dev/null
+++ b/src/routes/pages.js
@@ -0,0 +1,24 @@
+import route from 'koa-route';
+import {c} from '../core/db';
+import {accepts, index} from './';
+
+async function exists(ctx) {
+ const page = await getPage(ctx.path);
+ return Boolean(page);
+}
+
+async function getPage(path) {
+ return await c('pages').findOne({url: path});
+}
+
+export default function () {
+ return route.get('*', accepts({
+ 'text/html': index(exists),
+ 'text/plain': index(exists),
+ 'application/json': async(ctx) => {
+ const page = await getPage(ctx.path);
+ if (!page) ctx.status = 404;
+ else ctx.body = page;
+ }
+ }))
+};
diff --git a/src/server.js b/src/server.js
index 09066327..568b4661 100644
--- a/src/server.js
+++ b/src/server.js
@@ -6,61 +6,63 @@ import logger from 'koa-logger';
import convert from 'koa-convert';
import conditional from 'koa-conditional-get';
import etag from 'koa-etag';
-// import prerender from 'koa-prerender';
+import prerender from 'koa-prerender';
import helmet from 'koa-helmet';
import mount from 'koa-mount';
import bodyParser from 'koa-bodyparser';
-import api_v1 from 'hudozka-api-v1';
-import config from './config';
-import {redirectionTable} from './config';
+import apiV1 from 'hudozka-api-v1';
+import config, {redirectionTable} from './config';
import {routes, queryObject} from './routes';
import {redirect} from './routes/redirect';
import {services as serviceKeys, authChecker} from './core/service';
+import handlebars from 'handlebars';
const dirPublic = path.join(__dirname, '../public');
const dirTemplates = path.join(__dirname, 'templates');
-export default function(store){
- const $ = convert;
-
- const app = new Koa();
- app.proxy = true;
+handlebars.registerHelper('raw-helper', options => options.fn());
- app.use(bodyParser({
- extendTypes: {
- json: 'application/ejson'
- }
- }));
- app.use(logger());
- app.use(apis(store));
- app.use($(conditional()));
- app.use($(etag()));
- // app.use(prerenderRmFragment());
- // app.use($(prerender(config.prerender)));
- app.use(serve(dirPublic));
- app.use(serve(dirTemplates));
- app.use($(redirect(redirectionTable)));
- app.use($(helmet()));
- app.use(queryObject());
- app.use(routes());
+export default function (store) {
+ const $ = convert;
- return Server(app.callback());
+ const app = new Koa();
+ app.proxy = true;
+
+ app.use(bodyParser({
+ extendTypes: {
+ json: 'application/ejson'
+ }
+ }));
+ app.use(logger());
+ app.use(apis(store));
+ app.use($(conditional()));
+ app.use($(etag()));
+ app.use(prerenderRmFragment());
+ app.use($(prerender(config.prerender)));
+ app.use(serve(dirPublic));
+ app.use(serve(dirTemplates));
+ app.use($(redirect(redirectionTable)));
+ app.use($(helmet()));
+ app.use(queryObject());
+ app.use(routes());
+
+ return Server(app.callback());
}
-function apis(store){
- let checkAuth = authChecker(serviceKeys);
- return mount('/api/v1', api_v1(checkAuth, store));
+function apis(store) {
+ let checkAuth = authChecker(serviceKeys);
+ return mount('/api/v1', apiV1(checkAuth, store));
}
-function prerenderRmFragment(){
- return async (ctx, next) => {
- await next();
+function prerenderRmFragment() {
+ return async(ctx, next) => {
+ await next();
- try {
- let xp = ctx.response.header['x-prerender'] == 'true';
- if (xp) ctx.body = ctx.body.replace('', '');
- } catch (e) {
- }
- }
+ try {
+ let xp = ctx.response.header['x-prerender'] === 'true';
+ if (xp) ctx.body = ctx.body.replace('', '');
+ } catch (e) {
+ }
+ }
}
diff --git a/src/style/body-wrapper.scss b/src/style/body-wrapper.scss
index 9e1bcacf..a61f0073 100644
--- a/src/style/body-wrapper.scss
+++ b/src/style/body-wrapper.scss
@@ -2,6 +2,9 @@
.body-wrapper {
background-color: white;
+
+ font-family: $text-font;
+ font-size: $default-font-size;
}
.body-wrapper__content {
diff --git a/src/style/components/album.scss b/src/style/components/album.scss
index fc4313c9..6a1458c0 100644
--- a/src/style/components/album.scss
+++ b/src/style/components/album.scss
@@ -7,7 +7,7 @@
margin-bottom: 1.25em;
h3 {
- font-size: $big-font-size;
+ font-size: $biggest-font-size;
text-align: center;
}
diff --git a/src/style/components/article.scss b/src/style/components/article.scss
index 90593fb9..a64a730b 100644
--- a/src/style/components/article.scss
+++ b/src/style/components/article.scss
@@ -7,7 +7,7 @@
margin-bottom: 1.25em;
h3 {
- font-size: $big-font-size;
+ font-size: $biggest-font-size;
text-align: center;
}
@@ -33,13 +33,16 @@
margin: 1.25em auto $small-margin;
}
- ul{
- margin-bottom: 1.25em;
-
- list-style: none;
+ ul, ol{
+ margin-left: $half-margin;
+ margin-bottom: $single-margin;
padding-left: $single-margin;
}
+ ul{
+ list-style: disc;
+ }
+
p{
margin-bottom: 1.25em;
}
diff --git a/src/style/components/cloud-page.scss b/src/style/components/cloud-page.scss
new file mode 100644
index 00000000..e3b49649
--- /dev/null
+++ b/src/style/components/cloud-page.scss
@@ -0,0 +1,79 @@
+@import "../variables";
+
+.cloud-page {
+ margin-bottom: $post-margin;
+
+ &__body{
+
+ a {
+ color: $dark-color;
+ text-decoration: none;
+ border-bottom: 1px solid lighten($dark-color, 10%);
+
+ &:hover {
+ border-bottom: 1px solid lighten($dark-color, 25%);
+ }
+
+ &:active {
+ border-bottom: 1px solid $dark-color;
+ }
+ }
+
+ h1{
+ text-align: center;
+ margin-bottom: $single-margin;
+
+ font-size: $biggest-font-size;
+ }
+
+ h2 {
+ text-align: left;
+ margin-top: $double-margin;
+ font-family: $text-font;
+ }
+
+ ul, ol{
+ margin-left: $half-margin;
+ margin-bottom: $single-margin;
+ padding-left: $single-margin;
+ }
+
+ ul{
+ list-style: disc;
+ }
+
+ p{
+ margin-bottom: 1.25em;
+ }
+
+ blockquote {
+ background-color: hsl(215, 10%, 96%);
+ padding: 1em 1em .5em;
+ margin: $single-margin 0;
+ font-family: $title-font;
+ font-style: italic;
+ font-weight: normal;
+ font-size: 1.25em;
+
+ cite {
+ margin-top: $single-margin;
+ display: block;
+ color: #aaa;
+ font-size: initial;
+ font-family: $text-font;
+ text-align: right;
+ margin-right: $single-margin;
+ }
+ }
+ }
+}
+
+@media (max-width: $mobile-width) {
+ .cloud-page {
+ &__body {
+ img {
+ max-height: 20em;
+ }
+ }
+ }
+}
diff --git a/src/style/components/main-menu.scss b/src/style/components/main-menu.scss
index b6ebcdee..87b71925 100644
--- a/src/style/components/main-menu.scss
+++ b/src/style/components/main-menu.scss
@@ -3,7 +3,7 @@
.main-menu {
white-space: nowrap;
- font-size: 1.1em;
+ font-size: $normal-font-size;
color: $menu-inactive-item-color;
list-style: none;
diff --git a/src/style/components/timeline-record.scss b/src/style/components/timeline-record.scss
index 42ad514a..6d64a020 100644
--- a/src/style/components/timeline-record.scss
+++ b/src/style/components/timeline-record.scss
@@ -4,8 +4,6 @@
margin: 0 0 $post-margin;
.date {
- //margin: 0 0 0.45em;
- //font-size: 0.75em;
color: $light-color;
}
}
diff --git a/src/style/components/timeline.scss b/src/style/components/timeline.scss
index c3b60b5a..f3012069 100644
--- a/src/style/components/timeline.scss
+++ b/src/style/components/timeline.scss
@@ -10,7 +10,7 @@
display: flex;
justify-content: center;
- button.more{
+ button.more {
position: relative;
top: -2px;
@@ -20,13 +20,13 @@
padding: 0 10px;
background: none;
- font-size: 14pt;
+ font-size: $normal-font-size;
- &:hover{
+ &:hover {
background: gainsboro;
}
- &:active{
+ &:active {
top: 0;
}
}
diff --git a/src/style/footer.scss b/src/style/footer.scss
index cfcbca51..cf234f8e 100644
--- a/src/style/footer.scss
+++ b/src/style/footer.scss
@@ -9,6 +9,8 @@ footer {
position: relative;
background-color: $darker-color;
+ font-size: $smaller-font-size;
+
&:before {
$s: 3px;
diff --git a/src/style/hudozka-title.scss b/src/style/hudozka-title.scss
index 365f7d27..4cf365bb 100644
--- a/src/style/hudozka-title.scss
+++ b/src/style/hudozka-title.scss
@@ -8,20 +8,19 @@
p {
color: $light-color;
margin-bottom: $single-margin;
+ font-size: $smaller-font-size;
}
h1 {
margin-top: $single-margin;
- font-size: 2em;
+ font-size: $biggest-font-size;
}
}
@media (max-width: $mobile-width) {
.hudozka-title {
- text-align: center;
-
h1 {
- font-size: 1.5em;
+ font-size: $big-font-size;
}
}
}
diff --git a/src/style/main.scss b/src/style/main.scss
index e6f6479a..e5bc6ef4 100644
--- a/src/style/main.scss
+++ b/src/style/main.scss
@@ -10,7 +10,6 @@ html, body {
}
body {
- font-family: $text-font;
min-width: 320px;
}
diff --git a/src/style/style.scss b/src/style/style.scss
index 91c6b993..9ded95b8 100644
--- a/src/style/style.scss
+++ b/src/style/style.scss
@@ -11,6 +11,7 @@
@import "components/timeline-record-instagram";
@import "components/schedule-table";
@import "components/footer-links";
+@import "components/cloud-page";
@import "components/article";
@import "components/article-page-control";
@import "components/album";
diff --git a/src/style/variables.scss b/src/style/variables.scss
index f4e7937c..5dfdc202 100644
--- a/src/style/variables.scss
+++ b/src/style/variables.scss
@@ -16,7 +16,7 @@ $embed-video-ratio: 56.25%;
$small-margin: .2em;
$half-margin: .5em;
$single-margin: 1.0em;
-$double-margin: 3.0em;
+$double-margin: 2.0em;
$post-margin: 5.0em;
//COLOR
@@ -44,15 +44,28 @@ $white-shadow: 0 .1em 0.3em rgba(0, 0, 0, 0.2);
$title-font: "PT Serif", Georgia, serif;
$text-font: "PT Sans", Arial, sans-serif;
-$normal-font-size: 1em;
-$bigger-font-size: 1.2em;
-$big-font-size: 1.75em;
-$smaller-font-size: .8em;
+$default-font-size: 14pt;
+$smaller-font-size: 0.9rem;
+$normal-font-size: 1.0rem;
+$bigger-font-size: 1.2rem;
+$big-font-size: 1.5rem;
+$biggest-font-size: 2.5rem;
//MENU
$menu-thickness: 2.5em;
-$menu-colored-mark-thickness: 4px;
+$menu-colored-mark-thickness: 2px;
$menu-inactive-item-color: hsl(0, 0%, 50%);
//ANIMATION
-$short-duration: 0.3s;
\ No newline at end of file
+$short-duration: 0.3s;
+
+
+@media (max-width: $mobile-width) {
+ //FONTS
+ $default-font-size: 14pt;
+ $smaller-font-size: 0.9rem;
+ $normal-font-size: 1.0rem;
+ $bigger-font-size: 1.1rem;
+ $big-font-size: 1.3rem;
+ $biggest-font-size: 2.0rem;
+}
diff --git a/src/templates/components/cloud-page.html b/src/templates/components/cloud-page.html
new file mode 100644
index 00000000..1a162cf5
--- /dev/null
+++ b/src/templates/components/cloud-page.html
@@ -0,0 +1,3 @@
+
diff --git a/src/templates/main.html b/src/templates/main.html
index ff8cf9d6..23ea8d98 100644
--- a/src/templates/main.html
+++ b/src/templates/main.html
@@ -48,7 +48,11 @@
//ga('send', 'pageview');
}
+
+
+
+{{{{raw-helper}}}}
@@ -62,7 +66,6 @@
@@ -93,4 +96,6 @@
})();
+{{{{/raw-helper}}}}
+