From 58940904910163f8eedb967705b585fe4b5ae3b9 Mon Sep 17 00:00:00 2001
From: Anatoly Sablin
Date: Fri, 14 Dec 2018 22:52:10 +0300
Subject: [PATCH 01/36] Update version.
---
components/config.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/components/config.js b/components/config.js
index ef15dd9..75910ab 100644
--- a/components/config.js
+++ b/components/config.js
@@ -1,6 +1,6 @@
let domain = 'www.jabber.ru'
export let xmppDomain = 'jabber.ru'
-export let apiBase="/api/json/";
+export let apiBase="/api/v2/json/";
export let baseUrl = 'https://'+domain;
export let captchaSiteKey = "6Ld0CCATAAAAAAe5dOoNZhaFHAqak124eQS5t3Wu";
export let personalUrl = baseUrl + '/personal';
From 57b73348a61dc5de30fd49f912581af257270802 Mon Sep 17 00:00:00 2001
From: Anatoly Sablin
Date: Sat, 18 May 2019 17:14:33 +0300
Subject: [PATCH 02/36] Preparation for a new design.
---
.gitignore | 25 ++
components/about.js | 93 -----
components/anchor.js | 28 --
components/application.js | 164 ---------
components/callbackForm.js | 159 ---------
components/captcha.js | 81 -----
components/carousel.js | 167 ---------
components/chatlogs.js | 35 --
components/config.js | 12 -
components/form.messages.js | 53 ---
components/helpers.js | 31 --
components/history.js | 249 -------------
components/input.js | 95 -----
components/loading.state.js | 15 -
components/loginForm.js | 168 ---------
components/loginFormState.js | 242 -------------
components/menus.js | 117 -------
components/metrika.js | 1 -
components/notfound.js | 13 -
components/personal.js | 105 ------
components/root.js | 51 ---
components/scrollspy.js | 135 -------
components/settings.js | 184 ----------
components/svcs.js | 90 -----
components/user.js | 84 -----
css/_about.scss | 0
css/_carousel.scss | 160 ---------
css/_conferences.scss | 33 --
css/_fonts.scss | 62 ----
css/_footnlogog.scss | 235 -------------
css/_formsninputs.scss | 328 ------------------
css/_globals.scss | 107 ------
css/_loader.scss | 46 ---
css/_mainmenu.scss | 281 ---------------
css/_notfound.scss | 12 -
css/_personal.scss | 293 ----------------
css/_root.scss | 143 --------
css/_svcs.scss | 56 ---
index.js | 13 -
main.js | 59 ----
package.json | 60 ----
static/css/flickity.css | 141 --------
.../websymbolsligaregular.eot | Bin 16137 -> 0 bytes
.../websymbolsligaregular.svg | 182 ----------
.../websymbolsligaregular.ttf | Bin 38212 -> 0 bytes
.../websymbolsligaregular.woff | Bin 17948 -> 0 bytes
static/fonts/bicubik.ttf | Bin 11408 -> 0 bytes
static/fonts/bicubik.woff | Bin 7636 -> 0 bytes
static/fonts/proximanova-light-cur.otf | Bin 94316 -> 0 bytes
static/fonts/proximanova-light-webfont.eot | Bin 21299 -> 0 bytes
static/fonts/proximanova-light-webfont.ttf | Bin 45296 -> 0 bytes
static/fonts/proximanova-light-webfont.woff | Bin 24240 -> 0 bytes
static/fonts/proximanova-reg-cur.otf | Bin 94668 -> 0 bytes
static/fonts/proximanova-reg-cur.ttf | Bin 90112 -> 0 bytes
static/fonts/proximanovareg.ttf | Bin 130636 -> 0 bytes
static/fonts/proximanovareg2.ttf | Bin 231576 -> 0 bytes
static/static/archive.png | Bin 4422 -> 0 bytes
static/static/bg.png | Bin 21109 -> 0 bytes
static/static/glasses.png | Bin 3234 -> 0 bytes
static/static/hg.orig.png | Bin 47779 -> 0 bytes
static/static/hg.png | Bin 84099 -> 0 bytes
static/static/juick.png | Bin 5430 -> 0 bytes
static/static/logo.f.mblue.png | Bin 7374 -> 0 bytes
static/static/logo.mblue.png | Bin 11751 -> 0 bytes
static/static/logo.n.mblue.png | Bin 11938 -> 0 bytes
static/static/logo.png | Bin 9675 -> 0 bytes
static/static/ok.png | Bin 5789 -> 0 bytes
static/static/rating.png | Bin 6909 -> 0 bytes
static/static/saas.png | Bin 4530 -> 0 bytes
style.scss | 12 -
webpack.config.js | 172 ---------
71 files changed, 25 insertions(+), 4767 deletions(-)
create mode 100644 .gitignore
delete mode 100644 components/about.js
delete mode 100644 components/anchor.js
delete mode 100644 components/application.js
delete mode 100644 components/callbackForm.js
delete mode 100644 components/captcha.js
delete mode 100644 components/carousel.js
delete mode 100644 components/chatlogs.js
delete mode 100644 components/config.js
delete mode 100644 components/form.messages.js
delete mode 100644 components/helpers.js
delete mode 100644 components/history.js
delete mode 100644 components/input.js
delete mode 100644 components/loading.state.js
delete mode 100644 components/loginForm.js
delete mode 100644 components/loginFormState.js
delete mode 100644 components/menus.js
delete mode 100644 components/metrika.js
delete mode 100644 components/notfound.js
delete mode 100644 components/personal.js
delete mode 100644 components/root.js
delete mode 100644 components/scrollspy.js
delete mode 100644 components/settings.js
delete mode 100644 components/svcs.js
delete mode 100644 components/user.js
delete mode 100644 css/_about.scss
delete mode 100644 css/_carousel.scss
delete mode 100644 css/_conferences.scss
delete mode 100644 css/_fonts.scss
delete mode 100644 css/_footnlogog.scss
delete mode 100644 css/_formsninputs.scss
delete mode 100644 css/_globals.scss
delete mode 100644 css/_loader.scss
delete mode 100644 css/_mainmenu.scss
delete mode 100644 css/_notfound.scss
delete mode 100644 css/_personal.scss
delete mode 100644 css/_root.scss
delete mode 100644 css/_svcs.scss
delete mode 100644 index.js
delete mode 100644 main.js
delete mode 100644 package.json
delete mode 100644 static/css/flickity.css
delete mode 100644 static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.eot
delete mode 100644 static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.svg
delete mode 100644 static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.ttf
delete mode 100644 static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.woff
delete mode 100644 static/fonts/bicubik.ttf
delete mode 100644 static/fonts/bicubik.woff
delete mode 100644 static/fonts/proximanova-light-cur.otf
delete mode 100644 static/fonts/proximanova-light-webfont.eot
delete mode 100644 static/fonts/proximanova-light-webfont.ttf
delete mode 100644 static/fonts/proximanova-light-webfont.woff
delete mode 100644 static/fonts/proximanova-reg-cur.otf
delete mode 100644 static/fonts/proximanova-reg-cur.ttf
delete mode 100644 static/fonts/proximanovareg.ttf
delete mode 100644 static/fonts/proximanovareg2.ttf
delete mode 100644 static/static/archive.png
delete mode 100644 static/static/bg.png
delete mode 100644 static/static/glasses.png
delete mode 100644 static/static/hg.orig.png
delete mode 100644 static/static/hg.png
delete mode 100644 static/static/juick.png
delete mode 100644 static/static/logo.f.mblue.png
delete mode 100644 static/static/logo.mblue.png
delete mode 100644 static/static/logo.n.mblue.png
delete mode 100644 static/static/logo.png
delete mode 100644 static/static/ok.png
delete mode 100644 static/static/rating.png
delete mode 100644 static/static/saas.png
delete mode 100644 style.scss
delete mode 100644 webpack.config.js
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..86f2dbb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+.DS_STORE
+node_modules
+scripts/flow/*/.flowconfig
+scripts/rollup/results.json
+*~
+*.pyc
+.grunt
+_SpecRunner.html
+__benchmarks__
+build/
+remote-repo/
+coverage/
+.module-cache
+fixtures/dom/public/react-dom.js
+fixtures/dom/public/react.js
+test/the-files-to-test.generated.js
+*.log*
+chrome-user-data
+*.sublime-project
+*.sublime-workspace
+.idea
+*.iml
+.vscode
+*.swp
+*.swo
diff --git a/components/about.js b/components/about.js
deleted file mode 100644
index 3e34135..0000000
--- a/components/about.js
+++ /dev/null
@@ -1,93 +0,0 @@
-import {Footer} from "./menus.js";
-import {CallbackForm} from "./callbackForm.js";
-import {Anchor, Anchored} from "./anchor.js";
-import {Link} from 'react-router';
-import * as React from 'react';
-
-export const About = Anchored(class extends React.Component {
- render() { return (
-
- Часть общая
- Где я?
- Это - jabber.ru, крупнейший jabber сервер в рунете. По совместительству - старейший и, вероятно, самый надёжный. Если вы не представляете себе что такое jabber или xmpp, то вы можете прочесть статью в wikipedia (она излишне заумная). Просто поверьте нам на слово: jabber — это чат. Чат, который одинаково хорошо работает на телефоне, компьютере и вообще где угодно.
- Jabber правильно называть XMPP (Extensible Messaging and Presence Protocol), но мы по привычке продолжим использовать старое название.
- Просто чат?
- Да, просто чат. Чат 1 на 1, чат со множеством людей, чат с самим собой. Просто, надёжно и легко. Не способ найти собеседников, поддерживать социальную сеть или контролировать пользователя. Просто чат.
- Уже потом, на волне популярности, к этому чату приделали возможность играть в шахматы, оповещения о почте, прогнозы погоды, всяческих ботов и "шлюзы" в другие сети (например, icq). Но изначально, jabber - попытка сделать общение между людьми проще.
- Чем вы лучше других сервисов?
- Вот далеко не полный список наших плюсов:
-
- Мы бесплатны, надёжны и независимы. И это, пожалуй, самое важное.
- В jabbere используется множество независимых серверов. Неполадки одного сервера или банкротство компании не разрушат систему общения в целом;
- Наличие клиентов под все основные платформы. Можно пользоваться одним чатом и на телефоне, и на компьютере, и где угодно ещё;
- Отсутствие рекламы. Ваши сообщения не используются для продажи вам же рекламы. А со спамом мы боремся;
- Возможность использовать со своими адресами (user@your.domain.tld). Как email, только быстрее (это же "instant messaging"!);
- Зашифрованные сообщения позволяют передавать в jabber'е любую важную информацию без опасений её перехвата (эта функция выключена по умолчанию, но полюбилась очень многим);
-
- Если же сравнивать нас (jabber.ru) с другими публичными серверами, то из плюсов можно выделить надёжную и, главное, долгую работу нашего сервиса: несмотря на бесплатность, мы серьёзно относимся к своему делу.
- Вы классные, я хочу помочь
- Да, мы - классные. Но мы можем быть куда лучше. Да и существуем только благодаря поддержке сообщества. Если у вас есть идеи, предложения или возможность помочь проекту, вы можете обратиться к нам через форму обратной связи или через jabber, написав в конференцию support@conference.jabber.ru.
- Часть техническая
- Помогите, ничего не понимаю!
- Джаббер - это просто. Чтобы начать общаться нужно:
-
- Зарегистрироваться , указав имя пользователя и email;
- В почте найти письмо от нас, пройти по ссылке и указать свой пароль;
- Скачать программу, которая пришлась по вкусу;
- В программе ввести имя пользователя (будет выглядеть так: user@jabber.ru, где "user" — нужно заменить на свой логин) и пароль;
- Если вы дошли до этого места — помочь другу и добавить его в контакты;
- Начать общение.
-
- Какой клиент выбрать?
- На ваш выбор. Мы рекомендует Xabber для телефона, встроенный messages для OS X, для windows: Psi+, VacuumIM, Miranda NG. Если вы пользуетесь Линуксом, то вы сами знаете, какой клиент поставить. Также мы разрабатываем веб-клиент Kaiwa , который вы тоже можете попробовать.
- Клиент не работает, что делать?
- Во-первых, попытайтесь решить проблему сами: проверьте логин (должен содержать "@"), пароль, попробуйте поискать текст ошибки в интернете и почитать советы. Если ничего не помогает — попросите помощи у друзей или у нас через форму обратной связи.
- Как друзьям найти меня?
- Друзьям нужно сообщить имя, которое состоит из двух частей: вашего логина на сайте и домена "jabber.ru". Получится что-то похожее на "user@jabber.ru".
- Зачем в имени пользователя "@"?
- Символ "собачки", изначально, используется для указания принадлежности пользователя системе: user@server — пользователь "user" на сервере "server". Так и в джаббере, и в почте: user@jabber.ru — пользователь "user" на сервере "jabber.ru". Разделение имени пользователя на две части позволяет использовать множество серверов. А серверам — находить нужных пользователей.
- Мне не приходит письмо о регистрации, помогите
- Если письмо с регистрацией не приходит — проверьте папку "Спам", попробуйте зарегистрироваться ещё раз. Если ничего не помогает — напишите нам в форму обратной связи внизу страницы. Возможно, наш почтовый сервер тупит и нам нужно починить его. Вместо ответа на обращение вы просто получите письмо с ссылкой на регистрацию.
- Могу ли я отправлять/принимать почту со своего адреса в jabber.ru?
- Нет, не можете. Несмотря на то, что адреса очень похожи внешне, они используется разными программами. Позже, мы постараемся добавить возможность отправлять и принимать почту. Но использоваться для этого будет другая программа, не чат.
- Клиент хочет, чтобы я указал ему какой-то "ресурс". Что это?
- С одним именем пользователя вы можете подключаться к джабберу несколькими клиентами. Ресурс — это имя клиента. Можете вписать туда название устройства, случайную строку или вовсе игнорировать. Работать чат будет и без "ресурса".
- У меня не работает другой сервер с вами.
- В большинстве случаев, проблемы будут на вашей стороне. Прежде чем вы напишете нам в support@c.j.r , не поленитесь проверить свой сервер через сервис XMPP Observatory . Если по их данным с вашим сервером всё хорошо, проверьте, что регистрация на вашем сервере закрыта капчей. Так как мы не любим получать спам, мы предпочитаем закрываться от сервисов с открытой регистрацией. Проверить наличие вашего сервера в нашем спам листе можно сделав запрос к нашей DNSBL зоне 'dnsbl.jabber.ru'. Например, "jabber.ru.dnsbl.jabber.ru". Если же регистрация у вас закрыта, XMPP Observatory говорит, что с сервисом всё хорошо, а в DNSBL листе вы не значитесь — добро пожаловать в саппорт. Возможно, вы действительно нашли проблему у нас.
- Как удалить свой аккаунт?
- Принципиально, удалять аккаунт смысла нет. Но если осознание наличие аккаунта не даёт спокойно жить, то советуем воспользоваться функцией "Отмена регистрации" в различных клиентах. Например, такая функциональность есть в psi+. Администрация удалением аккаунтов не занимается.
- Как сменить свой пароль?
- В большинстве клиентов есть соответствующая функциональность. Попытайтесь воспользоваться ей. Если клиент не умеет менять пароль, то вы можете воспользоваться ссылкой "напомнить пароль" , ввести свой логин, и вам придёт ссылка для ввода нового пароля.
- Ко мне не приходят сообщения, почините!
- В феврале 2017 года мы запустили антиспам. Условия доставки сообщений достаточно просты: собеседник должен быть у вас в контакт листе и вы должны авторизовать его. Если вы уверены, что эти условия выполнены, а сообщения не проходят всё равно — напишите нам в support@c.j.r .
- От вас приходит спам! (You are sending spam!)
- Пожалуйста, свяжитесь с нами через форму обратной связи или в support@c.j.r и мы постараемся прекратить поток спама.
- Please report spam through feedback form at the bottom of this page or report spam to support@c.j.r . We'll do our best to stop spam.
- Часть планомечтательная
- Наши планы не всегда реалистичны или адекватны нашим возможностям. Но тем не менее, мы стремимся к следующему:
-
- Запуск почтового сервиса;
- Запуск платных услуг на базе Jabber.ru(хостинг доменов);
- Запуск собственного клиентского приложения с упрощённой регистрацией;
- Захват мира.
-
-{/*
- Часть историческая
- Начало начал
- История домена начинается со славного города Мытищи, промышленного и научного центра России . Домен изначально числился за провайдером east.ru. Почти год с этим доменом ничего не происходило, публичного сервиса как такого не было. В 2001 году домен фактически оказывается в руках ermine: знакомые по Фидо предлагают заняться jabber'ом, и предлагают помощь с оборудованием.
- С этого момента на jabber.ru появлется сайт и, собственно, jabber сервер. Изначально - jabber14. Позднее jabber мигрировали на ejabberd.
- Первые несколько лет jabber.ru переезжал несколько раз с одного сервера знакомых на другой, пока в 2003 году не был куплен собственный сервер на коммерческом хостинге.
- Значительное влияние на развитие проекта оказал Irsi (ныне забаненный на ЛОРе), который очень активно пиарил сервис на разных площадках. С его помощью аудитория начала расти куда быстрее.
- В 2005 году в историю сервиса ворвался Бобук. Он предложил обратиться к kukutz с просьбой о хостинге в Яндексе. И Яндекс, в лице kukutz, на эту просьбу дал согласие. Так Jabber.ru оказался в датацентрах Яндекса. Сначала на двух серверах, а ещё позже - на четырёх. Тут важно заметить, что во внутренние дела Яндекс никогда не вникал и сервис всегда оставался независимым.
- Почти все остальные события носят больше технических, нежели исторических характер. К проекту приложили свою руку разные люди, многие из которых значительно изменили лицо jabber.ru.
- Команда сегодня
- Сегодня основная команда jabber.ru, пожалуй, состоит из трёх человек. Найти их можно в support@conference.jabber.ru
- ermine Владычица, менеджер, директор - назовите как хотите. Но jabber.ru - детище именно ermine.
- zinid Программист, спортсмен и просто красавец . Человек, который отвечает за безперебойную работу собственно jabber'a, патчит и чинит сервер.
- vt Мастер на все руки. Помогает оказывать техническую поддержку пользователям, помогает конструктивной критикой, кодом в клиентские приложения.
- oxpa Системный администратор. Что-то чинит, что-то ломает и постоянно строит утопические планы.
-*/}
- Часть обратно контактная
-
-
-)}})
diff --git a/components/anchor.js b/components/anchor.js
deleted file mode 100644
index c85cda9..0000000
--- a/components/anchor.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import * as React from 'react'
-
-export class Anchor extends React.Component {
-
- render () {
- return
- }
-}
-
-export const Anchored = WithAnchors => class extends React.Component {
- componentDidMount () {
- console.log(window.location.hash, this.props.id)
- if (window.location.hash.length > 0){
- document.querySelector(window.location.hash).scrollIntoView()
- }
- }
- componentDidUpdate (prevProps) {
- if ((this.props.location.hash != prevProps.location.hash) && this.props.location.hash.length > 0) {
- document.querySelector(window.location.hash).scrollIntoView()
- }
- }
- render() {
- return
- }
-}
-Anchored.contextTypes = {
- router: React.PropTypes.object
- }
diff --git a/components/application.js b/components/application.js
deleted file mode 100644
index cbf6903..0000000
--- a/components/application.js
+++ /dev/null
@@ -1,164 +0,0 @@
-import { routerReducer } from 'react-router-redux';
-import thunk from 'redux-thunk';
-import {CallbackForm, cbFormState } from './callbackForm.js';
-import {setResetForm, loginForm, LoginFormConnected, setLoginForm, setRegForm, setRemindForm} from "./loginForm.js";
-import {carouselState, ConnectedCarousel} from "./carousel.js";
-import {ConnectedMainMenu} from "./menus.js";
-import {About} from "./about.js";
-import {Svcs} from "./svcs.js";
-import {PersonalSpace, settlePersonal} from "./personal.js";
-import {Settings, settingsState as settings, fetchSettings} from "./settings.js";
-import {Thread, MAMHistory, fetchHistory, setHistoryPeer} from "./history.js";
-import {user, userLogout} from "./user.js";
-import {loading} from "./loading.state.js"
-import {Root} from "./root.js";
-import {Chatlogs} from "./chatlogs.js";
-import {NotFound} from "./notfound.js";
-import {Router as Router_i, Route as Route_i} from 'react-router';
-import * as ReactRedux from 'react-redux';
-import * as React from 'react';
-import * as Redux from 'redux';
-
-
-const Router = ReactRedux.connect()(Router_i);
-//const DefaultRoute = ReactRedux.connect()(ReactRouter.Router.DefaultRoute);
-const Route = ReactRedux.connect()(Route_i);
-//const IndexRoute = ReactRedux.connect()(ReactRouter.IndexRoute);
-const Provider = ReactRedux.Provider;
-
-const Body = (props) =>
-
-
- {props.children}
-
-const ConnectedBody = ReactRedux.connect()(Body);
-
-let preset_vcard = {}
-let preset_jid = undefined;
-let loggedin = false;
-let preset_roster = [];
-if ((typeof(vcard) !== 'undefined') && vcard.vcard) {
- preset_vcard = vcard.vcard.vCard || {};
- preset_jid = vcard.jid || {};
- loggedin = true;
- preset_roster = roster.roster;
-}
-let preset_user = {vcard: preset_vcard, jid: preset_jid, loggedin: loggedin, roster: preset_roster}
-
-export var store = Redux.createStore(Redux.combineReducers({
- routing:routerReducer,
- loginForm,
- carouselState,
- cbFormState,
- user,
- loading,
- settings,
- MAMHistory
-}),{user:preset_user}, Redux.applyMiddleware(thunk))
-
-const loadSvcs = (nextState,cb)=>{
- require.ensure(
- ['./svcs.js']
- ,(require)=>{
- let svcs=require('./svcs.js');
- cb(null,svcs.Svcs)
- }
- ,'/svcs/svcs')
-
-}
-
-const loadAbout = (nextState,cb)=>{
- require.ensure(
- ['./about.js']
- ,(require)=>{
- let about=require('./about.js');
- cb(null,about.About)
- }
- ,'/about/about')
-
-}
-
- /*,{path:'about', getComponents: loadAbout }
- ,{path:'svcs', getComponent: loadSvcs}*/
-function clashSlashesOnEnter(nextState, replace) {
- let path = nextState.location.pathname;
- let flag = false;
- while(path.length > 1 && path.slice(-2) === '//') {
- path = path.slice(0, -1);
- flag = true;
- }
- if (flag) {
- replace({
- ...nextState.location,
- pathname: path
- })
- }
- return
-}
-function clashSlashesOnChange(prevState, nextState, replace) {
- clashSlashesOnEnter(nextState, replace)
-}
-
-export const Routes = [{
- path: '/',
- component: ConnectedBody,
- indexRoute: {component: Root},
- onEnter: clashSlashesOnEnter,
- onChange: clashSlashesOnChange,
- childRoutes: [
- {path: 'login', component: LoginFormConnected, store: store, onEnter: () => store.dispatch(setLoginForm())}
- ,{path: 'logout', store: store, onEnter: (route, replace, hook) => {store.dispatch(userLogout()), replace("/")}}
- ,{path: 'register', component: LoginFormConnected, store: store, onEnter: () => {store.dispatch(setRegForm())}}
- ,{path: 'remind', component: LoginFormConnected,store: store, onEnter: () => {store.dispatch(setRemindForm())}}
- ,{path: 'reset', component: LoginFormConnected,store: store, onEnter: () => {store.dispatch(setRemindForm())}}
- ,{path: 'reset/:ehash', component: LoginFormConnected, store: store, onEnter: () => {store.dispatch(setRemindForm(true))}}
- ,{path: 'register/:ehash', component: LoginFormConnected, store: store, onEnter: () => {store.dispatch(setRegForm(true))}}
- ,{path: 'about', component: About, store:store}
- ,{path: 'svcs', component: Svcs, store:store}
- ,{path: 'chatlogs', component: Chatlogs, store:store}
- ,{path: 'personal',
- component: PersonalSpace,
- store: store,
- onEnter: () => {store.dispatch(settlePersonal())},
- indexRoute: {component: Settings, onEnter: () => {store.dispatch(fetchSettings())}},
- childRoutes: [
- {path: 'roster/:jid', component: Thread, store: store,
- onEnter: (nextSt) => {
- store.dispatch(setHistoryPeer(nextSt.params.jid))
- store.dispatch(fetchHistory(nextSt.params.jid))
- },
- onLeave: (nextSt) => {
- store.dispatch(setHistoryPeer(undefined))
- }
- },
- {path: 'roster/:jid/:timestamp', component: Thread, store: store,
- onEnter: (nextSt) => {
- store.dispatch(setHistoryPeer(nextSt.params.jid))
- store.dispatch(fetchHistory(nextSt.params.jid, nextSt.params.timestamp))
- }
- },
- ]
- }
- ,{path:'download',component: ConnectedCarousel, store: store}
- ,{path:'*', component: NotFound, store: store}//, onEnter: (route, replace) => {replace("/")}}
- ],
- //onEnter: function(){resetCaptcha();console.log('entering main route')}
-}
-]
-export const App = (history) => (props)=>
-
-
-
-
-
-/*
-export const Routes = (
-
-
-
-
-
-
-)
-*/
-
diff --git a/components/callbackForm.js b/components/callbackForm.js
deleted file mode 100644
index cedb65b..0000000
--- a/components/callbackForm.js
+++ /dev/null
@@ -1,159 +0,0 @@
-import {isEmpty,checkFetchStatus} from './helpers';
-import {Recaptcha} from './captcha.js'
-import * as config from './config';
-import {InputField, TextArea, check} from './input.js'
-import {itemLoading} from './loading.state'
-import * as React from 'react'
-import * as ReactRedux from 'react-redux'
-
-const CBFORM = 'CBFORM'
-const SET = 'SET'
-const ERROR = 'ERROR'
-const RESULT = 'RESULT'
-
-const captcha = (props) =>
-
-
-
-const cbForm = (props) =>
-
-
-
-
-const mapCBFormDispatchToProps = (dispatch) => {
- return {
- sendForm: (props) => {dispatch(sendForm(props))},
- }
-}
-const mapCBFormStateToProps = (state) => {
- return {
- sending: state.loading.cbform,
- result: state.cbFormState.result,
- }
-}
-
-const mapInputPropsToDispatch = (inputType, stateName) => (dispatch) => {
- return {
- checkValue: () => dispatch(cbFormCheckValueAction(stateName || inputType)),
- setValue: (e="") => dispatch(cbFormValueAction(stateName || inputType, (typeof(e)==='string'?e:e.target.value)))
- }
-}
-const mapStateToInputProps = (inputType,stateName) => (state) => {
- return {
- ftype: inputType,
- display: true,
- val: state.cbFormState[stateName || inputType],
- incorrect: (stateName||inputType) in state.cbFormState.incorrect,
- error: state.cbFormState.incorrect[stateName || inputType]
- }
-}
-
-//const NameField = ReactRedux.connect(mapStateToInputProps('name'), mapInputPropsToDispatch('name'))(InputField);
-const EmailField = ReactRedux.connect(mapStateToInputProps('email'), mapInputPropsToDispatch('email'))(InputField);
-//const SubjectField = ReactRedux.connect(mapStateToInputProps('subject'), mapInputPropsToDispatch('subject'))(InputField);
-const RequestField = ReactRedux.connect(mapStateToInputProps('message'), mapInputPropsToDispatch('message'))(TextArea);
-const Captcha = ReactRedux.connect(mapStateToInputProps('captcha'),mapInputPropsToDispatch('captcha'))(captcha);
-
-const cbFormActionResult = (reason) => ({type: CBFORM, kind: RESULT, value: reason})
-const cbFormValueAction = (field, value) =>({type:CBFORM, kind:SET, value: {[field]:value}})
-const cbFormErrorAction = (field, error) => ({type:CBFORM, kind:ERROR, value:{[field]:error} })
-const cbFormCheckValueAction = (field) => (dispatch, getState) => {
- if (field == 'message') {return}
- let state = getState()
- let res = check[field](state.cbFormState)
- dispatch(cbFormErrorAction(field, res))
- if (res != "") {
- console.log('Value for', field, 'did not pass check with msg:', res)
- }
-}
-
-const handleSubmit = (props) => (e) => {
- e.preventDefault();
- props.sendForm(props);
- }
-
-function sendForm (props) {
- return (dispatch,getState) => {
- let state = getState();
- if (state.loading.cbform) {
- //doubleclick :(
- console.log('double click in callback post')
- return;
- }
- dispatch(itemLoading('cbform'))
- //let fields = ['name','subject','email','captcha', 'message'];
- let fields = ['email','captcha', 'message'];
- fields.forEach((elem) => {
- if (state.cbFormState[elem]) {dispatch(cbFormCheckValueAction(elem))}
- });
- state=getState();
- if (isEmpty(state.cbFormState.incorrect)) {
- let body={};
- fields.forEach((elem) => {
- if (state.cbFormState[elem]) {
- let value = state.cbFormState[elem];
- Object.assign(body,{[elem]:value});
- }
- });
- fetch(config.apiBase + 'feedback', {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- credentials: 'include',
- body: JSON.stringify(body)
- }).then(checkFetchStatus)
- .then((json) => {
- if (json['result'] == 'ok'){
- dispatch(cbFormActionResult())
- } else {
- dispatch(cbFormActionResult(json['error']))
- }
- if (typeof(grecaptcha) !== 'undefined') {
- grecaptcha.reset()
- }
- // prevent multiple posting on success
- dispatch(cbFormValueAction('email',''))
- dispatch(itemLoading('cbform'))
- })
- .catch((error) => {
- console.log(error);
- dispatch(cbFormErrorAction("An error occured. Please, retry"))
- dispatch(itemLoading('cbform'))
- });
- } else {
- dispatch(itemLoading('cbform'))
- }
- }
-}
-export let cbFormState = (state={incorrect:{}, email:"", message:"", result:"", captcha:""}, action) => {
- if (action.type != CBFORM) {return state}
- if (action.kind == RESULT) {return Object.assign({}, state, {result:"Ваше сообщение доставлено администратору и на него скоро ответят."})}
- if (action.kind == SET) return Object.assign({}, state, action.value)
- if (action.kind == ERROR) {
- let field = Object.keys(action.value)[0]
- if (action.value[field] == "") {
- let new_incorrect = Object.assign({}, state.incorrect)
- delete new_incorrect[field]
- return Object.assign({}, state, {incorrect: new_incorrect})
- } else {
- return Object.assign({}, state, {incorrect: Object.assign({}, state.incorrect, action.value)})
- }
- }
- return state;
-}
-
-
-export const CallbackForm = ReactRedux.connect(mapCBFormStateToProps,mapCBFormDispatchToProps)(cbForm)
diff --git a/components/captcha.js b/components/captcha.js
deleted file mode 100644
index d9e010f..0000000
--- a/components/captcha.js
+++ /dev/null
@@ -1,81 +0,0 @@
-import {captchaSiteKey} from './config.js'
-import * as React from 'react'
-
-export const resetCaptcha = (cptID) => {
- console.log('trying to reset captcha');
- if (typeof(cptId) === "undefined") {
- if(typeof(window.grecaptcha) !== "undefined" && typeof(window.cptID) !== "undefined") {
- console.log('found global recaptcha');
- try {
- window.grecaptcha.reset(window.cptID)
- } catch (err) {
- console.log('error reseting captcha', err)
- }
- } else {
- console.log('no global captcha could be found for reset!')
- }
- } else {
- if (typeof(window.grecaptcha) !== "undefined") {
- console.log('found',cptID, 'recaptcha');
- try {
- window.grecaptcha.reset(cptID)
- } catch (err) {
- console.log('error reseting captcha', err)
- }
- } else {
- console.log('no local captcha could be found for reset!')
- }
- }
-}
-
-export class Recaptcha extends React.Component {
- constructor(props) {
- super(props);
- //this.props = Object.assign({},props);
- }
- componentWillUnmount () {
- resetCaptcha(window.cptID)
- }
- renderCaptcha (that) { return () => {
- console.log('that in captcha is', that)
- window.cptID = grecaptcha.render("recaptcha",
- {"sitekey": that.props.sitekey,
- "theme": that.props.theme,
- "callback": that.props.callback,
- "data-expired-callback": that.props.expireCallback,
- "size": that.props.size,
- "tabIndex": that.props.tabIndex
- })
-
- }}
- componentDidMount () {
- if (typeof (grecaptcha) === "undefined") {
- window.renderCaptcha = this.renderCaptcha(this);
- var script = document.createElement('script');
- script.type = 'text/javascript';
- script.src = 'https://www.google.com/recaptcha/api.js?render=explicit&onload=renderCaptcha';
- script.async = true;
- document.body.appendChild(script);
- } else {
- this.renderCaptcha(this)();
- }
- }
- render() {
- return
- }
-}
-
-Recaptcha.PropTypes = {
- sitekey: React.PropTypes.string,
- theme: React.PropTypes.string,
- callback: React.PropTypes.func.isRequired,
- expireCallback: React.PropTypes.func.isRequired,
- size: React.PropTypes.string,
- tabIndex: React.PropTypes.number.isRequired,
- className: React.PropTypes.string
-}
-Recaptcha.defaultProps = {
- sitekey: captchaSiteKey,
- theme: "light",
- size: "normal"
-}
diff --git a/components/carousel.js b/components/carousel.js
deleted file mode 100644
index 47e0d1b..0000000
--- a/components/carousel.js
+++ /dev/null
@@ -1,167 +0,0 @@
-import * as React from 'react'
-import * as ReactRedux from 'react-redux'
-const platforms = [
- {id: 0, name:"Windows"},
- {id: 1, name:"WinPhone"},
- {id: 2, name:"OSX"},
- {id: 3, name:"IOS"},
- {id: 4, name:"Linux"},
- {id: 5, name:"Android"}
-];
-const clients = [
- {Name: "Xabber", ImgUrl: "https://lh3.googleusercontent.com/5ZCn-z0We-puYjQ_t1m3RATzuakUurga5gRvWkpNVZHhmgdHvIQj_tpvs93CKebOqquT=h600", Description: "Jabber Клиент с открытым исходным кодом, простым и аккуратным дизайном. Можно использовать одновременно с несколькими аккаунтами. Разрабатывается с целью быть лучшим андроид клиентом: не просто открытым, но и свободным от рекламы.
Поддерживает в том числе:
Modern material interface Multiple accounts support Multi user chat (MUC) Chat history Avatars Notification setting for each contact Stream compression OTR, TLS, SSL, SASL Message delivery receipts
", tags: [5]},
- {Name: "Conversations", ImgUrl: "https://lh3.ggpht.com/ib4uM-oxW1Q8zSHib_UJVPaw73G5AHF1B3Swx_MXDXNzXf3hBDqgHnMWtYxChZ1I4fs=h600", Description: "Открытый jabber клиент разработанный специально для Android 4+
Создавался с целью быть как можно более красивым и удобным, без ущерба для безопасности использования.
Поддерживает несколько аккаунтов, MUC, любые виды шифрования (OMEMO, OTR, PGP), отправку и приём изображений. Интегрируется с адресной книгой телефона. Почти не влияет на потребление электропитания.
", tags: [5]},
- {Name: "Yaxim", ImgUrl: "https://lh4.ggpht.com/9VvBWxJ7-S6SWb9_x8uVS8C_VVe9khX649Dzm_MWQ12a88hYexDCmMmgF4DuwEssHQ=h600", Description: "Минималистичный клиент для Android.
Несмотря на простой дизайн, yaxim стремится быть надёжным и полезным приложением. Из существенных плюсов можно отметить легковесность, поддержку конференций, подтверждения о доставке сообщений.
https://yaxim.org/download/
", tags: [5] },
- {Name: "Adium", ImgUrl: "https://upload.wikimedia.org/wikipedia/commons/c/c5/AdiumX_screenshot.png", Description: "Универсальный клиент мгновенного обмена сообщениями (мессенджер) для Mac OS X-систем, который поддерживает множество протоколов, и выпущен под лицензией GNU GPL.
Поддержка русского языка, использование многочисленных протоколов, tab-ы, уникальные возможности настройки интерфейса, возможность шифрования сообщений - вот далеко не полный список его возможностей.
http://www.ixbt.com/td/adium.shtml
", tags: [2]},
- {Name: "Pidgin", ImgUrl: "https://upload.wikimedia.org/wikipedia/commons/b/bc/Pidgin_Ubuntu_Buddy_List.png", Description: "Один из лучших кроссплатформенных клиентов. Поддерживает сразу несколько протоколов и аккаунтов (ICQ, Jabber, MSN, умеет Twitter)
Позволяет сохранять комментарии к пользователям из контакт‐листа. Может объединять несколько контактов в один метаконтакт.
https://ru.wikipedia.org/wiki/Pidgin ", tags: [0,2,4]},
- {Name: "IM+", ImgUrl: "https://upload.wikimedia.org/wikipedia/ru/c/c2/Im_plus_screenshot.png", Description: "кроссплатформенная программа обмена мгновенными сообщениями для мобильных устройств, а также кроссплатформенное веб-приложение. Поддерживает протоколы Twitter, Facebook, Google Talk, XMPP, Yahoo!, AOL Instant Messenger, ICQ, Myspace, Windows Live Messenger/MSN, ВКонтакте, Mail.Ru Агент, Я.Онлайн, Одноклассники.ru.
Позволяет обмениваться файлами, сохраняет историю, допускает чат сразу с несколькими собеседниками, поддерживает скины и графические смайлики.
", tags: [0, 1, 3, 5]}
-];
-const emptyPage = [{Name:"Ничего не нашлось",Description:"По вашим критериям не нашлось ни одного клиента. Попробуйте подобрать пару хороших клиентов для разных платформ. Или напишите свой: чем вы хуже других?", tags:[]}]
-import {Footer} from "./menus.js";
-import {isEmpty} from "./helpers.js";
-const TAB = 'TAB'; // tab changed action
-const TAG = 'TAG'; // client tag lock action
-
-const CarouselCell = (props) =>
-
-
-
{props.Client.Name}
-
-
- {props.Client.Name}
-
-
-
-
-const OsTags = (props) =>
- {
- props.platforms.map( (tag) =>
-
-
- {/*tag.name*/}
-
- )}
-var Carousel = React.createClass({
- componentWillUpdate: function (newProps) {
- if (this.props.lockedTags != newProps.lockedTags) {
- this.flkty.deactivate();
- }
- },
- componentDidUpdate: function (oldProps) {
- //console.log('tags changes?',this.props,oldProps);
- if (this.props.lockedTags != oldProps.lockedTags) {
- this.flkty.activate();
- //this.flkty.reloadCells();
- }
- },
- componentDidMount: function () {
- if (typeof(document.querySelector) !== "undefined") {
- var elem = document.querySelector('.main-carousel');
- require.ensure(["flickity"], (require)=> {
- let flickity = require('flickity')
- var flkty = new flickity( elem, {
- contain: true,
- setGallerySize: true,
- wrapAround: true,
- noDomMod: true,
- dragThreshold: 20,
- watchCSS: true
- });
- flkty.on('cellSelect', this.props.onCellSelect(flkty));
- this.flkty = flkty;
- }, 'flickity');
- }
- },
- render: function() {
- var cells = this.props.clients.map(function(client){
- return ()
- });
- return (
-
- )
- }
-});
-
-export function carouselState(state={clients:clients, selectedTags:clients[0].tags, lockedTags: []}, action){
- if (action.type == TAG) {
-
- var lockedTags = Object.assign([],state.lockedTags);
- var idx;
- if ((idx = lockedTags.indexOf(action.tag)) < 0 ) {
- lockedTags.push(action.tag)
- }else {
- lockedTags.splice(idx,1)
- };
- var clientsToShow = [];
- if (lockedTags.length>0) {
- console.log('locked tags:', lockedTags);
- clientsToShow = clients.filter(
- client => lockedTags.every( tag => client.tags.indexOf(tag)>-1)
- )
- if (isEmpty(clientsToShow)) { clientsToShow=emptyPage}
-
- } else {
- clientsToShow = clients
- }
- var selectedTags = clientsToShow.length>0?clientsToShow[0].tags:[]
- return Object.assign({},state,{clients: clientsToShow,
- lockedTags: lockedTags,
- selectedTags: selectedTags})
- }
- if (action.type == TAB){
- console.log('setting tags by tab', action.tab);
- return Object.assign({}, state, {selectedTags:state.clients[action.tab].tags})
- }
- return state
-}
-
-function setLockedTag(tagId) {
- return {type:TAG, tag: tagId}
-}
-function setSelectedPlatformsByTab(TabIndex) {
- return {type:TAB, tab:TabIndex}
-}
-
-const mapCarouselStateToProps = (state) => {
- return {
- clients: state.carouselState.clients,
- platforms: platforms,
- selectedTags: state.carouselState.selectedTags,
- lockedTags: state.carouselState.lockedTags
- }
-}
-const mapCarouselDispatchToProps = (dispatch) => {
- return {
- onCellSelect: (flkty) => { return function () {dispatch(setSelectedPlatformsByTab(flkty.selectedIndex)) } },
- onTagClick: (tagId) => {return function() {dispatch(setLockedTag(tagId))}}
- }
-}
-const PropTypes = React.PropTypes
-Carousel.propTypes = {
- onCellSelect: PropTypes.func.isRequired,
- clients: PropTypes.array.isRequired,
- platforms: PropTypes.array.isRequired,
- selectedTags: PropTypes.array.isRequired
-}
-
-export const ConnectedCarousel = ReactRedux.connect(mapCarouselStateToProps,mapCarouselDispatchToProps)(Carousel)
-
diff --git a/components/chatlogs.js b/components/chatlogs.js
deleted file mode 100644
index 4208fa8..0000000
--- a/components/chatlogs.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import {Footer} from "./menus.js";
-import {Anchor, Anchored} from "./anchor.js";
-import {Link} from 'react-router';
-import * as React from 'react';
-
-const conferences_jabber_ru = ["42fm", "4pda.ru", "bombus-talks", "bombus", "bombusmod", "clubjabber", "codingteam", "comp.lang.c", "coq", "c_plus_plus", "debian", "devel", "dotnet", "ejabberd", "emacs", "english", "erlang", "erlim", "fangamedev", "freebsd", "freestyle", "gentoo", "golang", "haskell", "icfpc", "java", "javascript", "jtalk", "linux-talks", "linux", "lisp", "mint", "miranda-im", "miranda-ng", "mobile", "moto", "nethack", "ocaml", "pikabu", "programming", "psi-dev", "rock", "scala", "sex", "siemens", "support", "symbian", "sysadmins", "talks", "tcl", "tkabber", "vim", "web", "world_of_tanks", "xmonad", "yaroslavl"]
-const with_img_jabber_ru = ["bombusmod", "ejabberd", "linux", "miranda-ng", "nethack", "ocaml", "debian", "emacs", "scala", "tkabber", "xmonad", "codingteam", "haskell", "jtalk", "dotnet", "erlang"]
-
-const style = (name) => {
- if (with_img_jabber_ru.includes(name)) {
- return {backgroundImage:'url(https://chatlogs.jabber.ru/img/chatlogs/'+name+'.ico)'}
- } else {
- return {}
- }
-}
-const Conference = (props) => (
-
-)
-const conf_server = "conference.jabber.ru"
-const conferences = conferences_jabber_ru.map((c) => ())
-export const Chatlogs = (props) => (
-
-)
diff --git a/components/config.js b/components/config.js
deleted file mode 100644
index 75910ab..0000000
--- a/components/config.js
+++ /dev/null
@@ -1,12 +0,0 @@
-let domain = 'www.jabber.ru'
-export let xmppDomain = 'jabber.ru'
-export let apiBase="/api/v2/json/";
-export let baseUrl = 'https://'+domain;
-export let captchaSiteKey = "6Ld0CCATAAAAAAe5dOoNZhaFHAqak124eQS5t3Wu";
-export let personalUrl = baseUrl + '/personal';
-
-export let regSuccess = "На указанный вами email отправлено письмо с ссылкой для подтверждения регистрации. Если письмо не пришло к вам в течение 30 минут, пожалуйста, воспользуйтесь формой обратной связи и сообщите нам о проблеме";
-export let loginSuccess = "Перенаправляю..."
-export let remSuccess = "На использованный вами при регистрации email было отправлено письмо с дальнейшими инструкциями."
-export let resetSuccess = "Пароль успешно изменён. Теперь вы можете войти используя его."
-
diff --git a/components/form.messages.js b/components/form.messages.js
deleted file mode 100644
index eff3a85..0000000
--- a/components/form.messages.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import * as React from 'react';
-
-let success = {};
-success.login =
-
-
Успешно
-
Наш охранный ёж решил,
- что может пустить вас.
-
-
-
-success.register =
-
-
Успешно
-
Выбранное вами имя зарегистрировано и на указанный email было отправлено письмо с дальнейшими инструкциями по активации аккаунта. Ссылка будет работать одни сутки.
-
Пожалуйста, пройдите по ссылке из письма, чтобы завершить регистрацию.
-
-
-
-success.remind =
-
-
Успешно
-
Наши ежи с трудом запоминают новые пароли, но мы знаем как упростить эту задачу. Вам на почту была отправлена ссылка с секретным словом для обучения ежей.
-
Используйте ссылку из письма, чтобы указать новый пароль.
-
-
-success.password =
-
-
Успешно
-
Пока мы пытаемся объяснить охранным ежам, что пароль изменился, попробуйте войти в личный кабинет с новым паролем.
-
-
-
-let errors = {}
-errors['not-authorized'] = 'Неверный логин или пароль'
-errors['bad-key'] = 'Неверный токен. Попробуйте запросить ссылку ещё раз.'
-errors['weak-password'] = 'Попробуйте ещё раз с более сложным паролем.'
-errors['email-exceeds'] = 'На этот email зарегистрировано слишком много аккаунтов. Попробуйте другой.'
-errors['email-not-exists'] = 'Мы не знаем, куда отправить письмо. Возможно, аккаунт был удалён.'
-errors['invalid-content-type'] = 'Упс! Что-то пошло не так. Попробуйте ещё раз или сообщите нам.'
-errors['service-error'] = 'Упс! Что-то пошло не так. Попробуйте ещё раз или сообщите нам.'
-errors['incomplete-data'] = 'Упс! Что-то пошло не так. Попробуйте ещё раз или сообщите нам.'
-errors['captcha-failure'] = 'Капча думает, что вы робот. Попробуйте ещё раз.'
-errors['login-already-exists'] = 'Такая учётная запись уже есть :( Попробуйте другой логин.'
-errors['item-not-found'] = 'Упс! Что-то пошло не так. Попробуйте ещё раз или сообщите нам.'
-errors['invalid-email'] = 'Сервер считает, что ваша почта "плохая". Попробуйте другую.'
-errors['invalid-jid'] = 'Имя пользователя не прошло проверку сервера. Вы точно не почту вводите? Попробуйте другой псевдоним.'
-errors['email-not-match'] = 'Имя пользователя и почта не совпадают.'
-errors['one-time-email'] = 'Выберите другой одноразовый ящик, этот мы знаем.'
-errors['unknown-mail-domain'] = 'Наш почтовый сервер не знает про этот домен. Попробуйте другой домен или сообщите нам об ошибке.'
-
-
-export {success, errors}
diff --git a/components/helpers.js b/components/helpers.js
deleted file mode 100644
index 2ccd453..0000000
--- a/components/helpers.js
+++ /dev/null
@@ -1,31 +0,0 @@
-export function isEmpty(object) {
- for(var key in object) {
- if(object.hasOwnProperty(key)){
- return false;
- }
- }
- return true;
-}
-export function some(object, test) {
- var i = 0
- for(var key in object) {
- if(object.hasOwnProperty(key)){
- if (test(object[key], i, object)) {
- return true
- } else {
- i+=1
- }}}
- return false
-}
-export function checkFetchStatus(response, debug=false) {
- console.log('checking status of', response);
- if (response.status >= 200 && response.status < 300) {
- if (debug) {console.log('got response:',response);}
- return response.json()
- } else {
- var error = new Error(response.statusText)
- error.response = response
- throw error
- }
-}
-
diff --git a/components/history.js b/components/history.js
deleted file mode 100644
index 361e6d6..0000000
--- a/components/history.js
+++ /dev/null
@@ -1,249 +0,0 @@
-import {itemLoading} from './loading.state';
-import {isEmpty,checkFetchStatus} from './helpers'
-import { Scrollbars } from 'react-custom-scrollbars'
-import * as config from './config.js';
-import * as React from 'react';
-import * as ReactRedux from 'react-redux';
-
-const HISTORY = 'HISTORY'; // history actions
-const FETCH = 'FETCH'; // start fetching from server
-const PEER = 'PEER'; // save current chat peer jid
-const UPDATE = 'UPDATE'; // add messages into state
-const UREQ = 'REQUEST_UPDATE'; // request history update
-const ALLMSG = 'ALL_MSG'; // all messages from history are fetched
-const HEIGHT = 'HEIGHT'; // save previous scroller height to calculate offset later
-const LONG_INTERVAL = 5000; // poll for new messages every 5 seconds
-const SHORT_INTERVAL = 200; //
-
-class Scroller extends React.Component {
- render () {
- return
- }
-}
-
-const message = (props) => {
- let dt = new Date(props.ts/1000)
- return
-
- {props.peer_name}
- {props.my_name}
-
-
-
{'' + dt.getFullYear() + '-' + (dt.getMonth() + 1) + '-' + dt.getDate()}
-
{'' + dt.getHours() + ':' + dt.getMinutes()}
-
-
{props.txt}
-
-}
-const Message = message
-
-const Nomsgs = (props) =>
- Позднее здесь будет история общения с вашим собеседником
-
-
-class thread extends React.Component {
- checkScrollerVisible () {
- return () => {
- if (this.should_update) {
- this.should_update = false
- //let tp = this.scrollbar.refs.view.scrollHeight - this.scrollbar.refs.view.scrollTop
-
- //console.log('visible hight mark is', tp)
- //let tp = this.props.old_height
- if (! this.props.loading && ! this.props.no_more) {
- this.props.getMoreMessages(this.props.peer, this.props.oldest_ts)//, tp)
- }
- }
- }
- }
- componentDidMount () {
- if (this.scrollbar && (this.scrollbar.refs.view.scrollHeight !=0) ) {
- this.scrollbar.scrollToBottom()
- }
- if (this.props.peer)
- this.props.getMoreMessages(this.props.peer, this.props.oldest_ts)
- }
- componentWillReceiveProps (nextProps) {
- if ( (nextProps.messages.length != this.props.messages.length)
- && this.props.peer == nextProps.peer
- && this.scrollbar && nextProps.messages.length > 0) {
- //console.log('saving position for nextProps', nextProps)
- let srv = this.scrollbar.refs.view
- console.log('saving position:', srv.scrollHeight, srv.scrollTop, srv.scrollHeight - srv.scrollTop)
- this.props.saveScrollPosition(srv.scrollHeight - srv.scrollTop)
- }
- }
- componentDidUpdate (newprops){
- if (this.props.peer != newprops.peer ) {
- if (this.scrollbar) {
- this.scrollbar.scrollToBottom()
- }
- this.props.saveScrollPosition(null)
- }
- if (this.props.old_height && this.scrollbar) {
- let srv = this.scrollbar.refs.view
- console.log(this.props.messages.length, newprops.messages.length)
- console.log('unscrolling to', srv.scrollHeight, '-', this.props.old_height, srv.scrollHeight - this.props.old_height)
- this.scrollbar.scrollTop(srv.scrollHeight - this.props.old_height)
- this.should_update = true
- }
- //console.log('loading', this.props.loading, 'no_more', this.props.no_more)
- if (this.scrollbar && ! this.props.loading && ! this.props.no_more ){
- console.log('no more is', this.props.no_more)
- // basically, if DOM had settled, there are not enough messages,
- // but we didn't try fetching
- //console.log(scr.view.scrollHeight, '<=', scr.view.clientHeight)
- let srv = this.scrollbar.refs.view
- if (srv.scrollHeight <= srv.clientHeight) {
- console.log('visible high mark after update', srv.scrollHeight)
-
- this.props.getMoreMessages(this.props.peer, this.props.oldest_ts, srv.scrollHeight, this.props.old_height)
- }
- }
- }
- render () {
- let props = this.props
- let messages = []
- messages = props.messages.map((m, i) => )
-
- if (messages.length == 0) {
- messages =
- } else {
- if(this.props.no_more) {
- messages.unshift(
- Ë Ë Ë
-
)}
- messages.push(È È È
)
- }
-
- return
- this.scrollbar = scrollbar}
- hideTracksWhenNotNeeded={true}
- onScrollAtTop={this.checkScrollerVisible()}
- edgeYThreshold={500}
- >
- {messages}
-
-
- }
-}
-const mapStateToThreadProps = (state) => {
- let msgs = []
- let oldest_ts = null
- let jid = state.MAMHistory.peer
- let peer = state.user.roster.filter((peer) => (peer.jid == jid))[0] || undefined
- let peer_name = peer && peer.name || state.MAMHistory.peer
- if (state.MAMHistory.msgs[state.MAMHistory.peer]) {
- let ids = Object.keys(state.MAMHistory.msgs[state.MAMHistory.peer]).sort()
- oldest_ts = ids[0]
- msgs = ids.map(
- (i) => state.MAMHistory.msgs[state.MAMHistory.peer][i]
- )
- }
- return {
- loading: state.loading[jid] || false,
- messages: msgs || [],
- no_more: (state.MAMHistory.no_more.indexOf(jid) != -1),
- old_height: state.MAMHistory.old_height,
- peer: state.MAMHistory.peer,
- peer_name: peer_name,
- my_name: state.user.vcard? state.user.vcard.NICKNAME : state.user.jid || "My precious",
- oldest_ts: oldest_ts
- }
-}
-const mapDispatchToThreadProps = (dispatch) => ({
- saveScrollPosition: (height) => dispatch(saveHeight(height)),
- getMoreMessages: (jid, timestamp, height, oldheight) => {
- if(!oldheight || (height != oldheight)) {dispatch(saveHeight(height))};
- dispatch(fetchHistory(jid, timestamp))
- },
- shouldUpdate: () => dispatch(requestHistoryUpdate(true))
-})
-
-export const Thread = ReactRedux.connect(mapStateToThreadProps, mapDispatchToThreadProps)(thread)
-
-const requestHistoryUpdate = (flag) => ({type: HISTORY, kind: UREQ, value: flag})
-const updateHistory = (jid, history) => (
- {type: HISTORY, kind: UPDATE, value: [jid, history]}
-)
-const setPeerJid = (jid) => ({type: HISTORY, kind: PEER, value: jid})
-const setEndReached = (jid) => ({type: HISTORY, kind: ALLMSG, value: jid})
-const saveHeight = (height) => ({type: HISTORY, kind: HEIGHT, value: height})
-
-export const setHistoryPeer = (jid) => (dispatch, getState) => {
- dispatch(setPeerJid(jid))
-}
-export const fetchHistory = (jid, timestamp=null) => (dispatch, getState) => {
- let state = getState()
- if (jid === undefined) return
- if (state.loading[jid]) {
- console.log('history for ', jid, ' is being fetched already')
- return
- }
- dispatch(itemLoading(jid))
- //console.log('fetching data since', timestamp)
- let body = Object.assign({jid:jid}, timestamp?{ts:parseInt(timestamp)}:{})
- fetch(config.apiBase + 'mam/messages', {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- credentials: 'include',
- body: JSON.stringify(body)
- }).then((response) => checkFetchStatus(response,true)
- ).then((json) => {
- if ('error' in json && json.error != "") {
- //console.log('Error getting messages for '+ jid + ', something went wrong')
- console.log(json.error)
- }
- let messages = Object()
- if( json.messages) {
- json.messages.forEach((msg) => (messages[msg.ts] = msg))
- } else {
- dispatch(setEndReached(jid))
- }
- dispatch(updateHistory(jid, messages))
- dispatch(itemLoading(jid))
- }).catch((error)=>{
- console.log('Something went wront fetching messages for', jid,'.Message was:', error)
- dispatch(itemLoading(jid))
- })
-
-}
-
-export const MAMHistory = (state = {msgs:{}, no_more:[], old_height:null}, action) => {
- if (action.type != HISTORY) {return state}
- switch (action.kind) {
- case UPDATE: {
- let [jid, messages] = action.value
-
- let jid_messages = Object.assign({}, state.msgs[jid]||{}, messages)
-
-
- return Object.assign({},
- state,
- { msgs: Object.assign({}, state.msgs,
- {[jid]: jid_messages}
- )}
- )
- }
- case PEER: {
- return Object.assign({}, state, {peer: action.value, old_height: null})
- }
- case ALLMSG: {
- let no_more = [ ...state.no_more, action.value]
- return Object.assign({}, state, {no_more: no_more})
- }
- case HEIGHT: {
- return Object.assign({}, state, {old_height:action.value})
- }
- }
- return state;
-
-}
-
-
-
-
diff --git a/components/input.js b/components/input.js
deleted file mode 100644
index c222e04..0000000
--- a/components/input.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import * as React from 'react'
-
-export class TextArea extends React.Component{
- render () {
- let props=this.props;
- if (!props.display) {return null}
- return (
-
-
- {(props.incorrect)?
{props.error}
- : ""
- }
-
)
- }
-}
-
-export class InputField extends React.Component{
- componentDidMount() {
- this.props.focus && this.props.display && this._input.focus();
- }
- render () {
- let props=this.props;
- if (!props.display) {return null}
- return (
-
-
this._input=inp }
- />
- {(props.incorrect)?
{props.error}
- : ""
- }
-
)
- }
-}
-
-let check = {};
-check.login = (state) => {
- let Login = state.login;
- let Domains = state.domains;
- if (Login.length < 1) return "Введите логин, пожалуйста";
- if (Login.length > 512) return "Пожалуйста, используйте более короткий логин";
- if(!(typeof(Domains) === 'undefined')) {
- if (Login.indexOf('@') != -1) {
- let domain = Login.split('@',2)[1];
- console.log(domain);
- if (Domains.some((elem) => elem === domain)) {return "";}
- else return "Мы не поддерживаем домен "+domain;
- }
- //return "Логин не должен содержать символ '@'"
- };
-
- return "";
-};
-check.password = (state) => {
- let Password = state.password;
- if (Password.length <5) return "Пароль должен быть не менее 5 букв";
- if (Password.length >512) return "Пожалуйста, используйте более короткий пароль";
- return "";
-};
-check.email = (state) => {
- var filter=/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*$/ui;
- if (filter.test(state.email)) {
- return "";
- } else {
- return "Не больно-то это похоже на email адрес";
- }
-};
-check.captcha = (state) => {
- if (state.captcha.length > 1) {return ""}
- return "заполните, пожалуйста, капчу"
-};
-check.confirmation = (state) => {
- if (state.confirmation == state.password) {return ""}
- return "введённые пароли должны совпадать!"
-}
-
-export {check};
diff --git a/components/loading.state.js b/components/loading.state.js
deleted file mode 100644
index bb45b7d..0000000
--- a/components/loading.state.js
+++ /dev/null
@@ -1,15 +0,0 @@
-export const LOAD = 'LOAD';
-export const itemLoading = (item) => ({type: LOAD, kind: item})
-export const loading = (state = {}, action) => {
- if (action.type != LOAD) return state
- console.log('trying set "',action.kind,'" item loading')
- let new_state = Object.assign({}, state)
- if (action.kind in new_state) {
- // invert value we have
- new_state[action.kind] = !new_state[action.kind]
- } else {
- // set loading, if key is not know
- new_state[action.kind] = true
- }
- return new_state
-}
diff --git a/components/loginForm.js b/components/loginForm.js
deleted file mode 100644
index dfb7ec0..0000000
--- a/components/loginForm.js
+++ /dev/null
@@ -1,168 +0,0 @@
-import {Promise} from 'es6-promise';
-import {Link} from 'react-router';
-import * as ReactRedux from 'react-redux';
-import * as config from './config';
-import * as React from 'react';
-import * as Redux from 'redux';
-import 'whatwg-fetch';
-import {isEmpty,checkFetchStatus} from './helpers'
-import {Recaptcha} from './captcha.js'
-import {InputField, check} from './input.js'
-import {LOGINF, REGF, REMINDF, formAction, checkFormValueAction, formSetValueAction, formSuccessAction, formState, formValues, checkValue, submitForm} from './loginFormState.js'
-import * as msg from './form.messages.js'
-
-
-const handleSubmit = (props) => (e) => {
- e.preventDefault();
- props.sendForm(props);
- }
-
-const NewPasswordFormHeader = () =>
-
- Введите новый пароль
-
-
-
-const NoLinksFormHeader = (props) =>
-
-const FormHeader = (props) => {
- if (props.pwset) {return NewPasswordFormHeader(props) }
- if (props.nolinks) { return NoLinksFormHeader(props) }
- return (
-
- )
-}
-const captcha = (props) => {
- if (props.display) return (
- )
- return null
-}
-
-const FormItself = (props) =>
-
-
-const Message = (props) => {
- if ( props.message ){
- return (
-
- {msg.success[props.result]}
-
)
- }
- return null
-}
-const LoginForm = React.createClass({
- contextTypes: {
- router: React.PropTypes.object
- },
- componentDidMount: function () {
- if (this.props.loggedin) {
- this.context.router.push('/personal')
- }
- },
- render: function () {
- let props=this.props
- if (props.loading) {
- return
- }
- return (
-
-
- {props.message?
-
- :
- }
-
-
-
-
-
- )
- }
-});
-
-export function setLoginForm() {return formAction(LOGINF, false)}
-export function setRegForm(SecondStage=false) {return formAction(REGF, SecondStage)}
-export function setRemindForm(SecondStage=false) {return formAction(REMINDF, SecondStage)}
-export function setResetForm() {return formAction(REMINDF, true)}
-
-const mapLoginFormDispatchToProps = (dispatch) => {
- return {
- setLogin: () => {dispatch(setLoginForm())},
- setReg: () => {dispatch(setRegForm())},
- setRemind: () => {dispatch(setRemindForm())},
- sendForm: (props) => {dispatch(submitForm(props))},
- formSuccessAction: (kind, secondStage) => {dispatch(formSuccessAction(kind,secondStage))}
- }
-}
-const mapLoginFormStateToProps = (state) => {
- return {
- loggedin: state.user.loggedin || false,
- loading: !(('vcard' in state.loading ) || ('vcard' in state.user)) || ('vcard' in state.loading && state.loading.vcard),
- kind: state.loginForm.formState.kind,
- secondStage: state.loginForm.formState.secondStage,
- actionLabel: state.loginForm.formState.actionLabel,
- sending: state.loginForm.formState.sending,
- result: state.loginForm.formState.result,
- message: state.loginForm.formState.message
- }
-}
-const mapInputPropsToDispatch = (inputType, stateName) => (dispatch) => {
- return {
- checkValue: () => dispatch(checkFormValueAction(stateName || inputType)),
- setValue: (e="") => dispatch(formSetValueAction(stateName || inputType, (typeof(e)==='string'?e:e.target.value)))
- }
-}
-const mapStateToInputProps = (inputType,stateName) => (state) => {
- return {
- ftype: inputType,
- display: state.loginForm.formState[stateName || inputType],
- val: state.loginForm.formValues[stateName || inputType],
- incorrect: (stateName||inputType) in state.loginForm.formValues.incorrect,
- error: state.loginForm.formValues.incorrect[stateName || inputType]
- }
-}
-const LoginField = ReactRedux.connect(mapStateToInputProps('login'), mapInputPropsToDispatch('login'))(InputField);
-const PasswordField = ReactRedux.connect(mapStateToInputProps('password'), mapInputPropsToDispatch('password'))(InputField);
-const PasswordConfirmationField = ReactRedux.connect(mapStateToInputProps('password','confirmation'), mapInputPropsToDispatch('password','confirmation'))(InputField);
-const EmailField = ReactRedux.connect(mapStateToInputProps('email'), mapInputPropsToDispatch('email'))(InputField);
-const Captcha = ReactRedux.connect(mapStateToInputProps('captcha'),mapInputPropsToDispatch('captcha'))(captcha);
-export const loginForm = Redux.combineReducers({ formState, formValues})
-export const LoginFormConnected = ReactRedux.connect(mapLoginFormStateToProps, mapLoginFormDispatchToProps)(LoginForm);
-
-
diff --git a/components/loginFormState.js b/components/loginFormState.js
deleted file mode 100644
index 4d1818e..0000000
--- a/components/loginFormState.js
+++ /dev/null
@@ -1,242 +0,0 @@
-import {InputField, check} from './input.js'
-import {isEmpty,checkFetchStatus} from './helpers'
-import * as config from './config';
-import * as msg from './form.messages.js'
-import {resetCaptcha} from './captcha.js'
-import {browserHistory as history} from 'react-router'
-import {setUserLoggedIn} from './user.js'
-
-let apiUrls = {'login':'auth', 'remind':'reset', 'register':'register'}
-const FORM = 'LFORM'; // login form actions
-
-export const LOGINF = 'login'; // make form to be a login form
-export const REGF = 'register'; // make form to be a registration form
-export const REMINDF = 'remind'; // make form to be a remind password form
-
-const SETFVAL = "SETFV" ; // set form value generic action
-const LOGINV = "LOGINV"; // set login value
-const EMAILV = "EMAILV"; // set email value
-const PASSWV = "PASSWV"; // set password value
-const CAPTCHAV = "CAPTCHAV"; // set recaptcha response
-
-const CHECKFV = "CHECK" ; // check form value action
-const CHKLOGIN = "CLOG" ; // check login value
-const CHKEMAIL = "CEMAIL"; // check email value
-const CHKPASS = "CPASS" ; // check password value
-const CHKCAPT = "CCAP" ; // check captcha value (ensure it is if needed)
-const CHKCNFR = "CCONFR"; // check password confirmation to match pw
-const CHKALL = "CALL" ; // check all fields of a form
-
-const SEND = "S"; // action to send form
-const GOT = "G"; // action to set state when request is done
-const SENDING = "S1"; // form state
-const FALTY = "F"; // form state in case of error
-
-
-export function formAction(actKind, value) {
- return {type:FORM, kind:actKind, value:value}
-}
-export function checkFormValueAction(field) {
- return formAction(CHECKFV, field)
-}
-
-function formActionSending() {
- return formAction(SEND, false)
-}
-export function formSetValueAction(kind, value) {
- return formAction(SETFVAL, {[kind]:value})
-}
-
-// is_msg displays text removing all inputs and setting redirect
-export function formActionResult(data, is_msg=false) {
- return formAction(GOT, {is_msg:is_msg, msg:data})
-}
-
-
-// runs checks for a field by type
-// returns an object to insert into new state
-// i.e. saves values for types other than checked
-function checkValue(type, state) {
- let incorrect = state.incorrect;
- let ckResult = (check[type])(state)
- if (ckResult == "") {
- delete(incorrect[type]);
- } else {
- incorrect[type] = ckResult;
- }
- return {incorrect:incorrect}
-}
-
-export function formValues (state={login:"", password:"", email:"",captcha:"", confirmation:"", incorrect:{}}, action){
- if (action.type != FORM) { return state };
- let newState = Object.assign({}, state);
- switch (action.kind) {
- case SETFVAL:
- let field = Object.keys(action.value)[0];
- Object.assign(newState, action.value);
- // skip cheking until it is forced
- if (! (field in newState.incorrect)) {
- return newState
- };
- Object.assign(newState, checkValue(field, newState));
- return newState;
- case CHECKFV:
- return Object.assign(newState, checkValue(action.value, newState));
- case LOGINF:
- case REGF:
- case REMINDF:
- //reset state of the form for each switch of type
- if (newState.captcha != "" ) {resetCaptcha()}
- return Object.assign(newState, {
- login:"", password:"",
- email:"", captcha:"",
- confirmation:"", incorrect:{}
- })
-
- }
- return state;
-}
-
-export function formState (state={
- login:true, password:true,
- email:false, captcha:true,
- confirmation: false, message: false,
- actionLabel:"Войти",
- kind:LOGINF, sending:false,
- secondStage: false,
- result:""
- }
- ,action){
- //console.log("formstate action:",state,action);
- if (action.type != FORM) {return state};
- let s = action.value ; // this is for action.value === SecondStage
- switch (action.kind) {
- case SEND: return Object.assign({}, state, {sending:!state.sending, result:""})
- case GOT: return Object.assign({}, state, {
- sending: false,
- message: action.value.is_msg,
- result: action.value.msg
- })
- case LOGINF: return Object.assign({}, state, {
- login: true, password: true,
- email: false, captcha: true,
- confirmation: false, message: false,
- actionLabel: "Войти", result: "",
- secondStage: false,
- kind:LOGINF
- })
- case REGF:
- case REMINDF:
- let newstate = {}
- if (action.value) {
- Object.assign(newstate, state, {
- login: false, password: true,
- email: false, captcha: false,
- confirmation: true, message: false,
- actionLabel: "Установить", result: "",
- secondStage: true, kind: action.kind
- })
-
- } else {
- Object.assign(newstate, state, {
- login: true, password: false,
- email: true, captcha: true,
- confirmation: false, message: false,
- actionLabel: "Регистрация", result: "",
- secondStage: false, kind: action.kind
- })
- if (action.kind == REMINDF) {
- Object.assign(newstate,
- {actionLabel: "Выслать пароль",
- email: false})
- }
- }
- return newstate
- default: return state
- }
-}
-
-
-const composeBodyForFetch = (dispatch, getState) => {
- let state=getState();
- let fields = ['login','password','email','captcha'];
- fields.forEach((elem) => {
- if (state.loginForm.formState[elem]) {dispatch(checkFormValueAction(elem))}
- });
-
- // renew state as check funstion should have processed it
- state=getState();
- if (! isEmpty(state.loginForm.formValues.incorrect)) {
- return null;
- } else {
- let body={};
- Object.assign(body,{kind:state.loginForm.formState.kind});
- // for each field get it's value, process and add into body
- fields.forEach((elem) => {
- if (state.loginForm.formState[elem]) {
- let value = state.loginForm.formValues[elem];
- if (elem == 'login') {
- if (value.indexOf('@') == -1) {
- console.log('index of @ is -1, adding domain to ', value);
- value = value.trim() + '@' + config.xmppDomain
- }
- value = value.toLowerCase()
- }
- Object.assign(body,{[elem]:value});
- }
- });
- return body;
- }
-}
-
-export function submitForm (props) {
- return (dispatch,getState) => {
- dispatch(formActionSending())
- let body = composeBodyForFetch( dispatch, getState);
-
- if (body) {
- if (typeof(props.routeParams) !== 'undefined' && typeof(props.routeParams.ehash) !== 'undefined') {
- Object.assign(body,{hash:props.routeParams.ehash})
- }
- fetch(config.apiBase + apiUrls[body.kind], {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- credentials: 'include',
- body: JSON.stringify(body)
- }).then((response) => checkFetchStatus(response,true))
- .then((json) => {
- console.log('json response', json);
- resetCaptcha();
- dispatch(formSetValueAction('captcha', ""));
- if (json['result'] == 'ok'){
- dispatch(formSuccessAction(body.kind, props.secondStage))
- } else {
- dispatch(formActionResult(msg.errors[json['error']] || json['error']))
- }})
- .catch((error) => {console.log(error);dispatch(formActionResult("An error occured. Please, retry"))});
- } else {
- dispatch(formActionSending())
- }
- }
-}
-
-function formSuccessAction (kind, secondStage) {
- switch(kind) {
- case LOGINF: return (dispatch) => {
- dispatch(setUserLoggedIn())
- dispatch(formAction(GOT, {is_msg:true, msg:kind}));
- setTimeout(()=>{history.push('/personal')}, 2000)
- }
- case REGF:
- case REMINDF: return (dispatch) => {
- //
- dispatch(formAction(GOT, {is_msg: true, msg:secondStage?'password':kind}));
- setTimeout(()=>{history.push('/login')}, 2000)
- }
-
- }
-}
-
diff --git a/components/menus.js b/components/menus.js
deleted file mode 100644
index 49d103f..0000000
--- a/components/menus.js
+++ /dev/null
@@ -1,117 +0,0 @@
-import {isEmpty} from './helpers';
-import {Link} from 'react-router';
-import {setVcard, setRoster, setJid} from "./user";
-import {Roster} from "./personal";
-import {itemLoading} from './loading.state';
-import Swipeable from 'react-swipeable';
-import * as React from 'react';
-import * as ReactRedux from 'react-redux';
-
-const LogoutMMItem = (props) => {
- if (props.loggedin) {
- return (
- e.stopPropagation()}>
-
- Выход
-
-
-
- )}
- else return null
-}
-
-class Mainmenu extends React.Component {
- render () {
- let props = this.props
- return (
-
-
-
-
-
- Jabber .ru
-
-
-
-
-
- Главная
- Помощь
- Сервисы
- Скачать
- Кабинет
-
-
-
-
-
-
- Главная
- Помощь
- Сервисы
- Скачать
- Кабинет
- {props.peer_name}
-
-
-
)
- }
-}
-
-const mapMMStateToProps = (state) => {
- let jid = state.MAMHistory.peer
- let peer = state.user.roster.filter((peer) => (peer.jid == jid))[0] || undefined
- let peer_name = peer && peer.name || state.MAMHistory.peer
- return {
- loggedin: state.user.loggedin,
- skipLogo: state.routing.locationBeforeTransitions && state.routing.locationBeforeTransitions.pathname == '/' || false,
- open: state.loading.menu || false,
- peer: state.MAMHistory.peer || undefined,
- peer_name: peer_name
- }
-}
-const mapDispatchToMMProps = (dispatch) => {
- return {
- getVcard: () => dispatch(setVcard(false)),
- getRoster: () => dispatch(setRoster(false)),
- toggleMenu: (onclick, tgt) => (e) => {if ((onclick && e.type == 'click')||!onclick) {e.preventDefault(); e.stopPropagation();dispatch(itemLoading('menu'))}}
- }
-}
-export const ConnectedMainMenu = ReactRedux.connect(mapMMStateToProps, mapDispatchToMMProps)(Mainmenu);
-
-
-export const Footer = (props) =>
-
- {props.children}
-
-
-
-
- Help
- FAQ
-
- {/**/}
-
-
- Сервисы
-
-
-
- {/**/}
-
-
- Клиенты
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/components/metrika.js b/components/metrika.js
deleted file mode 100644
index 5479a23..0000000
--- a/components/metrika.js
+++ /dev/null
@@ -1 +0,0 @@
-export const Yametrika = {__html: '(function(d,w,c){(w[c]=w[c]||[]).push(function(){try{w.yaCounter24966088=new Ya.Metrika({id:24966088,clickmap:true,trackLinks:true,accurateTrackBounce:true,webvisor:true,trackHash:true});}catch(e){ }});var n=d.getElementsByTagName("script")[0],s=d.createElement("script"),f=function(){n.parentNode.insertBefore(s,n);};s.type="text/javascript";s.async=true;s.src="https://mc.yandex.ru/metrika/watch.js";if(w.opera=="[object Opera]"){d.addEventListener("DOMContentLoaded",f,false); }else{f();}})(document, window,"yandex_metrika_callbacks");'}
diff --git a/components/notfound.js b/components/notfound.js
deleted file mode 100644
index f00322f..0000000
--- a/components/notfound.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import {Footer} from "./menus.js";
-import {LoginFormConnected} from "./loginForm.js";
-import {Link} from 'react-router';
-import * as React from 'react';
-import * as ReactRedux from 'react-redux';
-
-export const NotFound = (props) => {
- return
-
404
-
No secret page for you. Only 404 is here :(
-
-}
-
diff --git a/components/personal.js b/components/personal.js
deleted file mode 100644
index b9f88f9..0000000
--- a/components/personal.js
+++ /dev/null
@@ -1,105 +0,0 @@
-import {isEmpty} from "./helpers";
-import {setVcard, setRoster, setJid} from "./user";
-import {browserHistory as history, Link} from 'react-router'
-
-import { Scrollbars } from 'react-custom-scrollbars';
-import * as React from 'react';
-import * as ReactRedux from 'react-redux';
-
-const RosterItem = (props) =>
- {props.name || props.jid}
-
-
-class RRoster extends React.Component {
- render () {
- let props = this.props
- let roster = props && props.roster || []
- let classes = "roster"
- if ((roster.length == 0) && (! props.loggedin)) {classes = classes + " empty"}
- if ((roster.length == 0) && (props.loggedin)) {classes = classes + " nocontacts"}
- return
- {roster.map((item, ind) => ( ))}
-
- }
-}
-RRoster.contextTypes = {
- router: React.PropTypes.object
-}
-
-class PersonalMenu extends React.Component {
- render () { return
- }
-}
-
-export class Personal extends React.Component {
- componentDidMount() {
- if (! this.props.loggedin) {
- this.context.router.push('/login')
- }
- }
- render () {
- if (! this.props.loggedin) return
- return
-
- {this.props.children}
-
- return
-
У нас обед!
-
Мы обязательно сделаем здесь много интересного. Но несколько позже.
- { this.props.user.vcard.PHOTO && this.props.user.vcard.PHOTO.BINVAL != "" ?
-
- : ""
- }
-
- }
-}
-
-Personal.contextTypes = {
- router: React.PropTypes.object
-}
-
-export const settlePersonal = () => (dispatch, getState) => {
- let state = getState()
- if (isEmpty(state.user.roster)) {
- dispatch(setRoster(false))
- }
- if (isEmpty(state.user.vcard)) {
- dispatch(setVcard(true))
- }
-}
-
-const mapStateToPersonalProps = (state) => {
- return {
- loggedin: state.user.loggedin,
- vcard: state.user.vcard,
- roster: state.user.roster,
- user: state.user
- }
-}
-const mapDispatchToPersonalProps = (dispatch) => {
- return {
- getVcard: () => dispatch(setVcard(true)),
- getRoster: () => dispatch(setRoster(false))
- }
-}
-
-const mapStateToRosterProps = (state) => {
- return {
- peer: state.MAMHistory.peer,
- roster: state.user.roster || [],
- loggedin: state.user.loggedin
- }
-}
-
-export const PersonalSpace = ReactRedux.connect(mapStateToPersonalProps, mapDispatchToPersonalProps)(Personal)
-export const Roster = ReactRedux.connect(mapStateToRosterProps)(RRoster)
-
diff --git a/components/root.js b/components/root.js
deleted file mode 100644
index 3445c63..0000000
--- a/components/root.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import {Footer} from "./menus.js";
-import {LoginFormConnected} from "./loginForm.js";
-import {Link} from 'react-router';
-import * as React from 'react';
-import * as ReactRedux from 'react-redux';
-
-export const RootPlain = (props) => {
- return
-
-
-
-
-
-
-
Где я? Это — Jabber.ru, джаббер, мессенджер, чат.
-
С чего начать? Зарегистрироваться (форма ниже), скачать клиент по нраву, написать друзьям. Потом прочесть помощь.
-
А почему не... Если кратко — мы лучше. Подробней — в разделе «помощь».
-
-
-
-
-
Несколько советов
-
Логин (имя пользователя) вы будете сообщать собеседникам. Используйте только латинские буквы и цифры.
-
К имени пользователя автоматом добавят домен — "jabber.ru". Из "elena" получится "elena@jabber.ru".
-
На сайте можно использовать логин без домена. В программах и для собеседников — только с доменом.
-
На странице помощи есть форма обратной связи и ответы на частозадаваемые вопросы.
-
-
- { props.loading ?
-
- : props.loggedin?
- null
- :
-
- }
-
-
-
-
-}
-
-const mapStateToRootProps = (state) => {
- return {
- // if there is no 'vcard' state at all or it is not yet resolved
- loading: !(('vcard' in state.loading ) || ('vcard' in state.user)) || ('vcard' in state.loading && state.loading.vcard),
- loggedin: state.user.loggedin,
- }
-}
-
-
-export const Root = ReactRedux.connect(mapStateToRootProps)(RootPlain)
diff --git a/components/scrollspy.js b/components/scrollspy.js
deleted file mode 100644
index 779dc06..0000000
--- a/components/scrollspy.js
+++ /dev/null
@@ -1,135 +0,0 @@
-const win = window
-const doc = document
-
-
-export class Scrollspy extends React.Component {
-
- static get PropTypes () {
- return {
- items: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
- currentClassName: React.PropTypes.string.isRequired,
- }
- }
-
- static get defaultProps () {
- return {
- items: [],
- currentClassName: '',
- }
- }
-
- constructor (props) {
- super(props)
-
- this.state = {
- targetItems: [],
- inViewState: [],
- }
- }
-
- _initSpyTarget (items) {
- const targetItems = items.map((item) => {
-
- return doc.getElementById(item)
- })
-
- return targetItems
- }
-
- _getElemsViewState (targets) {
- let elemsInView = []
- let elemsOutView = []
- let viewStatusList = []
-
- const targetItems = targets ? targets : this.state.targetItems
-
- let hasInViewAlready = false
-
- for (let i = 0, max = targetItems.length; i < max; i++) {
- let currentContent = targetItems[i]
- let isInView = hasInViewAlready ? false : this._isInView(currentContent)
-
- if (isInView) {
- hasInViewAlready = true
- elemsInView.push(currentContent)
- } else {
- elemsOutView.push(currentContent)
- }
-
- viewStatusList.push(isInView)
- }
-
- return {
- inView: elemsInView,
- outView: elemsOutView,
- viewStatusList,
- }
- }
-
- _isInView (el) {
- const winH = win.innerHeight
- const scrollTop = doc.documentElement.scrollTop || doc.body.scrollTop
- const scrollBottom = scrollTop + winH
- const elTop = el.offsetTop
- const elBottom = elTop + el.offsetHeight
-
- return (elTop < scrollBottom) && (elBottom > scrollTop)
- }
-
- _spy (targets) {
- this.setState({
- inViewState: this._getElemsViewState(targets).viewStatusList,
- })
- }
-
- _handleSpy () {
- let timer
-
- if (timer) {
- clearTimeout(timer)
- timer = null
- }
- timer = setTimeout(this._spy.bind(this), 100)
- }
-
- _initFromProps () {
- const targetItems = this._initSpyTarget(this.props.items)
-
- this.setState({
- targetItems,
- })
-
- this._spy(targetItems)
- }
-
- componentDidMount () {
- this._initFromProps()
- win.addEventListener('scroll', this._handleSpy.bind(this))
- }
-
- componentWillUnmount () {
- win.removeEventListener('scroll', this._handleSpy.bind(this))
- }
-
- componentWillReceiveProps () {
- this._initFromProps()
- }
-
- render () {
- let counter = 0
- const items = React.Children.map(this.props.children, (child, idx) => {
- return React.cloneElement(child, {
- className: (child.props.className ? child.props.className : '') + (this.state.inViewState[idx] ? ' ' + this.props.currentClassName : ''),
- key: counter++,
- })
- })
-
- return (
-
-
-
- )
- }
-}
diff --git a/components/settings.js b/components/settings.js
deleted file mode 100644
index dcc9a25..0000000
--- a/components/settings.js
+++ /dev/null
@@ -1,184 +0,0 @@
-import * as config from './config';
-import {some, checkFetchStatus} from './helpers';
-import {itemLoading} from './loading.state';
-import { Scrollbars } from 'react-custom-scrollbars';
-import * as msg from './form.messages.js'
-
-import * as React from 'react';
-import * as ReactRedux from 'react-redux';
-
-const SETTINGS = 'SETTINGS'; // set setting action
-const FETCH = 'FETCH'; // start fetching from server
-const VALUE = 'values';
-const ERROR = 'errors';
-
-class settings extends React.Component {
- render () {
- let props = this.props
- /*
- if (props.loading) {return (
- )}*/
- return
-
-
-
На этой странице можно включить (или выключить) сохранение своих сообщений. Изменения сделанные на этой странице начинают учитываться сервером не сразу. На применение настроек может потребоваться около часа.
-
По умолчанию, сообщения будут:
-
- {Object.keys(props.historyDefaultOptions).map((cv) => (
- {props.historyDefaultOptions[cv]}
- ))}
-
-
-
-
Переписка с этими контактами будет сохраняться всегда:
-
-
{props.always_err?"Ошибка в списке контактов":null}
-
-
-
Переписка с этими контактами не будет сохраняться никогда:
-
-
{props.never_err?"Ошибка в списке контактов":null}
-
-
- Сохранить настройки
- {props.form_err}
-
-
-
- }
-}
-
-
-
-const mapStateToSettingsProps = (state) => {
- let defaultOptions = {roster:"сохраняться, если собеседник в ростере", always:"сохраняться всегда", never:"не сохраняться никогда"}
- return {
- loading: state.loading.settings || false,
- historyDefaultOptions: defaultOptions,
- historyDefault: state.settings[VALUE].default,
- historyAlwaysList: Array.prototype.join.call(state.settings[VALUE].always,"\n"),
- historyNeverList: Array.prototype.join.call(state.settings[VALUE].never,"\n"),
- always_err: state.settings[ERROR].always,
- never_err: state.settings[ERROR].never,
- form_err: state.settings[ERROR].form
- }
-}
-const mapDispatchToSettingsProps = (dispatch) => ({
- setHistoryDefault : (ev) => {dispatch(setSettingsValue('default', ev.target.value))},
- setHistoryList : (list) => (ev) => { if (ev.target.value.endsWith('\n') || ev.target.classList[0]) {
- dispatch(validateSettingsLists())
- }
- dispatch(setSettingsValue(list, ev.target.value.split('\n')))
- },
- saveSettings : () => {dispatch(saveSettings())},
- validate: ()=> {dispatch(validateSettingsLists())}
- }
-)
-export const Settings = ReactRedux.connect(mapStateToSettingsProps, mapDispatchToSettingsProps)(settings)
-
-export const fetchSettings = () => (dispatch, getState) => {
- let item = 'settings'
- let state = getState()
- if (!state.user.loggedin) {
- console.log('user is not logged in')
- }
- if (state.loading.settings) {
- console.log('Settings are already being fetched')
- }
- dispatch(itemLoading(item))
- fetch(config.apiBase + 'mam/' + item, {
- method: 'GET',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- credentials: 'include',
- }).then((response) => checkFetchStatus(response,true))
- .then((json) => {
- if ('error' in json && json.error != "") {
- console.log('Error getting '+item+', something went wrong')
- console.log(json.error)
- }
-
- dispatch(setSettingsValue('default', json.default))
- if (json.never) dispatch(setSettingsValue('never', json.never))
- if (json.always) dispatch(setSettingsValue('always', json.always))
- dispatch(itemLoading(item))
- }).catch((error)=>{
- console.log('Something went wrong fetching', item,'Message was:', error)
- dispatch(itemLoading(item))
- })
-}
-const saveSettings = () => (dispatch, getState) => {
- dispatch(validateSettingsLists())
- dispatch(setSettingsError('form', false))
- let state = getState()
- if (some(state.settings[ERROR], (el) => !!el)) {
- dispatch(setSettingsError('form', "В списках контактов есть ошибки. Исправьте, пожалуйста."))
- return
- } else {
- dispatch(itemLoading('settings'))
- let body = state.settings[VALUE]
- const filter = (el) => !!el
- body.always = body.always.filter(filter)
- body.never = body.never.filter(filter)
- console.log('settings settings to', JSON.stringify(body))
- fetch(config.apiBase + 'mam/settings', {
- method: 'POST',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- credentials: 'include',
- body: JSON.stringify(body)
- }).then((response) => checkFetchStatus(response,true))
- .then( (json) => {
- if ('error' in json && json.error != "") {
- console.log('Error getting settings: something went wrong')
- console.log(json.error)
- dispatch(setSettingsError('form', msg.error[json.error] || json.error))
- }
- dispatch(setSettingsError('form', "Настройки сохранены."))
- dispatch(itemLoading('settings'))
- }).catch (( error ) => {
- console.log('Could not save settings due to:', error)
- dispatch(setSettingsError('form', "Попробуйте ещё раз позже, пожалуйста"))
- dispatch(itemLoading('settings'))
- })
- }
-}
-const setSettingsError = (err_type, value) => ({type: SETTINGS, kind: ERROR, value: {[err_type]:value}})
-const setSettingsValue = (val_type, value) => ({type:SETTINGS, kind: VALUE, value: {[val_type]:value}})
-
-const validateSettingsLists = () => (dispatch, getState) => {
- let state = getState()
- dispatch(setSettingsError('always', (!validateListOfJIDs(state.settings[VALUE].always))))
- dispatch(setSettingsError('never', (!validateListOfJIDs(state.settings[VALUE].never))))
-}
-const validateListOfJIDs = (list) => list.every((item,ind, ar) => {
- if (item.trim() == '') return true;
- let jid = item.split('@')
- return (jid.length == 2) && jid.every((it) => !! it) // false for empty string
-})
-
-
-export const settingsState = (state={
- [VALUE]:{default:'roster', always:[], never:[]},
- [ERROR]:{always:false, never:false, form:""}
- }, action) => {
- if (action.type != SETTINGS) return state;
-
- return Object.assign({}, state, {[action.kind]:
- Object.assign({}, state[action.kind], action.value)
- })
-}
diff --git a/components/svcs.js b/components/svcs.js
deleted file mode 100644
index 6227bb8..0000000
--- a/components/svcs.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import {Footer} from "./menus.js";
-import {Anchor, Anchored} from "./anchor.js";
-import {Link} from 'react-router';
-import * as React from 'react';
-
-const Placeholder = (props) =>
-
-export const Svcs = Anchored(
- class extends React.Component {
- render () { return (
-
-
-
-
Рейтинг конференций
-
-
-
-
Что это
-
jc.jabber.ru - первая в России система рейтинга и поиска джаббер-конференций, которая собирает и обрабатывает статистику с ведущих джаббер-серверов Рунета, а также предоставляет интерфейс для удобного поиска по ним.
-
Если вы тут первый раз и не поняли о чем шла речь в первом абзаце, то данный абзац как раз для вас. На самом деле все просто! Джаббер-конференция - это тот же чат(chat), т.е. место коллективного общения, если совсем в двух словах. А сам джаббер - это название протокола обмена мгновенными сообщениями, также известный как XMPP, который был принят в сети как стандарт, поэтому сегодня его используют все ведущие ресурсы Рунета для обмена сообщениями.
-
Данный проект создавался для того, чтоб облегчить пользователям выбор места для общения, так называемых чат-комнат(конференций). На нашем сайте Вы можете воспользоваться удобным поиском, посмотреть рейтинг самых посещаемых конференций, найти информацию о программном обеспечении и других сопутствующих вещах.
-
-
Как это работает
-
Наша система производит постоянное сканирование наиболее популярных русскоязычных узлов сети, ведет каталог, поисковый индекс и собирает статистику джаббер-конференций.
-
-
Кто участвует в рейтинге
-
В рейтинге участвуют русскоязычные конференции, все остальные мы стараемся фильтровать. Также "под нож" попадают конференции с множеством ботов, с закрытым доступом, уличенные в рассылке рекламы или накрутке рейтинга.
-
В рейтинге также учитывается и количественный показатель , после чего конференция начинает отображаться в рейтинге - это среднее количество участников за сутки, которое должно быть больше или равно пяти, но тут не стоит забывать, что мы не приветствуем накрутку и большое количество ботов в конференциях(обычно больше трех).
-
Не создавайте себе лишних проблем: если конференция будет забанена, вернуть ее в рейтинг у вас так просто не получится.
-
-
У вас есть сервер конференций и вы хотите участвовать в рейтинге
-
Если конференции вашего сервера посещаемы, то воспользуйтесь формой обратной связи и оставьте нам сообщение. Для участия в рейтинге ваш сервер должен позволять получать информацию с аккаунтов jabber.ru через Discovery.
-
-
-
-
-
-
Архив сообщений
-
-
Что это
-
Общедоступный архив сообщений конференций. В архив попадают только те, кто явно в него попросился. Логичное продолжение "Рейтинга конференций". Каждая фраза сказанная, каждая ссылочка на забавного котика, вомбата или капибару, заботливо сохраняется ежом в недрах его жёстких дисков. И наш архив даёт возможность найти историю сообщений каждой отдельной комнаты, за каждый отдельный день, по порядку написания.
-
Как это работает
-
Для конференций на серверах jabber.ru, история пишется непосредственно сервером сразу в лог. Для конференций, которые находятся вне серверов jabber.ru, история сохраняется специальными программами (ботами), которые слушают сообщения конференции. Как если бы участники строчили по три сообщения: себе, собеседникам и в архив. Только куда более дисциплинированно.
-
Как добавить комнату в архив
-
Так же, как и удалить
-
Как удалить архив сообщений комнаты
-
Так же, как и добавить
-
Что за дурацкий юмор?!
-
Ладно-ладно... Если вы хотите, чтобы логи вашего чата были доступны (или недоступны) в нашем сервисе — напишите в конференцию support@conference.jabber.ru, и мы что-нибудь придумаем. Но почти наверняка мы попросим вас предъявить права владельца конференции и общее согласие по вопросу добавления конференции в архив.
-
-
-
-
-
-
Juick
-
-
Что это
-
Juick.com — старомодная социальная сеть. Вы видели твиттер? Если вы хотите твиттер без толпы гламурных шутников и с доставкой сообщений в jabber, жуйк — ваш выбор. Просто напишите боту juick@juick.com и следуйте его неразборчивым инструкциям
-
Как это работает
-
Вы точно видели твиттер? Вот почти так же. Только основным интерфейсом для juick'а является бот. Вы пишете сообщения боту — бот размещает их на странице сервиса. Другие пользователи, если захотят, могут ответить вам. Просто командный интерфейс бота, описание которого можно увидеть по команде HELP, оставляет множество возможностей ошибиться. Для тех, кто не хочет вести свой блог, есть RSS.
-
Сервис (и бот) поддерживают некоторые расширенные возможности форматирования текст. Подробнее о них можно узнать в помощи самого сервиса.
-
Сейчас juick работает с джаббером и телеграммом. С whatsapp'ом и viber'ом не работает по очевидным причинам: закрытый протокол и несговорчивость в создании гейтов в другие сервисы.
-
Зачем вам вообще это?!
-
Мы пользовались juick'ом, когда это ещё не было модно. И продолжили пользоваться им позже, когда стало уже не модно. Как пользователи, мы дорожим juick'ом и хотели бы, чтобы он был популярнее. Кроме того, juick — отличная возможность писать код, получать отзывы и наблюдать за жизнью небольшого и не очень дружного сообщества. "Just for fun", если в двух словах.
-
А нам это зачем?
-
Juick — хорошее дополнение к мессенджеру. Если вы хотите сохранить какие-либо записки или поделиться со всеми чем-либо полезным, juick — то, что вам нужно. Кроме того, в социальных сетях намного проще получить совет почти на любую тему (если, конечно, вы не боитесь публичного обсуждения ваших проблем).
-
В общем, знакомьтесь, дружите! Зачем ещё нужны социальные сети?
-
-
-
-
-
-
Ваш домен - наш сервер
-
-
Что это
-
"Ваш домен" — это SaaS от Jabber.ru. "Почта для доменов", "виртуальный хостинг" — разные названия одной и той же услуги: SaaS, service as a service, "услуга как услуга", предоставление крупными компаниями своих служб и сервисов под ваши нужны. Серверы Jabber.ru теперь тоже можно использовать со своим доменом. Мы обеспечим вам надёжные услуги передачи сообщений просто потому, что нам нравится заниматься этим (да-да, "потому что мы можем"). Мы не гарантируем вам 100% надёжности или уникальности предоставляемых услуг. Мы просто надёжны и бесплатны. А кроме того — готовы оказать поддержку, которая вам никогда не понадобиться, лично, живыми людьми, а не толстенными справочными страницами.
-
Как это работает
-
Как и любой другой SaaS в этой области: мы просто пересылаем, храним, цензурируем ваши сообщения, охраняем их от полчищ опасных кенгуру и представляем, по возможности, удобные интерфейсы для работы с ними.
-
Что именно умеет jabber.ru
-
Jabber. Мы умеем jabber, мгновенные сообщения. В будущем мы планируем запустить виртуальный почтовый сервис. А также станцию слежения за астероидами в поясе Койпера. Но только после захвата мира.
-
Как начать использовать
-
Чтобы использовать сервера jabber.ru со своим доменом вам потребуется зарегистрировать учётную запись на нашем сервисе и настроить ДНС. После подтверждения учётной записи, вы сможете зарегистрировать в системе перенаправление ваших доменов на сервера jabber.ru. После проверки нашим сервисом ДНС записей - всё готово, вы можете добавлять свои пользователей и начинать общение под собственным доменным именем.
-
-
Дополнительные услуги
-
Если у вас есть идеи связанные с jabber, а наш сервис их, на первый взгляд, не поддерживает, — напишите нам в support@conference.jabber.ru, и мы обязательно что-нибудь придумаем в индивидуальном порядке. Мы любим странные штуки и беспокойных людей.
-
-
-
-
-)}})
diff --git a/components/user.js b/components/user.js
deleted file mode 100644
index 64f12b4..0000000
--- a/components/user.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import * as config from './config.js';
-import {isEmpty,checkFetchStatus} from './helpers';
-import {formActionResult} from './loginFormState';
-import {itemLoading} from './loading.state';
-import {browserHistory as history} from 'react-router';
-import {setLoginForm} from './loginForm.js';
-
-const USER = 'USER'; // all user action
-const VCARD = 'vcard'; //set user's vcard value action
-const ROSTER = 'roster'; //set user's vcard value action
-const JID = 'JID'; // set user jid (after auth or whatever)
-const LOGIN = 'LOGIN'; //sets only 'loggedin' state
-const LOGOUT = 'LOGOUT'; //unset whole state on logout (kill cached data, maybe)
-
-export const userLogout = () => (dispatch, getstat) => {
- dispatch(logout())
- dispatch(setLoginForm())
- console.log('fetching logout')
- fetch(config.apiBase + 'logout', {credentials: 'include'})
- history.push('/')
-}
-
-const getUserItem = (item) => (redirMe) => (dispatch, getState) => {
- let state = getState()
- if (!state.user.loggedin) {
- console.log('Not fetching', item, ': user is not logged in')
- return
- }
- if (!isEmpty(state.user[item]) || state.loading[item]) {
- console.log('User item', item, 'is not empty or is already being fetched')
- return
- }
- console.log('setting '+ item);
- dispatch(itemLoading(item));
- fetch(config.apiBase + item, {
- method: 'GET',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- },
- credentials: 'include',
- }).then((response) => checkFetchStatus(response,true))
- .then((json) => {
- if ('error' in json && json.error != "") {
- console.log('Error getting '+item+', something went wrong')
- console.log(json.error)
- if (redirMe) {
- history.push('/login')
- dispatch(formActionResult('Пожалуйста, авторизуйтесь ещё раз.'))
- }
-
- }
-
- json[item] && dispatch(setUserStateItem(item, json[item]))
- dispatch(itemLoading(item))
- }).catch((error)=>{
- console.log('Something went wront fetching', item,'Message was:', error)
- dispatch(itemLoading(item))
- })
-}
-
-const login = () => ({type:USER, kind:LOGIN})
-const logout = () => ({type:USER, kind:LOGOUT})
-const setUserStateItem = (item, value) => ({type: USER, kind: item, value: value})
-
-export const setRoster = getUserItem('roster')
-export const setVcard = getUserItem('vcard')
-export const setUserLoggedIn = login
-
-
-export const user = (state={roster:[], vcard:{}, jid:null, loggedin: false}, action) =>{
- if (action.type != USER) return state;
- console.log('User action: ', action)
- switch (action.kind) {
- case JID: return Object.assign({}, state, {jid:action.value})
- case VCARD: return Object.assign({}, state, {vcard:action.value.vCard, loggedin: true})
- case ROSTER: return Object.assign({}, state, {roster:action.value})
- case LOGIN: return Object.assign({}, state, {loggedin: true})
- case LOGOUT: return Object.assign({}, state, {jid:null, loggedin: false, vcard: {}, roster:[]})
- default: return state;
- }
- return state;//should never happen...
-
-}
diff --git a/css/_about.scss b/css/_about.scss
deleted file mode 100644
index e69de29..0000000
diff --git a/css/_carousel.scss b/css/_carousel.scss
deleted file mode 100644
index 66f4b14..0000000
--- a/css/_carousel.scss
+++ /dev/null
@@ -1,160 +0,0 @@
-.picon {
- font: $piconfont;
- text-transform: uppercase;
- padding: 7px 0em 4px 1em;
- color: #777777;
- border-bottom: solid;
- border-color: gray;
- cursor: pointer;
-
- &.selected {
- color: #ffffff;
- border-color: #ffffff;
- }
- &.locked {
- border-color: $mred !important;
- border-bottom: solid;
- span:after {
- content: " w";
- visibility: visible;
- }
- }
- span:before {
- content:attr(data-txt);
- display:inline;
- }
- span:after {
- font: 15px WebSymbolsLigaRegular;
- text-transform: none;
- content:" v";
- display: inline;
- visibility: hidden;
- }
- &:hover span:after {
- visibility: visible;
- }
-
-}
-.platform-icons {
- margin-bottom: 45px;
- text-align: center;
-}
-#carousel {
- //padding-top: 30px;
-}
-.main-carousel {
- margin: auto;
- width: 90%;
- min-height: 600px;
-
- &:after {
- content: 'flickity';
- display: none;
- }
-}
-.cellwrap {
- width: 80%;
- min-height: 600px;
- margin-right: 15%;
-}
-
-.carousel-cell {
- //width: 100%;
- //min-height: 600px;
- //margin-right: 15%;
- display: table;
-}
-
-.clientImg {
- display: table-cell;
- text-align: center;
- max-height: 600px;
- div {
- max-width:360px;
- max-height:600px;
- overflow:hidden;
- text-align:right;
- direction:rtl;
- img {
- display:block;
- }
- }
-}
-.description-wrap1{
- display:table-cell;
- vertical-align: top;
- padding-left: 60px;
- &>h2 {
- margin-top: 0;
- }
-}
-
-.client-description {
- @extend .wtxt;
- font: $clientdescfont;
- min-height: 600px;
-}
-
-.clientNameNarrow {
- display: none;
-}
-
-.flickity-page-dots {
- display: none;
-}
-
-
-@media screen and (max-width: ($mdevW)+200) {
- .flickity-prev-next-button {
- display:none;
- }
- .main-carousel {
- width: 100%;
- }
- .cellwrap,
- .carousel-cell {
- margin: 0;
- width: 100%;
- }
-}
-@media screen and (max-width: $mdevW) {
- #carousel {
- padding-top: 0;
- }
- .clientNameWide {
- display: none;
- }
- .clientNameNarrow {
- text-align: center;
- display: block;
- }
-
- .clientImg {
- display:block;
- div {
- width: 100%;
- margin: auto;
- img {
- width: auto;
- height: auto;
- max-width: 100%;
- margin: auto;
- }
- }
- }
- .description-wrap1{
- display:block;
- padding: 15px;
- text-align:justify;
- }
-
- .picon {
- width: 35%;
- display: inline-block;
- }
- .platform-icons {
- margin-bottom: 0;
- }
-
-
-}
diff --git a/css/_conferences.scss b/css/_conferences.scss
deleted file mode 100644
index c083e70..0000000
--- a/css/_conferences.scss
+++ /dev/null
@@ -1,33 +0,0 @@
-.footer > .conferences {
- width: 80%;
-}
-.conference {
- //display: inline-block;
- //width: 25%;
- position: relative;
- min-width: 10em;
- &>a {
- border-bottom: none;
- }
-}
-
-.confico {
- display: inline-block;
- width: 16px;
- height: 16px;
- background-position: center;
- background-size: cover;
- margin-right: .5em;
- vertical-align: bottom;
-}
-.confname {
- display: inline-block;
-}
-
-.conferences + .footer-holder {
- height: 300px;
-}
-.conferences {
- min-height: 350px;
- columns: 4 10em;
-}
diff --git a/css/_fonts.scss b/css/_fonts.scss
deleted file mode 100644
index 1b4c864..0000000
--- a/css/_fonts.scss
+++ /dev/null
@@ -1,62 +0,0 @@
-//@import url(https://fonts.googleapis.com/css?family=Roboto&subset=latin,cyrillic);
-@import url('https://fonts.googleapis.com/css?family=Exo+2:300|Jura:600&subset=cyrillic');
-//@import url('https://fonts.googleapis.com/css?family=Exo+2:400,300,500|Jura:600&subset=cyrillic');
-
-@font-face{
- font-family: 'WebSymbolsLigaRegular';
- src: url('/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.eot');
- src: url('/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.eot?#iefix') format('embedded-opentype'),
- url('/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.woff') format('woff'),
- url('/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.ttf') format('truetype'),
- url('/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.svg#WebSymbolsRegular') format('svg');
-}
-
-@font-face {
- font-family:"bicubik";
- src:url("/fonts/bicubik.woff") format("woff"),url("/fonts/bicubik.ttf") format("truetype");
- font-weight:normal;
- font-style:normal;
-}
-
-/*
-@font-face {
- font-family: "Proxima Nova";
- src: url("/fonts/proximanova-reg-cur.otf");
-}
-
-@font-face {
- font-family:"HelveticaNeueCyrUl";
- src:url("/fonts/HelveticaNeueCyr-UltraLight.woff") format("woff"),url("/fonts/HelveticaNeueCyr-UltraLight.otf") format("opentype");
-}
-
-$helvetica-ff: HelveticaNeueCyrUl,Helvetica Neue,Helvetica,Arial,sans-serif;
-$trebuchet-ff: Trebuchet MS,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Tahoma,sans-serif;
-$proxima-ff: "Proxima Nova","Open Sans",Arial,sans-serif;
-*/
-$mred: #e45539;
-$rred: #ee8866;
-$juraff: Jura,sans-serif;
-$forumff: "Forum",sans-serif;
-$istokff: "Istok Web", sans-serif;
-$robotoff: Roboto,sans-serif;
-$exo2ff: "Exo 2", sans-serif;
-$tenorff: $exo2ff;//"Tenor Sans",sans-serif;
-$footerwff: $tenorff;
-$footmenuitemff: $footerwff;
-$footmenuheadff: $juraff;
-$mmfont: 20px $juraff;
-$piconfont: 18px $juraff;
-$clientdescfont: 18px $tenorff;
-$formheaderfont: 27px $juraff;
-$inputfont: italic 18px $tenorff;
-
-.wtxt {
- color: #ffffff;
-}
-.rtxt {
- color: $mred;
-}
-
-h1,h2 {
- font-family: $juraff;
-}
diff --git a/css/_footnlogog.scss b/css/_footnlogog.scss
deleted file mode 100644
index fa71cd2..0000000
--- a/css/_footnlogog.scss
+++ /dev/null
@@ -1,235 +0,0 @@
-.logo {
- background-image: url('/static/logo.png');
- background-size: contain;
- background-repeat: no-repeat;
- width: 90px;
- height: 90px;
- display: inline-block;
- position:relative;
- bottom: -12px;
-}
-.logotext {
- margin-left: -15px;
- font-family:bicubik,"Lucida Console", Monaco, monospace;
- font-size: 30px;
- font-weight: bold;
- display: inline-block;
- letter-spacing: 7px;
-
-}
-.logorow {
- min-height: 3.5em;
- position:relative;
- margin: auto;
- @media screen and (min-width: $sdevW) {
- padding-bottom: 15px;
- }
-}
-
-
-
-.footer {
- color: #FFFFFF;
- position: absolute;
- bottom:0px;
- width: 100%;
- left: 0;
-}
-
-.footerwrap {
- @extend .wtxt;
- font: 18px $footerwff;
- padding: 10px;
- @media screen and (max-width: $smdevW + 1) {
- padding: 0 10px;
- }
- position:relative;
- margin: auto;
- //min-height: 100%;
- a h1, a h2, h1 a, h2 a{
- &:hover:before {
- content:"o"; //link symbol
- font: 1em WebSymbolsLigaRegular;
- text-transform:none;
- position:absolute;
- left: -1em;
- line-height: 1em;
- }
- }
- h1, h2 {
- @extend .rtxt;
- }
-}
-a {
- text-decoration: none;
-}
- p a:after {
- //content:"\00a0@"; //going out link
- content:"@"; //going out link
- font: 15px WebSymbolsLigaRegular;
- text-transform:none;
- display:inline;
- margin-left: 2px;
- }
-.fblock {
- display: inline-block;
- vertical-align: top;
- padding-right: 60px;
- padding-bottom: 20px;
-}
-
-.footmenu {
- display: inline-block;
- margin-left:10px;
-}
-.footmenuitem {
- font: 15px $footmenuitemff;
- padding-top: 5px;
- a {
- color:inherit;
- text-decoration: none;
- position: relative;
- }
-}
-.footmenuheader {
- font: 18px $footmenuheadff;
-}
-.hg2 {
- width: 730px;
- height: 290px;
- right: 0;
-
- span {
- width: 738px;
- }
-}
-.hg, .hg2{
- position: absolute;
- bottom: 0px;
- overflow: hidden;
- text-align: center;
- display: inline-block;
- z-index: -1;
-}
-.hg, .hg2 {
- span {
- display: block;
- margin-left: -4px;
- }
- img {
- height: 100%;
- width: 100%;
- object-fit: cover;
- }
-}
-
-.footer-holder {
- height: 200px;
-
- @media screen and (max-width: $mdevW)
- and (orientation: landscape) {
-
- height: 200px;
-}
-
-}
-
-@media screen and (min-width: 1400px) {
- .footerwrap {
- width: 80%;
- }
-
- .hg, .hg2 {
- width: 730px;
- height: 290px;
- right: 0;
- span {
- width: 738px;
- }
- }
-}
-@media screen and (min-width: $midDevW + 1) and (max-width: $bigDevW) {
- .footerwrap {
- width: 90%
- }
- .logorow {
- margin-top: 0;
- //width: 90%;
- &.nologo {
- margin-top: -2em;
- }
- }
- .hg, .hg2 {
- width: 584px;
- height: 248px;
- right: 0; //5%
-
- span {
- width: 592px;
- }
- }
-}
-@media screen and (max-width: $midDevW) {
- .hg, .hg2 {
- width: 365px;
- height: 155px;
- right: 0;
-
- span {
- width: 373px;
- }
- }
-}
-/*
-@media screen and (min-width: $mbdevW) and (max-width: $midDevW) {
- .mainmenu {
- position: relative;
- margin-top: 30px;
- }
- .logorow.nologo {
- //margin-top: -2em;
- }
-}*/
-@media screen and (min-width: $mdevW) and (max-width: $mbdevW) {
- &.nologo {
- margin-top: 1em;
- }
-}
-//@media screen and (max-width: $mbdevW) {
-@media screen and (max-width: $midDevW) {
- .logorow {
- text-align: center;
- margin-top: 1em;
- &>a {
- width: 100%;
- text-align: center;
- margin-bottom: 1em;
- }
- }
- .logo{
- text-align:center;
- display:none;
- }
-}
-@media screen and (max-width: $sdevW) {
- .logorow {
- margin-top: 0;
- }
-}
-@media screen and (max-width: $mdevW) {
- .hg2 {display: none;}
- .hg > span > img {
- width:50%;
- height:auto;
- position: absolute;
- bottom: 0;
- right: 0;
- }
- .footer-holder {
- height: 320px;
- }
- .fblock {
- width:50%;
- padding-right:0px;
- }
-}
diff --git a/css/_formsninputs.scss b/css/_formsninputs.scss
deleted file mode 100644
index c4b5611..0000000
--- a/css/_formsninputs.scss
+++ /dev/null
@@ -1,328 +0,0 @@
-@import './css/_loader.scss';
-.form {
- text-align: left;
- color:#FFFFFF;
- position: relative;
-
- &.submit {
- display:block;
- box-sizing: border-box;
- border: solid $mred;
- //height: 33px;
- //width: 135px;
- //padding: 2px;
- }
- &.remind {
- text-align: right;
- position: relative;
- //right:42px;
- //bottom: -25px;
- font-size: 15px;
- a {
- border-bottom: 2px solid $mred;
- color:inherit;
- text-decoration: none;
- padding-bottom: 2px;
- }
- }
- &.register {
- padding: 10px;
- font-size: 18px;
- a {
- border-bottom: 2px solid $mred;
- }
- }
- input {
- font: $inputfont;
- box-sizing: border-box;
- padding: 7px;
- padding-bottom: 11px;
- border: 0;
- border-radius: 0;
- margin-bottom: 25px;
- padding-left: 12px;
- width: 100%;
-
- &.has-error {
- padding: 4px 4px 8px 8px;
- border: 3px solid $mred;
- box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.075) inset;
- animation: hsError 400ms ease-in-out;
- }
- }
- .error {
- //text-transform: uppercase;
- color: white;
- font-size: 15px;
- position: absolute;
- bottom: 5px;
- right: 0px;
- text-align: right;
- }
- &Error {
- margin-top: 10px;
- //position: absolute;
- //top: 0px;
- //right: 5px;
- //text-align: right;
- //max-width: 240px;
- }
-}
-#recaptcha {
- margin: auto;
- max-width: 294px;
- height: 71px;
- overflow: hidden;
- position: relative;
-
- div {
- position: relative;
- left: -3px;
- top: -2px;
- }
-}
-@keyframes rotologo {
- 0% {transform: rotate(10deg); }
- 35% {transform: rotate(-20deg); }
- 100% {transform: rotate(370deg); }
-}
-.recap {
- background: #F9F9F9 url("/static/logo.f.mblue.png") no-repeat center/contain ;
- position: relative;
- margin-bottom: 25px;
- border: #F9F9F9 3px solid;
- border-radius: 0;
- box-sizing: border-box;
- width: 100%;
- display:inline-block;
- overflow: hidden;
- &.has-error {
- border-color: $mred;
- }
- &::before {
- display: block;
- position: absolute;
- width: 80px;
- height: 102%;
- content: " ";
- background: transparent url("/static/logo.n.mblue.png") no-repeat center/contain ;
- transform: rotate(10deg);
- animation-name: rotologo;
- animation-duration: 1s;
- animation-iteration-count: 30;
- right: 0;
- left: 0;
- margin: auto;
-
- }
-}
-
-.cbform {
- position: relative;
- form {
- width: 50%;
- text-align: left;
- @media screen and (max-width: $mdevW) {
- width: 100%;
- }
- }
- .message {
- width: 100%;
- margin: none;
- padding: none;
- }
- textarea {
- padding: 0 10px;
- font: 1em "Exo 2", sans-serif;
- width: 100%;
- min-height: 200px;
- box-sizing:border-box;
- margin-bottom: 25px;
- }
- .recap {
- //width:unset;
- @media screen and (max-width: $mdevW) {
- width: 100%;
- text-aligh: middle;
- }
- }
- .form.submit, .formError {
- position: relative;
- text-align: center;
- top: unset;
- right: unset;
- display:block;
- width:100%;
- margin-left: 0;
- }
- .loader {
- position: relative;
- display: block;
- margin: auto;
- top: -3.5em;
- right: 0;
- }
-}
-
-.buttonRow {
- min-height: 62px;
- position: relative;
-}
-
-
-form {
- text-align: left;
- button {
- background-color: transparent;
- width: 100%;
- font-size: 18px;
- border:0;
- padding:0;
- color: inherit;
- cursor: pointer;
- }
-}
-
-.loginLabel, .regLabel {
- cursor: pointer;
- }
-
-.bodywrap>.loginForm {
- position: fixed;
- top: 120px;
- right:0;
- left:0;
- bottom: 0;
- overflow-y: auto;
- text-align: center;
- background-color: rgba(35,55,71,1);
- //background: transparent;
- .loader {
- position: relative;
- top: -1.5em;
- margin: auto;
- margin-bottom: 3em;
- }
- @media screen and (max-width: $sdevW){
- top: 3.5em;
- }
-
-}
-
-.bodywrap>.loginForm>.scroll {
- position: relative;
- margin: auto;
- height: 100%;
- @media screen and (max-width:$mdevW) {
- min-height: 370px;
- }
- @media screen and (min-width:$mdevW) {
- min-height: 700px;
- }
-}
-.mainlogin>.loginForm .message {
- text-align: center;
-}
-
-.form {
- &.submit {
- }
- &HeaderRow {
- text-align: left;
- margin-bottom: 25px;
- .remLabel {
- display: none;
- }
- &.remind {
- .remLabel {
- display: inline;
- }
- .regLabel, .logLabel {
- display: none;
- }
- }
- }
- &Header {
- text-decoration: none;
- margin-right: 20px;
- letter-spacing: 1px;
- font: $formheaderfont
- }
- &Error {
- // width: 70%;
- }
-}
-
-
-
-.close {
- cursor: pointer;
- position: absolute;
- right: 10px;
- top: 10px;
- width: 24px;
- height: 24px;
- border-radius: 50%;
- border: 1px white solid;
-
- &:hover {
- opacity: 0.8;
- }
- &:before,
- &:after {
- position: absolute;
- left: 11px;
- bottom: 5px;
- content: ' ';
- height: 14px;
- width: 2px;
- background-color: $mred;
- }
- &:before {
- transform: rotate(45deg);
- }
- &:after {
- transform: rotate(-45deg);
- }
-}
-.fwrap {
- box-sizing: border-box;
- color: #ffffff;
- display: block;
- background-color: #435762;
- padding: 35px 40px 20px 40px;
- width: 500px;
- position:absolute;
- right:0px;
- left:0px;
- margin: auto;
- top: 20%;
- @media screen and (max-width: ($mdevW)) {
- padding: 15px 20px 20px 10px;
- width: 95%;
- top: 0;
- position: relative;
- }
- @media screen and (max-width: ($mdevW)-200) {
- width: 100%;
- height: 100%;
- padding: 15px 0px;
- }
-
-}
-@media screen and (max-width: ($mdevW)) {
- a.close {
- display: none;
- }
- .loginForm {
- //padding-top:20px;
- background:none;
-
- .form {
- &HeaderRow {
- text-align: center;
- margin-bottom: 15px;
- }
- }
- }
-}
diff --git a/css/_globals.scss b/css/_globals.scss
deleted file mode 100644
index 1e2779c..0000000
--- a/css/_globals.scss
+++ /dev/null
@@ -1,107 +0,0 @@
-
-$sdevW: 400px;
-$mdevW: 700px;
-$mbdevW: 850px;
-$smdevW: 900px;
-$midDevW: 1100px;
-$bigDevW: 1400px;
-
-$mblue: rgb(35,55,71);
-
-a {
- color:inherit;
-}
-html {
- width:100%;
- //min-height: 100vh;
-}
-
-body {
- box-sizing: border-box;
- background: #233747;
- width:100%;
- margin: 0;
- height: 100%;
- overflow-x:hidden;
- //min-height: 100vh;
-}
-.hidden {
- visibility:hidden;
- opacity: 0 !important;
- transition: opacity 1s ease-in-out;
-}
-.visible {
- transition: opacity 1s ease-in-out;
- display: block !important;
- visibility: visible !important;
- opacity: 1 !important;
-}
-#RC {
- width:100%;
- height: 100%;
- //position: absolute;
- //top: 0;
- //bottom: 0;
- //width: 100%;
-}
-.bodywrap {
- position: relative;
- //min-height: 500px;
- //height: 100%;
- @media screen and (max-width: $mdevW) {
- min-height: 0;
- height: initial;
- }
-
-}
-.gradient-log {
- background: #586875;
- background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSI1MCUiIHN0b3AtY29sb3I9IiM1ODY4NzUiIHN0b3Atb3BhY2l0eT0iMSIvPgogICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjM2Q1MDVlIiBzdG9wLW9wYWNpdHk9IjEiLz4KICA8L2xpbmVhckdyYWRpZW50PgogIDxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9InVybCgjZ3JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+Cjwvc3ZnPg==);
- background: -moz-linear-gradient(left, #586875 50%, #3d505e 100%);
- background: -webkit-gradient(linear, left top, right top, color-stop(50%,#586875), color-stop(100%,#3d505e));
- background: -webkit-linear-gradient(left, #586875 50%,#3d505e 100%);
- background: -o-linear-gradient(left, #586875 50%,#3d505e 100%);
- background: -ms-linear-gradient(left, #586875 50%,#3d505e 100%);
- background: linear-gradient(to right, #586875 50%,#3d505e 100%);
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#586875', endColorstr='#3d505e',GradientType=1 );
-}
-.gradient-bak {
- background: #233747;
- background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSI2MCUiIHN0b3AtY29sb3I9IiMyMzM3NDciIHN0b3Atb3BhY2l0eT0iMSIvPgogICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjNDQ2Mjc3IiBzdG9wLW9wYWNpdHk9IjEiLz4KICA8L2xpbmVhckdyYWRpZW50PgogIDxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9InVybCgjZ3JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+Cjwvc3ZnPg==);
- background: -moz-linear-gradient(left, #233747 60%, #446277 100%);
- background: -webkit-gradient(linear, left top, right top, color-stop(60%,#233747), color-stop(100%,#446277));
- background: -webkit-linear-gradient(left, #233747 60%,#446277 100%);
- background: -o-linear-gradient(left, #233747 60%,#446277 100%);
- background: -ms-linear-gradient(left, #233747 60%,#446277 100%);
- background: linear-gradient(to right, #233747 60%,#446277 100%);
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#233747', endColorstr='#446277',GradientType=1 );
-}
-.gradient {
- background: #233747;
-background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIzMCUiIHN0b3AtY29sb3I9IiMyMzM3NDciIHN0b3Atb3BhY2l0eT0iMSIvPgogICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjNDQ2Mjc3IiBzdG9wLW9wYWNpdHk9IjEiLz4KICA8L2xpbmVhckdyYWRpZW50PgogIDxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9InVybCgjZ3JhZC11Y2dnLWdlbmVyYXRlZCkiIC8+Cjwvc3ZnPg==);
- background: -moz-linear-gradient(left, #233747 30%, #446277 100%);
- background: -webkit-gradient(linear, left top, right top, color-stop(30%,#233747), color-stop(100%,#446277));
- background: -webkit-linear-gradient(left, #233747 60%,#446277 100%);
- background: -o-linear-gradient(left, #233747 30%,#446277 100%);
- background: -ms-linear-gradient(left, #233747 30%,#446277 100%);
- background: linear-gradient(to right, #233747 30%,#446277 100%);
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#233747', endColorstr='#446277',GradientType=1 );
-}
-
-.debug {
- position: fixed;
- bottom:10px;
- left:10px;
- color: white;
-}
-.footerwrap {
- a {
- border-bottom: solid $mred 1px;
- &.rtxt {
- border-color: transparent;
- }
- }
- h1 a, h2 a {
- border-bottom: none;
- }
-}
diff --git a/css/_loader.scss b/css/_loader.scss
deleted file mode 100644
index 5b62eeb..0000000
--- a/css/_loader.scss
+++ /dev/null
@@ -1,46 +0,0 @@
-.loader {
-// position: absolute;
-// right:20%;
-// bottom: 100%;
-}
-.loader:before,
-.loader:after,
-.loader {
- border-radius: 50%;
- width: 2.5em;
- height: 2.5em;
- animation-fill-mode: both;
- animation: load7 1.8s infinite ease-in-out;
-}
-.loader {
- color: #ffffff;
- transform: translateZ(0);
- animation-delay: -0.16s;
-}
-.loader:before {
- left: -3.5em;
- -webkit-animation-delay: -0.32s;
- animation-delay: -0.32s;
-}
-.loader:after {
- left: 3.5em;
-}
-.loader:before,
-.loader:after {
- content: '';
- position: absolute;
- top: 0;
-}
-
-@keyframes load7 {
- 0%,
- 80%,
- 100% {
- box-shadow: 0 2.5em 0 -1.3em;
- }
- 40% {
- box-shadow: 0 2.5em 0 0;
- }
-}
-
-
diff --git a/css/_mainmenu.scss b/css/_mainmenu.scss
deleted file mode 100644
index 87c0f58..0000000
--- a/css/_mainmenu.scss
+++ /dev/null
@@ -1,281 +0,0 @@
-.logorow {
- &>a, .mainmenu {
- display: inline-block;
- box-sizing: border-box;
- }
- @media screen and (min-width: $midDevW) {
- .mainmenu {
- margin-left: 3%;
- }
- }
- &>.logowrap {
- display: inline-block;
- text-align: right;
- @media screen and (max-width:$midDevW) {
- text-align: center;
- }
- width: 50%;
- a {
- outline: none;
- }
- @media screen and (max-width: $sdevW){
- display: none;
- }
- }
- @media screen and (max-width: $sdevW){
- .clicker {
- display: block;
- position: absolute;
- width: 100%;
- height: 100%;
- z-index: 1;
- }
- }
-}
-.logorow .mainmenu {
- @extend .wtxt;
- font: $mmfont;
- letter-spacing: 1px;
- &>.root, &>.p-menu-ico {
- display: none;
- }
- .mmitem .logout .ico, .mmitem.peer {
- display: none;
- }
-
- @media screen and (max-width: $midDevW) and (min-width: $mbdevW){
- //margin-top: 30px;
- display: block;
- padding: 0;
- }
- @media screen and (max-width: $sdevW){
- width: 100%;
- text-align: center;
- margin: 3px 0;
- .mmitem {
- display: inline-block;
- position: relative;
- margin: auto;
- margin-top: 12px;
- font-size: 30px;
- &.peer {
- display: inline-block;
- text-transform: none;
- overflow: hidden;
- max-width: 62%;
- &>a {
- text-transform: none;
- }
- }
- &>a {
- display: none;
- &.selected, &.logout {
- display: inline;
- }
- }
- &.logout {
- min-width: 1em;
- display: inline-block;
- float: right;
- margin: 10px;
- margin-bottom: 0;
- z-index: 2;
- .text {
- display: none;
- }
- .ico {
- display: inline;
- z-index: 2;
- }
- }
- }
- &>.p-menu-ico {
- display: inline;
- float: left;
- width: 1em;
- margin: 0.5em;
- }
- }
-}
-
-
-
-.mmitem {
- &:last-child {
- margin-right: 0;
- }
- margin-right: 2em;
- position: relative;
- a {
- &.selected {
- @extend .rtxt
- }
- color:inherit;
- text-decoration: none;
- position: relative;
- outline: none;
- text-transform: uppercase;
- display:inline-block;
-
- &:after {
- display:block;
- padding: 5px 5px 0px 5px;
- position:absolute;
- width:100%;
- margin-left: -5px;
- content: '';
- border-bottom: solid 3px $mred;
- transform: scaleX(0.0);
- transition: transform 250ms ease-in-out;
-
- }
- &.logout:after {
- display: none;
- }
- &:hover:after {
- transform: scaleX(1.0);
- }
- }
- &.logout .ico::after{
- font: 30px WebSymbolsLigaRegular;
- content: "`"; // logout
- }
-}
-
-@media screen and (min-width: $midDevW + 1) and (max-width: $bigDevW) {
- .mmitem {
- margin-right: 1em;
- }
-}
-
-@media screen and (max-width: $midDevW){
- .mmitem{
- margin-top: 1em;
- letter-spacing: 0;
- margin-right: 0.6em;
- }
-}
-@media screen and (max-width: $mdevW){
- .mainmenu {
- //margin-top: 10px;
- }
-
- .mmitem{
- &:last-child {
- margin-right: 1em;
- }
- a {
- margin-bottom: 5px;
- &:after {
- padding: 0;
- margin-left: 0;
- }
- }
- }
-
-}
-@media screen and (max-width: $sdevW){
- .mmitem{
- font-size: 16px;
- &:last-child {
- margin-right: 0em;
- }
- }
-
-}
-
-.clicker {
- display: none;
-}
-.p-menu-ico {
- display: block;
- height: 3px;
- background-color: white;
- box-shadow: 0 9px white, 0 18px white;
- &::before {
- display: block;
- content: " ";
- position: absolute;
- top: -0.3em;
- bottom: -.92em;
- left: -0.3em;
- right: -0.3em;
- //border: solid white 2px;
- border-radius: 3px;
- }
-}
-
-.scrollout-container {
- display: none;
- position: fixed;
- overflow: hidden;
- height: 100%;
- width: 100%;
- z-index: -1;
- @media screen and (max-width: $sdevW){
- display: block;
- transition: z-index 0.5s step-end;
- .scrollout {
- margin-left: -100%;
- transition: margin 0.4s ease-out;
- }
- &.openned {
- z-index: 10;
- transition: z-index 0.5s step-start;
- background: rgba($mblue, 0.6);
- .scrollout {
- margin-left: 0;
- }
- }
- }
-}
-.scrollout {
- display: block;
- overflow: auto;
- position: absolute;
- background: $mblue;
- height: 100%;
- width: 80%;
- .mmitem {
- @extend .wtxt;
- font: $mmfont;
- display: block;
- margin: 10px auto;
- width: 90%;
- font-size: 30px;
- text-align: left;
- border-bottom: 1px rgba($mred, 0.8) solid;
-
- a::after {
- display: none;
- }
- &::before {
- content: "\0d7";
- display: inline-block;
- font: 30px WebSymbolsLigaRegular;
- }
- }
- .roster {
- margin-top: 1em;
- text-align: center;
- &.empty, &.nocontacts {
- color: gray;
- &::before {
- display:block;
- content: "l";//"cloudup";
- font: 3em WebSymbolsLigaRegular;
- }
- &::after {
- display:block;
- font: 1.5em "Exo 2", sans-serif;
- }
- }
- &.empty::after {
- content: "Войдите, чтобы увидеть список собеседников.";
- }
- &.nocontacts::after {
- content: "Расскажите друзьям про jabber.ru и добавьте их в свой список контактов!";
- }
- }
-}
-
diff --git a/css/_notfound.scss b/css/_notfound.scss
deleted file mode 100644
index 30b0d18..0000000
--- a/css/_notfound.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-.notFoundNumber {
- color: $mred;
- width: 100%;
- font-family: bicubik,"Lucida Console", Monaco, monospace;
- font-size: 5em;
- font-weight: bold;
-
-}
-.notFoundText {
- @extend .wtxt;
- font: $mmfont;
-}
diff --git a/css/_personal.scss b/css/_personal.scss
deleted file mode 100644
index 203cd14..0000000
--- a/css/_personal.scss
+++ /dev/null
@@ -1,293 +0,0 @@
-.personal {
- box-sizing: border-box;
- color: white;
- bottom: 0;
- margin: 0 auto;
- color: white;
- position: fixed;
- left: 0;
- right: 0;
- bottom: 0;
- top: 150px;
- max-width: 80%;
- @media screen and (max-width: $midDevW) {
- max-width: 90%;
- //width: 90%;
- }
- @media screen and (max-width: $mbdevW) {
- max-width: 100%;
- }
- &>.p-menu {
- width: 20%;
- height: 100%;
- }
- &>.roster-history {
- width: 80%;
- max-width: 1024px;
- height: 100%;
- box-sizing: border-box;
- }
- @media screen and (max-width: $sdevW) {
- top: 3.5em;
- //position: relative;
- .r-settings {
- margin: 1px;
- left: 0;
-
- }
- &>.p-menu {
- display: none;
- }
- &>.roster-history {
- width: 100%;
- }
- }
-
-}
-
-.p-menu, .roster-history {
- font-family: Jura, sans-serif;
- color: white;
- display: inline-block;
- position: relative;
-}
-
-.p-settings {
- text-align: right;
- display: block;
- position: relative;
- line-height: 2em;
- box-sizing: border-box;
- &:after {
- content: "^";//"settings";
- font: 1em WebSymbolsLigaRegular;
- display:inline-block;
- margin: 0 0 0 0.5em;
- }
- &.selected:after {
- color: $mred;
- }
- &:before {
- position: absolute;
- display: block;
- content: " ";
- bottom: 0px;
- height: 1px;
- width: 100%;
- background: $mred;
- background: linear-gradient(to right, rgba($mred,0), $mred);
- }
- &.selected:before, &:hover:before {
- background: $mred;
- }
- &.selected, &:active, &:hover {
- outline: none;
- }
-}
-.roster {
- color: white;
- a.selected, a:active, a:hover {
- outline: none;
- }
-}
-.r-item {
- display: block;
- text-align: right;
- padding: 0.5em 0;
- font-size: 1.5em;
- box-sizing: border-box;
- padding: 0.5em 20px 0.5em 0;
- width: 100%;
- position: relative;
- overflow-wrap: break-word;
- outline: none;
-
- &:first-of-type:after, & + &:after {
- display: block;
- position: absolute;
- left: 0;
- right: 0px;
- bottom: 0;
- height: 1px;
- content: " ";
- background: $mred;
- background: linear-gradient(to right, rgba($mred,0), $mred);
- }
- &:last-of-type:after {
- background: $mred;
- background: linear-gradient(to right, rgba($mred,0), $mred);
- }
- &.selected:after, &:hover:after {
- background: $mred;
- }
-}
-
-.r-settings {
- position: absolute;
- left: 22%;
- right: 0;
- margin-left: 40px;
- * {
- box-sizing: border-box;
- }
- display: inline-block;
- height: 100%;
- text-align: left;
- font: 1.5em "Exo 2", sans-serif;
- color: white;
- &>div {
- margin-bottom: 15px;
- }
- .settings-default, .settings-always, .settings-never, .settings-save {
- position: relative;
- padding: 10px;
- }
- .settings-default>p:nth-child(1) {
- width: 90%;
- margin-top: -0.5em;
- text-align: justify;
- @media screen and (max-width: $sdevW) {
- width: 100%
- }
- }
- .settings-save {
- padding: 10px;
- }
- select {
- color: white;//rgba(0,0,0,0);
- //text-shadow: 0 0 0 white;
- font-size: 0.8em;
- line-height: 2em;
- height: 3em;
- }
- textarea {
- color: white;
- min-height: 10em;
- font-size: 1em;
- }
- select, textarea {
- display: block;
- background: $mblue;
- border: 2px $mred solid;
- margin: 20px;
- padding: 0 20px;
- width: 90%;
- @media screen and (max-width: $sdevW) {
- margin: 10px auto;
- padding: 0 10px;
- width: 100%;
- }
- }
- textarea + .errortext {
- color: $rred;
- bottom: 0;
- right: 7%;
- right: calc(5% + 20px);
-
- }
- button {
- color: white;
- background: $mblue;
- border: 2px $mred solid;
- font: 1em "Exo 2", sans-serif;
- min-width: 20%;
- padding: 15px;
- margin: 30px;
- }
- .errortext {
- position: absolute;
- text-align: right;
- width: 90%;
- margin: auto;
- @media screen and (max-width: $mbdevW) {
- position: static;
- }
- }
- .settings-save > .errortext {
- position: static;
- }
-}
-
-.roster-history {
- .no-history {
- text-align: center;
- width: 80%;
- font: 2.5em "Exo 2", sans-serif;
- margin: auto;
- color: gray;
- @media screen and (max-width: $sdevW) {
- width: 100%;
- }
- }
- .no-history:before {
- content: "l";//"cloudup";
- font: 3em WebSymbolsLigaRegular;
- display:block;
- }
-
- .message {
- margin: 5px 10px;
- padding: 10px;
- max-width: 90%;
- @media screen and (max-width: $mbdevW) {
- margin: 5px 10px;
- padding: 0;
- max-width: 100%;
- }
- .message-author {
- color: #a6a6a6;
- display: inline;
- text-transform: capitalize;
- }
- &.incoming {
- .me {
- display: none;
- }
-
- }
- &.outgoing {
- .peer {
- display:none;
- }
- }
- .message-text {
- hyphens: auto;
- overflow-wrap: break-word;
- font: 1.3em "Exo 2", sans-serif;
- @media screen and (max-width: $mbdevW) {
- font-size: 1em;
- }
- }
- .message-timestamp {
- display: inline;
- color: grey;
- margin-left: 0.5em;
- .ts-date, .ts-time {
- display: inline;
- }
- .ts-date {
- display: none;
- }
- }
- }
-}
-.message.incoming + .message.outgoing, .message.outgoing + &.message.incoming {
- margin: 12px 10px 0 10px;
-}
-.message.incoming + .message.incoming, .message.outgoing + .message.outgoing {
- margin: 0 10px;
- padding-top: 0;
- .message-author, .message-timestamp {
- display:none
- }
-}
-.roster-history-placeholder {
- font: 3em WebSymbolsLigaRegular;
- text-align: center;
- color: gray;
-}
-.start-placeholder {
- font: 3em WebSymbolsLigaRegular;
- text-align: center;
- color: gray;
-}
diff --git a/css/_root.scss b/css/_root.scss
deleted file mode 100644
index 0cdd75e..0000000
--- a/css/_root.scss
+++ /dev/null
@@ -1,143 +0,0 @@
-.nologo {
- padding-top:0;
- .logo {
- @media screen and (min-width: $mdevW) {
- background: transparent;
- }
- }
-}
-.mainhelp, .mainlogin {
- display: inline-block;
- position: relative;
- box-sizing: border-box;
- width: 50%;
- padding: 3%;
- padding-bottom: 10%;
- vertical-align: top;
- text-align: right;
- h1 {
- margin-top: 0;
- }
- .formHeader {
- font-size: 36px;
- @media screen and (max-width: $mdevW ) {
- font-size: 30px;
- }
- }
- .form.submit {
- margin: 0;
- }
- .form.remind {
- position: relative;
- right: 0;
- bottom: 0;
- }
- .loader {
- position: relative;
- display: block;
- margin: auto;
- }
- @media screen and (max-width: $mbdevW) {
- text-align: justify;
- }
- @media screen and (max-width: $smdevW+1) {
- padding: 0 1%;
- }
- @media screen and (max-width: $bigDevW) {
- padding-top: 0;
- }
- @media screen and (max-width: $mdevW + 1) {
- width: 100%;
- }
-}
-
-.mainhelp {
- p {
- text-indent: 2em;
- }
-}
-
-.mainlogin {
- min-height: 550px;
- border-left: solid 1px;
- border-color: $mred;
- .loginForm, .gradient-log{
- background:transparent;
- position: relative;
- width: unset;
- padding: unset;
- overflow: unset;
- top: unset;
- .hg2 {display:none}
- a.close {display:none}
- }
-
- .success {
- margin-bottom: -40px;
- }
- @media screen and (min-width: 550px ) and (max-width: $mdevW) {
- .loginForm {
- max-width: 80%;
- margin: auto;
- }
-
- }
- @media screen and (max-width: $mdevW) {
- border: none;
- padding: 0;
- padding-bottom: 10%;
- }
-}
-.mainreg {
- width: 90%;
- margin: auto;
- @media screen and (max-width: $bigDevW) {
- width: 100%
- }
- @media screen and (max-width: $mdevW ) {
- .mainhelp {
- display:none;
- }
- }
-}
-.dialog {
- width: 80%;
- @media screen and (max-width: $bigDevW) {
- width: 100%
- }
- margin: auto;
- display: table;
- h1 {
- margin: 0.5em 0;
- }
- &:after {
- content: "";
- display: table;
- clear: both;
- }
- .hello, .phrases {
- h1:nth-child(1) {
- margin-top: 0;
- }
- width: 50%;
- box-sizing: border-box;
- display: table-cell;
- padding: 0 3%;
- @media screen and (max-width: $smdevW + 1) {
- padding: 0 1%;
- }
- @media screen and (max-width: $mdevW + 1) {
- width: 100%;
- }
- }
- .hello {
- background-image: url('/static/logo.png');
- background-size: contain;
- background-repeat: no-repeat;
- background-position: 87% 50%;
- height: 100%;
- @media screen and (max-width: $mdevW + 1) {
- display:none;
- }
- }
-}
diff --git a/css/_svcs.scss b/css/_svcs.scss
deleted file mode 100644
index 2442226..0000000
--- a/css/_svcs.scss
+++ /dev/null
@@ -1,56 +0,0 @@
-.svcimg {
- margin: 25px;
-}
-.footerwrap .svc:nth-child(odd) .svcimg {
- float:left;
-}
-.footerwrap .svc:nth-child(even) .svcimg {
- float:right;
- transform: scaleX(-1);
-}
-.svc:nth-child(n+2):before {
- display: block;
- width: 90%;
- content:" ";
- border: 1px solid $mred;
- margin: auto;
-}
-.svc:last-of-type p:last-child {
- width: 70%;
-}
-.svcs h2 {
- margin-bottom: -0.6em;
-}
-.svc:first-child h1 {
- margin-top: 0;
-}
-.svc.planning {
- color: gray;
- h2 {
- color: darkgrey;
- }
- h1>.anchor {
- display: inline-block;
- }
- h1:after {
- content: " — в процессе запуска";
- color: darkgrey;
- }
-}
-
-@media screen and (max-width: $mdevW) {
-
- .svcimg {
- height: auto;
- width: 100%;
- margin: auto;
- text-align: center;
- img {
- height: auto;
- width: auto;
- max-width: 250px;
- max-height: 350px;
-
- }
- }
-}
diff --git a/index.js b/index.js
deleted file mode 100644
index 6222a99..0000000
--- a/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import {App, store} from "./components/application.js"
-import {syncHistoryWithStore} from 'react-router-redux'
-import {browserHistory} from 'react-router'
-import * as ReactDOM from 'react-dom';
-import * as React from 'react';
-
-const history = syncHistoryWithStore(browserHistory, store);
-
-const SApp = App(history);
-
-ReactDOM.render(
-
-, document.getElementById('RC'))
diff --git a/main.js b/main.js
deleted file mode 100644
index 076d57c..0000000
--- a/main.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import ReactDomServer from 'react-dom/server';
-import {store,Routes} from './components/application.js';
-import { syncHistoryWithStore } from 'react-router-redux'
-//import createMemoryHistory from 'history';
-import createMemoryHistory from 'history/lib/createMemoryHistory'
-import {Yametrika} from './components/metrika.js'
-
-//import {createLocation} from 'history/LocationUtils'
-const SSI = {__html:
- '{}' +
- 'var vcard = ;' +
- 'var roster = ;'
-}
-var HTMLWrap = (props) =>
-
-
- Jabber.ru
-
-
-
-
-
-
-
-
-
-
- {props.children}
-
-
-
-
-
-
-
-
-
-
-const match = ReactRouter.match;
-const RouterContext = ReactRouter.RouterContext;
-const Provider = ReactRedux.Provider;
-
-
-
-const History = syncHistoryWithStore(createMemoryHistory(),store);
-const Rs = ReactRouter.createRoutes(Routes);
-
-module.exports = function render(locals, callback) {
- const loc = History.createLocation(locals.path);
- match( {routes:Rs, location:loc}, (error, redirectLocation, renderProps) => {
- callback(null, ''+ReactDomServer.renderToStaticMarkup(
-
-
-
-
- ),
- )
- })
-}
diff --git a/package.json b/package.json
deleted file mode 100644
index 173458a..0000000
--- a/package.json
+++ /dev/null
@@ -1,60 +0,0 @@
-{
- "name": "jru.web",
- "version": "0.1.0",
- "description": "a website attempt",
- "main": "index.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "oxpa",
- "license": "MIT",
- "dependencies": {
- "flickity": "https://github.com/oxpa/flickity.git",
- "history": "^1.17.0",
- "js-base64": "^2.1.9",
- "react": "^15.1.0",
- "react-custom-scrollbars": "https://github.com/oxpa/react-custom-scrollbars.git#jru",
- "react-dom": "^15.1.0",
- "react-lite": "^0.15.33",
- "react-redux": "^4.4.5",
- "react-router": "^2.4.1",
- "react-router-redux": "^4.0.8",
- "react-swipeable": "^3.9.2",
- "redux": "^3.5.2",
- "whatwg-fetch": "^1.0.0"
- },
- "devDependencies": {
- "babel-cli": "^6.9.0",
- "babel-loader": "^6.4.1",
- "babel-plugin-transform-object-assign": "^6.22.0",
- "babel-plugin-transform-object-rest-spread": "^6.23.0",
- "babel-plugin-transform-react-jsx": "^6.24.1",
- "babel-plugin-transform-runtime": "^6.9.0",
- "babel-polyfill": "^6.9.1",
- "babel-preset-es2015": "^6.9.0",
- "babel-preset-es2015-native-modules": "^6.6.0",
- "babel-preset-es2015-webpack": "^6.4.3",
- "babel-preset-react": "^6.5.0",
- "babel-preset-stage-2": "^6.5.0",
- "babelify": "^7.3.0",
- "browserify": "^13.0.1",
- "css-loader": "^0.23.1",
- "es6-promise": "^4.1.0",
- "extract-text-webpack-plugin": "^1.0.1",
- "file-loader": "^0.9.0",
- "node-sass": "^3.8.0",
- "postcss-loader": "^0.9.1",
- "react": "^15.1.0",
- "react-router": "^2.4.1",
- "react-router-redux": "^4.0.4",
- "redux": "^3.5.2",
- "redux-thunk": "^2.1.0",
- "sass-loader": "^4.0.0",
- "script-loader": "^0.7.0",
- "static-site-generator-webpack-plugin": "https://github.com/oxpa/static-site-generator-webpack-plugin.git",
- "strip-loader": "^0.1.2",
- "style-loader": "^0.13.1",
- "url-loader": "^0.5.7",
- "webpack": "^1.13.1"
- }
-}
diff --git a/static/css/flickity.css b/static/css/flickity.css
deleted file mode 100644
index bb62af2..0000000
--- a/static/css/flickity.css
+++ /dev/null
@@ -1,141 +0,0 @@
-/*! Flickity v1.2.1
-http://flickity.metafizzy.co
----------------------------------------------- */
-
-.flickity-enabled {
- position: relative;
-}
-
-.flickity-enabled:focus { outline: none; }
-
-.flickity-viewport {
- overflow: hidden;
- position: relative;
- height: 100%;
-}
-
-.flickity-slider {
- position: absolute;
- width: 100%;
- height: 100%;
-}
-
-/* draggable */
-
-.flickity-enabled.is-draggable {
- -webkit-tap-highlight-color: transparent;
- tap-highlight-color: transparent;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-
-.flickity-enabled.is-draggable .flickity-viewport {
- cursor: move;
- cursor: -webkit-grab;
- cursor: grab;
-}
-
-.flickity-enabled.is-draggable .flickity-viewport.is-pointer-down {
- cursor: -webkit-grabbing;
- cursor: grabbing;
-}
-
-/* ---- previous/next buttons ---- */
-
-.flickity-prev-next-button {
- position: absolute;
- top: 50%;
- width: 44px;
- height: 44px;
- border: none;
- border-radius: 50%;
- background: white;
- background: hsla(0, 0%, 100%, 0.75);
- cursor: pointer;
- /* vertically center */
- -webkit-transform: translateY(-50%);
- -ms-transform: translateY(-50%);
- transform: translateY(-50%);
-}
-
-.flickity-prev-next-button:hover { background: white; }
-
-.flickity-prev-next-button:focus {
- outline: none;
- box-shadow: 0 0 0 5px #09F;
-}
-
-.flickity-prev-next-button:active {
- filter: alpha(opacity=60); /* IE8 */
- opacity: 0.6;
-}
-
-.flickity-prev-next-button.previous { left: 10px; }
-.flickity-prev-next-button.next { right: 10px; }
-/* right to left */
-.flickity-rtl .flickity-prev-next-button.previous {
- left: auto;
- right: 10px;
-}
-.flickity-rtl .flickity-prev-next-button.next {
- right: auto;
- left: 10px;
-}
-
-.flickity-prev-next-button:disabled {
- filter: alpha(opacity=30); /* IE8 */
- opacity: 0.3;
- cursor: auto;
-}
-
-.flickity-prev-next-button svg {
- position: absolute;
- left: 20%;
- top: 20%;
- width: 60%;
- height: 60%;
-}
-
-.flickity-prev-next-button .arrow {
- fill: #333;
-}
-
-/* color & size if no SVG - IE8 and Android 2.3 */
-.flickity-prev-next-button.no-svg {
- color: #333;
- font-size: 26px;
-}
-
-/* ---- page dots ---- */
-
-.flickity-page-dots {
- position: absolute;
- width: 100%;
- bottom: -25px;
- padding: 0;
- margin: 0;
- list-style: none;
- text-align: center;
- line-height: 1;
-}
-
-.flickity-rtl .flickity-page-dots { direction: rtl; }
-
-.flickity-page-dots .dot {
- display: inline-block;
- width: 10px;
- height: 10px;
- margin: 0 8px;
- background: #333;
- border-radius: 50%;
- filter: alpha(opacity=25); /* IE8 */
- opacity: 0.25;
- cursor: pointer;
-}
-
-.flickity-page-dots .dot.is-selected {
- filter: alpha(opacity=100); /* IE8 */
- opacity: 1;
-}
diff --git a/static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.eot b/static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.eot
deleted file mode 100644
index fabf25c73ec64c5397b7f6a3c84489a75905cad6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 16137
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
DV$!{a
diff --git a/static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.svg b/static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.svg
deleted file mode 100644
index c8b9261..0000000
--- a/static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.svg
+++ /dev/null
@@ -1,182 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.ttf b/static/fonts/WebSymbolsLiga-Regular/websymbolsligaregular.ttf
deleted file mode 100644
index 03fa52f0af0f82e064fce2e9dd6293a9cc33edb0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 38212
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 B0O(NwfP%H;yClNV!R4n<
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