diff --git a/.editorconfig b/.editorconfig
new file mode 100755
index 0000000..d4eed84
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = false
+indent_style = space
+indent_size = 2
diff --git a/.gitattributes b/.gitattributes
new file mode 100755
index 0000000..a29125a
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,106 @@
+# From https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes
+
+# Handle line endings automatically for files detected as text
+# and leave all files detected as binary untouched.
+* text=auto
+
+#
+# The above will handle all files NOT found below
+#
+
+#
+## These files are text and should be normalized (Convert crlf => lf)
+#
+
+# source code
+*.php text
+*.css text
+*.sass text
+*.scss text
+*.less text
+*.styl text
+*.js text eol=lf
+*.coffee text
+*.json text
+*.htm text
+*.html text
+*.xml text
+*.svg text
+*.txt text
+*.ini text
+*.inc text
+*.pl text
+*.rb text
+*.py text
+*.scm text
+*.sql text
+*.sh text
+*.bat text
+
+# templates
+*.ejs text
+*.hbt text
+*.jade text
+*.haml text
+*.hbs text
+*.dot text
+*.tmpl text
+*.phtml text
+
+# server config
+.htaccess text
+
+# git config
+.gitattributes text
+.gitignore text
+.gitconfig text
+
+# code analysis config
+.jshintrc text
+.jscsrc text
+.jshintignore text
+.csslintrc text
+
+# misc config
+*.yaml text
+*.yml text
+.editorconfig text
+
+# build config
+*.npmignore text
+*.bowerrc text
+
+# Heroku
+Procfile text
+.slugignore text
+
+# Documentation
+*.md text
+LICENSE text
+AUTHORS text
+
+
+#
+## These files are binary and should be left untouched
+#
+
+# (binary is a macro for -text -diff)
+*.png binary
+*.jpg binary
+*.jpeg binary
+*.gif binary
+*.ico binary
+*.mov binary
+*.mp4 binary
+*.mp3 binary
+*.flv binary
+*.fla binary
+*.swf binary
+*.gz binary
+*.zip binary
+*.7z binary
+*.ttf binary
+*.eot binary
+*.woff binary
+*.pyc binary
+*.pdf binary
diff --git a/.gitignore b/.gitignore
new file mode 100755
index 0000000..0012879
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+# Don't check auto-generated stuff into git
+coverage
+build
+node_modules
+stats.json
+releases
+
+# Cruft
+.DS_Store
+npm-debug.log
+.idea
diff --git a/README.md b/README.md
index 025b8dc..f1eafd7 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,43 @@
-# live.hyperfox.org
+# [live.hyperfox.org](https://github.com/xiam/hyperfox-live)
+> This is the source code of the [Hyperfox][1]'s [live visualization tool][2].
-This is the source code of the [Hyperfox][1]'s [live visualization tool][2].
## Requisites
-* ruby
-* rubygems
-* nodejs (I know, I know...)
-* npm
+* Node.js (>=6)
+* npm (>=3)
## Initial setup
```
-gem install compass
-
-cd html
npm install
-
-sudo npm install -g bower
-sudo npm install -g grunt-cli
-
-bower update
-npm install grunt-contrib-compass --save-dev
-
```
## Development
+```bash
+# start a webpack server for development
+# at http://localhost:3000
+npm run start:development
```
-cd html
-grunt serve
+
+## Build
+See [electron-packager](https://github.com/electron-userland/electron-packager) for electron package requirements.
+
+```bash
+# create the webpack build in the `build` folder
+npm run build
+# create an electron package for all the platforms
+npm run package
+# create an electron package for osx
+npm run package:osx
+# create an electron package for windows
+npm run package:win
+# create an electron package for linux
+npm run package:linux
```
+
# License (MIT)
> Copyright (c) 2014-2015 José Carlos Nieto, https://menteslibres.net/xiam
diff --git a/app/app.js b/app/app.js
new file mode 100755
index 0000000..ed45b44
--- /dev/null
+++ b/app/app.js
@@ -0,0 +1,101 @@
+/**
+app.js
+This is the entry file for the application, only setup and boilerplate code.
+**/
+
+import 'babel-polyfill'
+
+// Load the manifest.json file and the .htaccess file
+import 'file?name=[name].[ext]!./manifest.json'
+
+// Import all the third party stuff
+import React from 'react'
+import ReactDOM from 'react-dom'
+import { Provider } from 'react-redux'
+import { applyRouterMiddleware, Router, browserHistory, hashHistory } from 'react-router'
+import { syncHistoryWithStore } from 'react-router-redux'
+import FontFaceObserver from 'fontfaceobserver';
+import { useScroll } from 'react-router-scroll';
+import configureStore from './store'
+
+// Import Language Provider
+import LanguageProvider from 'containers/LanguageProvider'
+// Import i18n messages
+import { translationMessages } from './i18n'
+
+// Import the CSS reset, which HtmlWebpackPlugin transfers to the build folder
+// import 'sanitize.css/lib/sanitize.css'
+import styles from 'containers/App/styles.css';
+import 'bulma/css/bulma.css'
+
+const openSansObserver = new FontFaceObserver('Open Sans', {});
+
+// When Open Sans is loaded, add a font-family using Open Sans to the body
+openSansObserver.load().then(() => {
+ document.body.classList.add(styles.fontLoaded);
+}, () => {
+ document.body.classList.remove(styles.fontLoaded);
+})
+
+// Create redux store with history
+// this uses the singleton browserHistory provided by react-router
+// Optionally, this could be changed to leverage a created history
+// e.g. `const browserHistory = useRouterHistory(createBrowserHistory)();`
+const initialState = {}
+const store = configureStore(initialState, browserHistory)
+
+// Sync history and store, as the react-router-redux reducer
+// is under the non-default key ("routing"), selectLocationState
+// must be provided for resolving how to retrieve the "route" in the state
+import { selectLocationState } from 'containers/App/selectors'
+const history = syncHistoryWithStore(hashHistory, store, {
+ selectLocationState: selectLocationState()
+})
+
+// Set up the router, wrapping all Routes in the App component
+import App from 'containers/App'
+import createRoutes from './routes'
+const rootRoute = {
+ component: App,
+ childRoutes: createRoutes(store)
+}
+const render = () => {
+ ReactDOM.render(
+
+
+
+
+ ,
+ document.getElementById('app')
+ )
+}
+// Hot reloadable translation json files
+if (module.hot) {
+ module.hot.accept('./i18n', () => {
+ render(translationMessages)
+ })
+}
+
+// Chunked polyfill for browsers without Intl support
+if (!window.Intl) {
+ (new Promise((resolve) => {
+ resolve(System.import('intl'));
+ }))
+ .then(() => Promise.all([
+ System.import('intl/locale-data/jsonp/en.js'),
+ System.import('intl/locale-data/jsonp/de.js'),
+ ]))
+ .then(() => render(translationMessages))
+ .catch((err) => {
+ throw err;
+ });
+} else {
+ render(translationMessages);
+}
+// Install ServiceWorker and AppCache in the end since
+// it's not most important operation and if main code fails,
+// we do not want it installed
+// import { install } from 'offline-plugin/runtime'
+// install()
diff --git a/app/components/Icons/Logo.js b/app/components/Icons/Logo.js
new file mode 100644
index 0000000..92dcf1f
--- /dev/null
+++ b/app/components/Icons/Logo.js
@@ -0,0 +1,22 @@
+import React, {PropTypes} from 'react'
+
+export default function Logo (props) {
+ return (
+
+ )
+ }
+}
diff --git a/app/components/Table/styles.css b/app/components/Table/styles.css
new file mode 100644
index 0000000..9177254
--- /dev/null
+++ b/app/components/Table/styles.css
@@ -0,0 +1,9 @@
+.Table{
+ table-layout: fixed;
+ &__TableHead{
+ background-color: #666;
+ }
+ &__TableBody{
+ overflow: scroll;
+ }
+}
diff --git a/app/containers/App/index.js b/app/containers/App/index.js
new file mode 100755
index 0000000..49b069b
--- /dev/null
+++ b/app/containers/App/index.js
@@ -0,0 +1,27 @@
+/* App.react.js
+ * This component is the skeleton around the actual pages, and should only
+ * contain code that should be seen on all pages. (e.g. navigation bar)
+ */
+
+import React, {PropTypes} from 'react'
+import NavigationBar from 'components/NavigationBar'
+import Pagination from 'components/Pagination'
+
+import classNames from 'classnames'
+import styles from './styles.css'
+
+export default class App extends React.Component {
+ static propTypes = {
+ children: PropTypes.node
+ }
+
+ render () {
+ return (
+
+
+ {this.props.children}
+
+
+ )
+ }
+}
diff --git a/app/containers/App/selectors.js b/app/containers/App/selectors.js
new file mode 100755
index 0000000..e2f8108
--- /dev/null
+++ b/app/containers/App/selectors.js
@@ -0,0 +1,18 @@
+// selectLocationState expects a plain JS object for the routing state
+const selectLocationState = () => {
+ let prevRoutingState
+ let prevRoutingStateJS
+
+ return (state) => {
+ const routingState = state.get('route') // or state.route
+
+ if (!routingState.equals(prevRoutingState)) {
+ prevRoutingState = routingState
+ prevRoutingStateJS = routingState.toJS()
+ }
+
+ return prevRoutingStateJS
+ }
+}
+
+export {selectLocationState}
diff --git a/app/containers/App/styles.css b/app/containers/App/styles.css
new file mode 100644
index 0000000..f622865
--- /dev/null
+++ b/app/containers/App/styles.css
@@ -0,0 +1,5 @@
+.App{
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
diff --git a/app/containers/App/tests/selectors.test.js b/app/containers/App/tests/selectors.test.js
new file mode 100755
index 0000000..8f43037
--- /dev/null
+++ b/app/containers/App/tests/selectors.test.js
@@ -0,0 +1,14 @@
+import { fromJS } from 'immutable'
+import expect from 'expect'
+
+import { selectLocationState } from 'containers/App/selectors'
+
+describe('selectLocationState', () => {
+ it('should select the route as a plain JS object', () => {
+ const route = fromJS({
+ locationBeforeTransitions: null
+ })
+ const mockedState = fromJS({route})
+ expect(selectLocationState()(mockedState)).toEqual(route.toJS())
+ })
+})
diff --git a/app/containers/LanguageProvider/actions.js b/app/containers/LanguageProvider/actions.js
new file mode 100755
index 0000000..5030d68
--- /dev/null
+++ b/app/containers/LanguageProvider/actions.js
@@ -0,0 +1,10 @@
+/* LanguageProvider actions */
+
+import {CHANGE_LOCALE} from './constants'
+
+export function changeLocale (languageLocale) {
+ return {
+ type: CHANGE_LOCALE,
+ locale: languageLocale
+ }
+}
diff --git a/app/containers/LanguageProvider/constants.js b/app/containers/LanguageProvider/constants.js
new file mode 100755
index 0000000..730444c
--- /dev/null
+++ b/app/containers/LanguageProvider/constants.js
@@ -0,0 +1,3 @@
+/* LanguageProvider constants */
+
+export const CHANGE_LOCALE = 'app/LanguageToggle/CHANGE_LOCALE'
diff --git a/app/containers/LanguageProvider/index.js b/app/containers/LanguageProvider/index.js
new file mode 100755
index 0000000..ca17662
--- /dev/null
+++ b/app/containers/LanguageProvider/index.js
@@ -0,0 +1,37 @@
+/* LanguageProvider
+ * this component connects the redux state language locale to the
+ * IntlProvider component and i18n messages (loaded from `app/translations`)
+ */
+
+import React from 'react'
+import { connect } from 'react-redux'
+import { createSelector } from 'reselect'
+import { IntlProvider } from 'react-intl'
+import { selectLocale } from './selectors'
+
+export class LanguageProvider extends React.Component {
+ render () {
+ return (
+
+ {this.props.children}
+
+ )
+ }
+}
+
+LanguageProvider.propTypes = {
+ locale: React.PropTypes.string,
+ messages: React.PropTypes.object,
+ children: React.PropTypes.element.isRequired
+}
+
+const mapStateToProps = createSelector(
+ selectLocale(),
+ (locale) => ({locale})
+)
+
+function mapDispatchToProps (dispatch) {
+ return {dispatch}
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(LanguageProvider)
diff --git a/app/containers/LanguageProvider/reducer.js b/app/containers/LanguageProvider/reducer.js
new file mode 100755
index 0000000..d752735
--- /dev/null
+++ b/app/containers/LanguageProvider/reducer.js
@@ -0,0 +1,19 @@
+/* LanguageProvider reducer */
+
+import { fromJS } from 'immutable'
+import {CHANGE_LOCALE} from './constants'
+
+const initialState = fromJS({
+ locale: 'en'
+})
+
+function languageProviderReducer (state = initialState, action) {
+ switch (action.type) {
+ case CHANGE_LOCALE:
+ return state.set('locale', action.locale)
+ default:
+ return state
+ }
+}
+
+export default languageProviderReducer
diff --git a/app/containers/LanguageProvider/selectors.js b/app/containers/LanguageProvider/selectors.js
new file mode 100755
index 0000000..cf69135
--- /dev/null
+++ b/app/containers/LanguageProvider/selectors.js
@@ -0,0 +1,12 @@
+import { createSelector } from 'reselect'
+
+/* Direct selector to the languageToggle state domain */
+const selectLanguage = () => state => state.get('language')
+
+/* Select the language locale */
+const selectLocale = () => createSelector(
+ selectLanguage(),
+ (languageState) => languageState.get('locale')
+)
+
+export {selectLanguage, selectLocale}
diff --git a/app/containers/LanguageProvider/tests/actions.test.js b/app/containers/LanguageProvider/tests/actions.test.js
new file mode 100755
index 0000000..86647e6
--- /dev/null
+++ b/app/containers/LanguageProvider/tests/actions.test.js
@@ -0,0 +1,15 @@
+import expect from 'expect'
+import {changeLocale} from '../actions'
+import {CHANGE_LOCALE} from '../constants'
+
+describe('LanguageProvider actions', () => {
+ describe('Change Local Action', () => {
+ it('has a type of CHANGE_LOCALE', () => {
+ const expected = {
+ type: CHANGE_LOCALE,
+ locale: 'es'
+ }
+ expect(changeLocale('es')).toEqual(expected)
+ })
+ })
+})
diff --git a/app/containers/LanguageProvider/tests/index.test.js b/app/containers/LanguageProvider/tests/index.test.js
new file mode 100755
index 0000000..60191f3
--- /dev/null
+++ b/app/containers/LanguageProvider/tests/index.test.js
@@ -0,0 +1,35 @@
+import LanguageProvider from '../index'
+
+import expect from 'expect'
+import { shallow } from 'enzyme'
+import { FormattedMessage, defineMessages } from 'react-intl'
+import configureStore from '../../../store'
+import React from 'react'
+import { Provider } from 'react-redux'
+import { browserHistory } from 'react-router'
+import { translatedMessages } from '../../../i18n'
+
+describe('', () => {
+ let store
+
+ before(() => {
+ store = configureStore({}, browserHistory)
+ })
+
+ it('should render the default language messages', () => {
+ const messages = defineMessages({
+ someMessage: {
+ id: 'some.id',
+ defaultMessage: 'This is some default message'
+ }
+ })
+ const renderedComponent = shallow(
+
+
+
+
+
+ )
+ expect(renderedComponent.contains()).toEqual(true)
+ })
+})
diff --git a/app/containers/LanguageProvider/tests/reducer.test.js b/app/containers/LanguageProvider/tests/reducer.test.js
new file mode 100755
index 0000000..f6e37a2
--- /dev/null
+++ b/app/containers/LanguageProvider/tests/reducer.test.js
@@ -0,0 +1,11 @@
+import expect from 'expect'
+import languageProviderReducer from '../reducer'
+import { fromJS } from 'immutable'
+
+describe('languageProviderReducer', () => {
+ it('returns the initial state', () => {
+ expect(languageProviderReducer(undefined, {})).toEqual(fromJS({
+ locale: 'en'
+ }))
+ })
+})
diff --git a/app/containers/LanguageProvider/tests/selectors.test.js b/app/containers/LanguageProvider/tests/selectors.test.js
new file mode 100755
index 0000000..ddae78a
--- /dev/null
+++ b/app/containers/LanguageProvider/tests/selectors.test.js
@@ -0,0 +1,14 @@
+import {selectLanguage} from '../selectors'
+import {fromJS} from 'immutable'
+import expect from 'expect'
+
+describe('selectLanguage', () => {
+ const globalSelector = selectLanguage()
+ it('should select the global state', () => {
+ const globalState = fromJS({})
+ const mockedState = fromJS({
+ language: globalState
+ })
+ expect(globalSelector(mockedState)).toEqual(globalState)
+ })
+})
diff --git a/app/containers/Logger/index.js b/app/containers/Logger/index.js
new file mode 100755
index 0000000..6ec8946
--- /dev/null
+++ b/app/containers/Logger/index.js
@@ -0,0 +1,49 @@
+/* Logger */
+
+import React from 'react'
+import Table from 'components/Table'
+
+import styles from './styles.css'
+
+const logs = [
+ {
+ date: '12:51:12',
+ method: 'GET',
+ origin: '11.1.1.143',
+ destination: 'disney.com',
+ path: '/',
+ status: 311,
+ size: '35.24Kb',
+ type: 'application/text',
+ time: '256.71'
+ }, {
+ date: '12:51:12',
+ method: 'GET',
+ origin: '11.1.1.13',
+ destination: 'rog.mx',
+ path: '/',
+ status: 311,
+ size: '35.24Kb',
+ type: 'application/text',
+ time: '33.71'
+ }, {
+ date: '12:51:12',
+ method: 'GET',
+ origin: '11.1.1.143',
+ destination: 'test.mx',
+ path: '/',
+ status: 311,
+ size: '42.24Kb',
+ type: 'application/text',
+ time: '133.37'
+ }
+]
+
+export default function Logger () {
+ const data = Array.apply(null, Array(42)).map(() => { return logs[Math.floor(Math.random() * 3)] })
+ return (
+
+
+
+ )
+}
diff --git a/app/containers/Logger/messages.js b/app/containers/Logger/messages.js
new file mode 100644
index 0000000..5b1aa49
--- /dev/null
+++ b/app/containers/Logger/messages.js
@@ -0,0 +1,12 @@
+import { defineMessages } from 'react-intl'
+
+export default defineMessages({
+ noConnected: {
+ id: 'hyperfox.containers.Logger.Notification.noconnected.message',
+ defaultMessage: 'Could not connect to the Hyperfox API. Seems like hyperfox is not running on your local box.'
+ },
+ noData: {
+ id: 'hyperfox.containers.Logger.Notification.nodata.message',
+ defaultMessage: 'No data to show yet.'
+ }
+})
diff --git a/app/containers/Logger/styles.css b/app/containers/Logger/styles.css
new file mode 100644
index 0000000..99e08bb
--- /dev/null
+++ b/app/containers/Logger/styles.css
@@ -0,0 +1,4 @@
+.Logger{
+ flex: 1;
+ overflow: auto;
+}
diff --git a/app/containers/NotFoundPage/index.js b/app/containers/NotFoundPage/index.js
new file mode 100755
index 0000000..84ca606
--- /dev/null
+++ b/app/containers/NotFoundPage/index.js
@@ -0,0 +1,14 @@
+/* NotFoundPage
+ * This is the page we show when the user visits a url that doesn't have a route
+ */
+
+import React from 'react'
+
+export default class NotFound extends React.Component {
+
+ render () {
+ return (
+
Page Not Found
+ )
+ }
+}
diff --git a/app/i18n.js b/app/i18n.js
new file mode 100644
index 0000000..c848bac
--- /dev/null
+++ b/app/i18n.js
@@ -0,0 +1,30 @@
+/* This will setup the i18n language files and locale data for hiperfox */
+import { addLocaleData } from 'react-intl'
+
+import enLocaleData from 'react-intl/locale-data/en'
+import esLocaleData from 'react-intl/locale-data/es'
+
+export const appLocales = [
+ 'en',
+ 'es'
+]
+
+import enTranslationMessages from './translations/en.json'
+import esTranslationMessages from './translations/es.json'
+
+addLocaleData(enLocaleData)
+addLocaleData(esLocaleData)
+
+const formatTranslationMessages = (messages) => {
+ const formattedMessages = {}
+ for (const message of messages) {
+ formattedMessages[message.id] = message.message || message.defaultMessage
+ }
+
+ return formattedMessages
+}
+
+export const translationMessages = {
+ en: formatTranslationMessages(enTranslationMessages),
+ es: formatTranslationMessages(esTranslationMessages)
+}
diff --git a/app/index.html b/app/index.html
new file mode 100755
index 0000000..7e4192e
--- /dev/null
+++ b/app/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+ Hyperfox
+
+
+
+
+
diff --git a/app/manifest.json b/app/manifest.json
new file mode 100755
index 0000000..a2ad609
--- /dev/null
+++ b/app/manifest.json
@@ -0,0 +1,33 @@
+{
+ "name": "Hyperfox",
+ "icons": [
+ {
+ "src": "favicon.png",
+ "sizes": "48x48",
+ "type": "image/png",
+ "density": 1.0
+ },
+ {
+ "src": "favicon.png",
+ "sizes": "96x96",
+ "type": "image/png",
+ "density": 2.0
+ },
+ {
+ "src": "favicon.png",
+ "sizes": "144x144",
+ "type": "image/png",
+ "density": 3.0
+ },
+ {
+ "src": "favicon.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "density": 4.0
+ }
+ ],
+ "start_url": "index.html",
+ "display": "standalone",
+ "orientation": "portrait",
+ "background_color": "#FFFFFF"
+}
diff --git a/app/reducers.js b/app/reducers.js
new file mode 100755
index 0000000..5e289c7
--- /dev/null
+++ b/app/reducers.js
@@ -0,0 +1,47 @@
+/* Combine all reducers in this file and export the combined reducers.
+ * If we were to do this in store.js, reducers wouldn't be hot reloadable.
+ */
+
+import { combineReducers } from 'redux-immutable'
+import { fromJS } from 'immutable'
+import { LOCATION_CHANGE } from 'react-router-redux'
+import languageProviderReducer from 'containers/LanguageProvider/reducer'
+
+/*
+ * routeReducer
+ *
+ * The reducer merges route location changes into our immutable state.
+ * The change is necessitated by moving to react-router-redux@4
+ *
+ */
+
+// Initial routing state
+const routeInitialState = fromJS({
+ locationBeforeTransitions: null
+})
+
+/**
+ * Merge route into the global application state
+ */
+function routeReducer (state = routeInitialState, action) {
+ switch (action.type) {
+ /* istanbul ignore next */
+ case LOCATION_CHANGE:
+ return state.merge({
+ locationBeforeTransitions: action.payload
+ })
+ default:
+ return state
+ }
+}
+
+/**
+ * Creates the main reducer with the asynchronously loaded ones
+ */
+export default function createReducer (asyncReducers) {
+ return combineReducers({
+ route: routeReducer,
+ language: languageProviderReducer,
+ ...asyncReducers
+ })
+}
diff --git a/app/routes.js b/app/routes.js
new file mode 100755
index 0000000..8ed3e7a
--- /dev/null
+++ b/app/routes.js
@@ -0,0 +1,41 @@
+// import { getHooks } from 'utils/hooks'
+
+const errorLoading = (err) => {
+ console.error('Dynamic page loading failed', err)
+}
+
+const loadModule = (cb) => (componentModule) => {
+ cb(null, componentModule.default)
+}
+
+export default function createRoutes () {
+ // const { injectReducer, injectSagas } = getHooks(store)
+
+ return [
+ {
+ path: '/',
+ name: 'home',
+ getComponent (nextState, cb) {
+ const importModules = Promise.all([
+ System.import('containers/Logger')
+ ])
+
+ const renderRoute = loadModule(cb)
+
+ importModules.then(([component]) => {
+ renderRoute(component)
+ })
+
+ importModules.catch(errorLoading)
+ }
+ }, {
+ path: '*',
+ name: 'notfound',
+ getComponent (nextState, cb) {
+ System.import('containers/NotFoundPage')
+ .then(loadModule(cb))
+ .catch(errorLoading)
+ }
+ }
+ ]
+}
diff --git a/app/store.js b/app/store.js
new file mode 100755
index 0000000..d1e20af
--- /dev/null
+++ b/app/store.js
@@ -0,0 +1,49 @@
+/* Create the store with asynchronously loaded reducers */
+
+import { createStore, applyMiddleware, compose } from 'redux'
+import { fromJS } from 'immutable'
+import { routerMiddleware } from 'react-router-redux'
+import createSagaMiddleware from 'redux-saga'
+import createReducer from './reducers'
+
+const sagaMiddleware = createSagaMiddleware()
+const devtools = window.devToolsExtension || (() => noop => noop)
+
+export default function configureStore (initialState = {}, history) {
+ // Create the store with two middlewares
+ // 1. sagaMiddleware: Makes redux-sagas work
+ // 2. routerMiddleware: Syncs the location/URL path to the state
+ const middlewares = [
+ sagaMiddleware,
+ routerMiddleware(history)
+ ]
+
+ const enhancers = [
+ applyMiddleware(...middlewares),
+ devtools()
+ ]
+
+ const store = createStore(
+ createReducer(),
+ fromJS(initialState),
+ compose(...enhancers)
+ )
+
+ // Create hook for async sagas
+ store.runSaga = sagaMiddleware.run
+
+ // Make reducers hot reloadable
+ /* istanbul ignore next */
+ if (module.hot) {
+ System.import('./reducers').then((reducerModule) => {
+ const createReducers = reducerModule.default
+ const nextReducers = createReducers(store.asyncReducers)
+
+ store.replaceReducer(nextReducers)
+ })
+ }
+
+ // Initialize it with no other reducers
+ store.asyncReducers = {}
+ return store
+}
diff --git a/app/store.test.js b/app/store.test.js
new file mode 100755
index 0000000..bb7c2a4
--- /dev/null
+++ b/app/store.test.js
@@ -0,0 +1,25 @@
+/* Test store addons */
+
+import expect from 'expect'
+import configureStore from './store'
+import { browserHistory } from 'react-router'
+
+describe('configureStore', () => {
+ let store
+
+ before(() => {
+ store = configureStore({}, browserHistory)
+ })
+
+ describe('asyncReducers', () => {
+ it('should contain an object for async reducers', () => {
+ expect(typeof store.asyncReducers).toEqual('object')
+ })
+ })
+
+ describe('runSaga', () => {
+ it('should contain a hook for `sagaMiddleware.run`', () => {
+ expect(typeof store.runSaga).toEqual('function')
+ })
+ })
+})
diff --git a/app/tests/store.test.js b/app/tests/store.test.js
new file mode 100755
index 0000000..2d9c2f9
--- /dev/null
+++ b/app/tests/store.test.js
@@ -0,0 +1,25 @@
+/* Test store addons */
+
+import expect from 'expect'
+import configureStore from '../store'
+import { browserHistory } from 'react-router'
+
+describe('configureStore', () => {
+ let store
+
+ before(() => {
+ store = configureStore({}, browserHistory)
+ })
+
+ describe('asyncReducers', () => {
+ it('should contain an object for async reducers', () => {
+ expect(typeof store.asyncReducers).toEqual('object')
+ })
+ })
+
+ describe('runSaga', () => {
+ it('should contain a hook for `sagaMiddleware.run`', () => {
+ expect(typeof store.runSaga).toEqual('function')
+ })
+ })
+})
diff --git a/app/translations/en.json b/app/translations/en.json
new file mode 100755
index 0000000..b28617a
--- /dev/null
+++ b/app/translations/en.json
@@ -0,0 +1,17 @@
+[
+ {
+ "id": "hyperfox.components.NavigationBar.search.message",
+ "defaultMessage": "Search.",
+ "message": ""
+ },
+ {
+ "id": "hyperfox.containers.Logger.Notification.noconnected.message",
+ "defaultMessage": "Could not connect to the Hyperfox API. Seems like hyperfox is not running on your local box.",
+ "message": ""
+ },
+ {
+ "id": "hyperfox.containers.Logger.Notification.nodata.message",
+ "defaultMessage": "No data to show yet.",
+ "message": ""
+ }
+]
diff --git a/app/translations/es.json b/app/translations/es.json
new file mode 100755
index 0000000..7038803
--- /dev/null
+++ b/app/translations/es.json
@@ -0,0 +1,17 @@
+[
+ {
+ "id": "hyperfox.components.NavigationBar.search.message",
+ "defaultMessage": "Search.",
+ "message": "Buscar"
+ },
+ {
+ "id": "hyperfox.containers.Logger.Notification.noconnected.message",
+ "defaultMessage": "Could not connect to the Hyperfox API. Seems like hyperfox is not running on your local box.",
+ "message": ""
+ },
+ {
+ "id": "hyperfox.containers.Logger.Notification.nodata.message",
+ "defaultMessage": "No data to show yet.",
+ "message": ""
+ }
+]
\ No newline at end of file
diff --git a/app/utils/hooks.js b/app/utils/hooks.js
new file mode 100755
index 0000000..f2789b5
--- /dev/null
+++ b/app/utils/hooks.js
@@ -0,0 +1,22 @@
+import createReducer from 'reducers.js'
+
+/* Inject an asynchronously loaded reducer */
+export function injectAsyncReducer (store) {
+ return (name, asyncReducer) => {
+ store.asyncReducers[name] = asyncReducer
+ store.replaceReducer(createReducer(store.asyncReducers))
+ }
+}
+
+/* Inject an asynchronously loaded saga */
+export function injectAsyncSagas (store) {
+ return (sagas) => sagas.map(store.runSaga)
+}
+
+/* Helper for creating injectors */
+export function getHooks (store) {
+ return {
+ injectReducer: injectAsyncReducer(store),
+ injectSagas: injectAsyncSagas(store)
+ }
+}
diff --git a/app/utils/tests/hooks.test.js b/app/utils/tests/hooks.test.js
new file mode 100755
index 0000000..4f0a1c7
--- /dev/null
+++ b/app/utils/tests/hooks.test.js
@@ -0,0 +1,82 @@
+/* Test hooks */
+
+import expect from 'expect'
+import configureStore from 'store.js'
+import { memoryHistory } from 'react-router'
+import { put } from 'redux-saga/effects'
+import { fromJS } from 'immutable'
+
+import {injectAsyncReducer, injectAsyncSagas, getHooks} from 'utils/hooks'
+
+// Fixtures
+
+const initialState = fromJS({ reduced: 'soon' })
+
+const reducer = (state = initialState, action) => {
+ switch (action.type) {
+ case 'TEST':
+ return state.set('reduced', action.payload)
+ default:
+ return state
+ }
+}
+
+const sagas = [
+ function * testSaga () {
+ yield put({ type: 'TEST', payload: 'yup' })
+ }
+]
+
+describe('hooks', () => {
+ let store
+
+ describe('getHooks', () => {
+ before(() => {
+ store = configureStore({}, memoryHistory)
+ })
+
+ it('given a store, should return all hooks', () => {
+ const { injectReducer, injectSagas } = getHooks(store)
+
+ injectReducer('test', reducer)
+ injectSagas(sagas)
+
+ const actual = store.getState().get('test')
+ const expected = initialState.merge({ reduced: 'yup' })
+
+ expect(actual.toJS()).toEqual(expected.toJS())
+ })
+ })
+
+ describe('helpers', () => {
+ before(() => {
+ store = configureStore({}, memoryHistory)
+ })
+
+ describe('injectAsyncReducer', () => {
+ it('given a store, it should provide a function to inject a reducer', () => {
+ const injectReducer = injectAsyncReducer(store)
+
+ injectReducer('test', reducer)
+
+ const actual = store.getState().get('test')
+ const expected = initialState
+
+ expect(actual.toJS()).toEqual(expected.toJS())
+ })
+ })
+
+ describe('injectAsyncSagas', () => {
+ it('given a store, it should provide a function to inject a saga', () => {
+ const injectSagas = injectAsyncSagas(store)
+
+ injectSagas(sagas)
+
+ const actual = store.getState().get('test')
+ const expected = initialState.merge({ reduced: 'yup' })
+
+ expect(actual.toJS()).toEqual(expected.toJS())
+ })
+ })
+ })
+})
diff --git a/electron.js b/electron.js
new file mode 100644
index 0000000..546db5f
--- /dev/null
+++ b/electron.js
@@ -0,0 +1,35 @@
+const electron = require('electron')
+
+const app = electron.app
+const BrowserWindow = electron.BrowserWindow
+
+let mainWindow
+function createWindow () {
+ mainWindow = new BrowserWindow({
+ width: 1200,
+ height: 600,
+ title: 'Hyperfox',
+ icon: __dirname + '/electron/hyperfox-icon.ico',
+ })
+ mainWindow.loadURL(`file://${__dirname}/build/index.html`)
+ mainWindow.on('closed', function () {
+ mainWindow = null
+ })
+}
+
+app.on('ready', createWindow)
+app.on('window-all-closed', function () {
+ if (process.platform !== 'darwin') {
+ app.quit()
+ }
+})
+
+app.on('activate', function () {
+ if (mainWindow === null) {
+ createWindow()
+ }
+})
+
+app.on('close-main-window', function () {
+ app.quit()
+})
diff --git a/electron/hyperfox-icon.icns b/electron/hyperfox-icon.icns
new file mode 100644
index 0000000..0cf9c02
Binary files /dev/null and b/electron/hyperfox-icon.icns differ
diff --git a/electron/hyperfox-icon.ico b/electron/hyperfox-icon.ico
new file mode 100644
index 0000000..ee0ac35
Binary files /dev/null and b/electron/hyperfox-icon.ico differ
diff --git a/electron/hyperfox-icon.png b/electron/hyperfox-icon.png
new file mode 100644
index 0000000..5e03d44
Binary files /dev/null and b/electron/hyperfox-icon.png differ
diff --git a/electron/package.js b/electron/package.js
new file mode 100644
index 0000000..0e6bf72
--- /dev/null
+++ b/electron/package.js
@@ -0,0 +1,33 @@
+var packager = require('electron-packager')
+var logger = require('../server/logger')
+
+var platform
+if (process.argv.indexOf("-p") !== -1) {
+ platform = process.argv[process.argv.indexOf('-p') + 1]
+}
+
+var packageOptions = {
+ dir: './',
+ name: 'Hyperfox',
+ platform: platform,
+ out: './releases',
+ overwrite: true,
+ icon: './electron/hyperfox-icon.icns',
+ ignore: [
+ 'app',
+ 'coverage',
+ 'releases',
+ 'internals',
+ 'server',
+ 'node_modules'
+ ]
+}
+
+packager(packageOptions, function done_callback (err, appPaths) {
+ if (err) {
+ logger.error(err.message)
+ }
+ if (appPaths !== undefined ) {
+ logger.electron(appPaths)
+ }
+})
diff --git a/html/.bowerrc b/html/.bowerrc
deleted file mode 100644
index 69fad35..0000000
--- a/html/.bowerrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "directory": "bower_components"
-}
diff --git a/html/.editorconfig b/html/.editorconfig
deleted file mode 100644
index c2cdfb8..0000000
--- a/html/.editorconfig
+++ /dev/null
@@ -1,21 +0,0 @@
-# EditorConfig helps developers define and maintain consistent
-# coding styles between different editors and IDEs
-# editorconfig.org
-
-root = true
-
-
-[*]
-
-# Change these settings to your own preference
-indent_style = space
-indent_size = 2
-
-# We recommend you to keep these unchanged
-end_of_line = lf
-charset = utf-8
-trim_trailing_whitespace = true
-insert_final_newline = true
-
-[*.md]
-trim_trailing_whitespace = false
diff --git a/html/.gitattributes b/html/.gitattributes
deleted file mode 100644
index 2125666..0000000
--- a/html/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-* text=auto
\ No newline at end of file
diff --git a/html/.gitignore b/html/.gitignore
deleted file mode 100644
index 980a1aa..0000000
--- a/html/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-node_modules
-*.swp
-dist
-.tmp
-.sass-cache
-bower_components
diff --git a/html/.jshintrc b/html/.jshintrc
deleted file mode 100644
index f750969..0000000
--- a/html/.jshintrc
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "node": true,
- "browser": true,
- "esnext": true,
- "bitwise": true,
- "camelcase": true,
- "curly": true,
- "eqeqeq": true,
- "immed": true,
- "indent": 2,
- "latedef": true,
- "newcap": true,
- "noarg": true,
- "quotmark": "single",
- "undef": true,
- "unused": true,
- "strict": true,
- "trailing": true,
- "smarttabs": true,
- "globals": {
- "angular": false
- }
-}
diff --git a/html/.travis.yml b/html/.travis.yml
deleted file mode 100644
index a80b6e0..0000000
--- a/html/.travis.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-language: node_js
-node_js:
- - '0.10'
-before_script:
- - 'npm install -g bower grunt-cli'
- - 'bower install'
diff --git a/html/Gruntfile.js b/html/Gruntfile.js
deleted file mode 100644
index 737d49a..0000000
--- a/html/Gruntfile.js
+++ /dev/null
@@ -1,441 +0,0 @@
-// Generated on 2014-12-20 using generator-angular 0.10.0
-'use strict';
-
-// # Globbing
-// for performance reasons we're only matching one level down:
-// 'test/spec/{,*/}*.js'
-// use this if you want to recursively match all subfolders:
-// 'test/spec/**/*.js'
-
-module.exports = function (grunt) {
-
- // Load grunt tasks automatically
- require('load-grunt-tasks')(grunt);
-
- // Time how long tasks take. Can help when optimizing build times
- require('time-grunt')(grunt);
-
- // Configurable paths for the application
- var appConfig = {
- app: require('./bower.json').appPath || 'app',
- dist: 'dist'
- };
-
- // Define the configuration for all the tasks
- grunt.initConfig({
-
- // Project settings
- yeoman: appConfig,
-
- // Watches files for changes and runs tasks based on the changed files
- watch: {
- bower: {
- files: ['bower.json'],
- tasks: ['wiredep']
- },
- js: {
- files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
- tasks: ['newer:jshint:all'],
- options: {
- livereload: '<%= connect.options.livereload %>'
- }
- },
- jsTest: {
- files: ['test/spec/{,*/}*.js'],
- tasks: ['newer:jshint:test', 'karma']
- },
- compass: {
- files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
- tasks: ['compass:server', 'autoprefixer']
- },
- gruntfile: {
- files: ['Gruntfile.js']
- },
- livereload: {
- options: {
- livereload: '<%= connect.options.livereload %>'
- },
- files: [
- '<%= yeoman.app %>/{,*/}*.html',
- '.tmp/styles/{,*/}*.css',
- '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
- ]
- }
- },
-
- // The actual grunt server settings
- connect: {
- options: {
- port: 9000,
- // Change this to '0.0.0.0' to access the server from outside.
- hostname: 'localhost',
- livereload: 35729
- },
- livereload: {
- options: {
- open: true,
- middleware: function (connect) {
- return [
- connect.static('.tmp'),
- connect().use(
- '/bower_components',
- connect.static('./bower_components')
- ),
- connect.static(appConfig.app)
- ];
- }
- }
- },
- test: {
- options: {
- port: 9001,
- middleware: function (connect) {
- return [
- connect.static('.tmp'),
- connect.static('test'),
- connect().use(
- '/bower_components',
- connect.static('./bower_components')
- ),
- connect.static(appConfig.app)
- ];
- }
- }
- },
- dist: {
- options: {
- open: true,
- base: '<%= yeoman.dist %>'
- }
- }
- },
-
- // Make sure code styles are up to par and there are no obvious mistakes
- jshint: {
- options: {
- jshintrc: '.jshintrc',
- reporter: require('jshint-stylish')
- },
- all: {
- src: [
- 'Gruntfile.js',
- '<%= yeoman.app %>/scripts/{,*/}*.js'
- ]
- },
- test: {
- options: {
- jshintrc: 'test/.jshintrc'
- },
- src: ['test/spec/{,*/}*.js']
- }
- },
-
- // Empties folders to start fresh
- clean: {
- dist: {
- files: [{
- dot: true,
- src: [
- '.tmp',
- '<%= yeoman.dist %>/{,*/}*',
- '!<%= yeoman.dist %>/.git{,*/}*'
- ]
- }]
- },
- server: '.tmp'
- },
-
- // Add vendor prefixed styles
- autoprefixer: {
- options: {
- browsers: ['last 1 version']
- },
- dist: {
- files: [{
- expand: true,
- cwd: '.tmp/styles/',
- src: '{,*/}*.css',
- dest: '.tmp/styles/'
- }]
- }
- },
-
- // Automatically inject Bower components into the app
- wiredep: {
- app: {
- src: ['<%= yeoman.app %>/index.html'],
- ignorePath: /\.\.\//
- },
- sass: {
- src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
- ignorePath: /(\.\.\/){1,2}bower_components\//
- }
- },
-
- // Compiles Sass to CSS and generates necessary files if requested
- compass: {
- options: {
- sassDir: '<%= yeoman.app %>/styles',
- cssDir: '.tmp/styles',
- generatedImagesDir: '.tmp/images/generated',
- imagesDir: '<%= yeoman.app %>/images',
- javascriptsDir: '<%= yeoman.app %>/scripts',
- fontsDir: '<%= yeoman.app %>/styles/fonts',
- importPath: './bower_components',
- httpImagesPath: '/images',
- httpGeneratedImagesPath: '/images/generated',
- httpFontsPath: '/styles/fonts',
- relativeAssets: false,
- assetCacheBuster: false,
- raw: 'Sass::Script::Number.precision = 10\n'
- },
- dist: {
- options: {
- generatedImagesDir: '<%= yeoman.dist %>/images/generated'
- }
- },
- server: {
- options: {
- debugInfo: true
- }
- }
- },
-
- // Renames files for browser caching purposes
- filerev: {
- dist: {
- src: [
- '<%= yeoman.dist %>/scripts/{,*/}*.js',
- '<%= yeoman.dist %>/styles/{,*/}*.css',
- '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
- '<%= yeoman.dist %>/styles/fonts/*'
- ]
- }
- },
-
- // Reads HTML for usemin blocks to enable smart builds that automatically
- // concat, minify and revision files. Creates configurations in memory so
- // additional tasks can operate on them
- useminPrepare: {
- html: '<%= yeoman.app %>/index.html',
- options: {
- dest: '<%= yeoman.dist %>',
- flow: {
- html: {
- steps: {
- js: ['concat', 'uglifyjs'],
- css: ['cssmin']
- },
- post: {}
- }
- }
- }
- },
-
- // Performs rewrites based on filerev and the useminPrepare configuration
- usemin: {
- html: ['<%= yeoman.dist %>/{,*/}*.html'],
- css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
- options: {
- assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
- }
- },
-
- // The following *-min tasks will produce minified files in the dist folder
- // By default, your `index.html`'s will take care of
- // minification. These next options are pre-configured if you do not wish
- // to use the Usemin blocks.
- // cssmin: {
- // dist: {
- // files: {
- // '<%= yeoman.dist %>/styles/main.css': [
- // '.tmp/styles/{,*/}*.css'
- // ]
- // }
- // }
- // },
- // uglify: {
- // dist: {
- // files: {
- // '<%= yeoman.dist %>/scripts/scripts.js': [
- // '<%= yeoman.dist %>/scripts/scripts.js'
- // ]
- // }
- // }
- // },
- // concat: {
- // dist: {}
- // },
-
- imagemin: {
- dist: {
- files: [{
- expand: true,
- cwd: '<%= yeoman.app %>/images',
- src: '{,*/}*.{png,jpg,jpeg,gif}',
- dest: '<%= yeoman.dist %>/images'
- }]
- }
- },
-
- svgmin: {
- dist: {
- files: [{
- expand: true,
- cwd: '<%= yeoman.app %>/images',
- src: '{,*/}*.svg',
- dest: '<%= yeoman.dist %>/images'
- }]
- }
- },
-
- htmlmin: {
- dist: {
- options: {
- collapseWhitespace: true,
- conservativeCollapse: true,
- collapseBooleanAttributes: true,
- removeCommentsFromCDATA: true,
- removeOptionalTags: true
- },
- files: [{
- expand: true,
- cwd: '<%= yeoman.dist %>',
- src: ['*.html', 'views/{,*/}*.html'],
- dest: '<%= yeoman.dist %>'
- }]
- }
- },
-
- // ng-annotate tries to make the code safe for minification automatically
- // by using the Angular long form for dependency injection.
- ngAnnotate: {
- dist: {
- files: [{
- expand: true,
- cwd: '.tmp/concat/scripts',
- src: ['*.js', '!oldieshim.js'],
- dest: '.tmp/concat/scripts'
- }]
- }
- },
-
- // Replace Google CDN references
- cdnify: {
- dist: {
- html: ['<%= yeoman.dist %>/*.html']
- }
- },
-
- // Copies remaining files to places other tasks can use
- copy: {
- dist: {
- files: [{
- expand: true,
- dot: true,
- cwd: '<%= yeoman.app %>',
- dest: '<%= yeoman.dist %>',
- src: [
- '*.{ico,png,txt}',
- '.htaccess',
- '*.html',
- 'views/{,*/}*.html',
- 'images/{,*/}*.{webp}',
- 'fonts/{,*/}*.*'
- ]
- }, {
- expand: true,
- cwd: '.tmp/images',
- dest: '<%= yeoman.dist %>/images',
- src: ['generated/*']
- }, {
- expand: true,
- cwd: '.',
- src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
- dest: '<%= yeoman.dist %>'
- }]
- },
- styles: {
- expand: true,
- cwd: '<%= yeoman.app %>/styles',
- dest: '.tmp/styles/',
- src: '{,*/}*.css'
- }
- },
-
- // Run some tasks in parallel to speed up the build process
- concurrent: {
- server: [
- 'compass:server'
- ],
- test: [
- 'compass'
- ],
- dist: [
- 'compass:dist',
- 'imagemin',
- 'svgmin'
- ]
- },
-
- // Test settings
- karma: {
- unit: {
- configFile: 'test/karma.conf.js',
- singleRun: true
- }
- }
- });
-
-
- grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
- if (target === 'dist') {
- return grunt.task.run(['build', 'connect:dist:keepalive']);
- }
-
- grunt.task.run([
- 'clean:server',
- 'wiredep',
- 'concurrent:server',
- 'autoprefixer',
- 'connect:livereload',
- 'watch'
- ]);
- });
-
- grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
- grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
- grunt.task.run(['serve:' + target]);
- });
-
- grunt.registerTask('test', [
- 'clean:server',
- 'concurrent:test',
- 'autoprefixer',
- 'connect:test',
- 'karma'
- ]);
-
- grunt.registerTask('build', [
- 'clean:dist',
- 'wiredep',
- 'useminPrepare',
- 'concurrent:dist',
- 'autoprefixer',
- 'concat',
- 'ngAnnotate',
- 'copy:dist',
- 'cdnify',
- 'cssmin',
- 'uglify',
- 'filerev',
- 'usemin',
- 'htmlmin'
- ]);
-
- grunt.registerTask('default', [
- 'newer:jshint',
- 'test',
- 'build'
- ]);
-};
diff --git a/html/Makefile b/html/Makefile
deleted file mode 100644
index 64337a5..0000000
--- a/html/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-clean:
- grunt clean
-
-publish: clean
- grunt build
- scp -r dist/* www-data@hyperfox.org:/var/www/hyperfox.org/live/
diff --git a/html/app/.buildignore b/html/app/.buildignore
deleted file mode 100644
index fc98b8e..0000000
--- a/html/app/.buildignore
+++ /dev/null
@@ -1 +0,0 @@
-*.coffee
\ No newline at end of file
diff --git a/html/app/.htaccess b/html/app/.htaccess
deleted file mode 100644
index cb84cb9..0000000
--- a/html/app/.htaccess
+++ /dev/null
@@ -1,543 +0,0 @@
-# Apache Configuration File
-
-# (!) Using `.htaccess` files slows down Apache, therefore, if you have access
-# to the main server config file (usually called `httpd.conf`), you should add
-# this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
-
-# ##############################################################################
-# # CROSS-ORIGIN RESOURCE SHARING (CORS) #
-# ##############################################################################
-
-# ------------------------------------------------------------------------------
-# | Cross-domain AJAX requests |
-# ------------------------------------------------------------------------------
-
-# Enable cross-origin AJAX requests.
-# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
-# http://enable-cors.org/
-
-#
-# Header set Access-Control-Allow-Origin "*"
-#
-
-# ------------------------------------------------------------------------------
-# | CORS-enabled images |
-# ------------------------------------------------------------------------------
-
-# Send the CORS header for images when browsers request it.
-# https://developer.mozilla.org/en/CORS_Enabled_Image
-# http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
-# http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
-
-
-
-
- SetEnvIf Origin ":" IS_CORS
- Header set Access-Control-Allow-Origin "*" env=IS_CORS
-
-
-
-
-# ------------------------------------------------------------------------------
-# | Web fonts access |
-# ------------------------------------------------------------------------------
-
-# Allow access from all domains for web fonts
-
-
-
- Header set Access-Control-Allow-Origin "*"
-
-
-
-
-# ##############################################################################
-# # ERRORS #
-# ##############################################################################
-
-# ------------------------------------------------------------------------------
-# | 404 error prevention for non-existing redirected folders |
-# ------------------------------------------------------------------------------
-
-# Prevent Apache from returning a 404 error for a rewrite if a directory
-# with the same name does not exist.
-# http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
-# http://www.webmasterworld.com/apache/3808792.htm
-
-Options -MultiViews
-
-# ------------------------------------------------------------------------------
-# | Custom error messages / pages |
-# ------------------------------------------------------------------------------
-
-# You can customize what Apache returns to the client in case of an error (see
-# http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:
-
-ErrorDocument 404 /404.html
-
-
-# ##############################################################################
-# # INTERNET EXPLORER #
-# ##############################################################################
-
-# ------------------------------------------------------------------------------
-# | Better website experience |
-# ------------------------------------------------------------------------------
-
-# Force IE to render pages in the highest available mode in the various
-# cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.
-
-
- Header set X-UA-Compatible "IE=edge"
- # `mod_headers` can't match based on the content-type, however, we only
- # want to send this header for HTML pages and not for the other resources
-
- Header unset X-UA-Compatible
-
-
-
-# ------------------------------------------------------------------------------
-# | Cookie setting from iframes |
-# ------------------------------------------------------------------------------
-
-# Allow cookies to be set from iframes in IE.
-
-#
-# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
-#
-
-# ------------------------------------------------------------------------------
-# | Screen flicker |
-# ------------------------------------------------------------------------------
-
-# Stop screen flicker in IE on CSS rollovers (this only works in
-# combination with the `ExpiresByType` directives for images from below).
-
-# BrowserMatch "MSIE" brokenvary=1
-# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
-# BrowserMatch "Opera" !brokenvary
-# SetEnvIf brokenvary 1 force-no-vary
-
-
-# ##############################################################################
-# # MIME TYPES AND ENCODING #
-# ##############################################################################
-
-# ------------------------------------------------------------------------------
-# | Proper MIME types for all files |
-# ------------------------------------------------------------------------------
-
-
-
- # Audio
- AddType audio/mp4 m4a f4a f4b
- AddType audio/ogg oga ogg
-
- # JavaScript
- # Normalize to standard type (it's sniffed in IE anyways):
- # http://tools.ietf.org/html/rfc4329#section-7.2
- AddType application/javascript js jsonp
- AddType application/json json
-
- # Video
- AddType video/mp4 mp4 m4v f4v f4p
- AddType video/ogg ogv
- AddType video/webm webm
- AddType video/x-flv flv
-
- # Web fonts
- AddType application/font-woff woff
- AddType application/vnd.ms-fontobject eot
-
- # Browsers usually ignore the font MIME types and sniff the content,
- # however, Chrome shows a warning if other MIME types are used for the
- # following fonts.
- AddType application/x-font-ttf ttc ttf
- AddType font/opentype otf
-
- # Make SVGZ fonts work on iPad:
- # https://twitter.com/FontSquirrel/status/14855840545
- AddType image/svg+xml svg svgz
- AddEncoding gzip svgz
-
- # Other
- AddType application/octet-stream safariextz
- AddType application/x-chrome-extension crx
- AddType application/x-opera-extension oex
- AddType application/x-shockwave-flash swf
- AddType application/x-web-app-manifest+json webapp
- AddType application/x-xpinstall xpi
- AddType application/xml atom rdf rss xml
- AddType image/webp webp
- AddType image/x-icon ico
- AddType text/cache-manifest appcache manifest
- AddType text/vtt vtt
- AddType text/x-component htc
- AddType text/x-vcard vcf
-
-
-
-# ------------------------------------------------------------------------------
-# | UTF-8 encoding |
-# ------------------------------------------------------------------------------
-
-# Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
-AddDefaultCharset utf-8
-
-# Force UTF-8 for certain file formats.
-
- AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml
-
-
-
-# ##############################################################################
-# # URL REWRITES #
-# ##############################################################################
-
-# ------------------------------------------------------------------------------
-# | Rewrite engine |
-# ------------------------------------------------------------------------------
-
-# Turning on the rewrite engine and enabling the `FollowSymLinks` option is
-# necessary for the following directives to work.
-
-# If your web host doesn't allow the `FollowSymlinks` option, you may need to
-# comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
-# performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
-
-# Also, some cloud hosting services require `RewriteBase` to be set:
-# http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
-
-
- Options +FollowSymlinks
- # Options +SymLinksIfOwnerMatch
- RewriteEngine On
- # RewriteBase /
-
-
-# ------------------------------------------------------------------------------
-# | Suppressing / Forcing the "www." at the beginning of URLs |
-# ------------------------------------------------------------------------------
-
-# The same content should never be available under two different URLs especially
-# not with and without "www." at the beginning. This can cause SEO problems
-# (duplicate content), therefore, you should choose one of the alternatives and
-# redirect the other one.
-
-# By default option 1 (no "www.") is activated:
-# http://no-www.org/faq.php?q=class_b
-
-# If you'd prefer to use option 2, just comment out all the lines from option 1
-# and uncomment the ones from option 2.
-
-# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
-
-# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-# Option 1: rewrite www.example.com → example.com
-
-
- RewriteCond %{HTTPS} !=on
- RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
- RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
-
-
-# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-# Option 2: rewrite example.com → www.example.com
-
-# Be aware that the following might not be a good idea if you use "real"
-# subdomains for certain parts of your website.
-
-#
-# RewriteCond %{HTTPS} !=on
-# RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
-# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
-#
-
-
-# ##############################################################################
-# # SECURITY #
-# ##############################################################################
-
-# ------------------------------------------------------------------------------
-# | Content Security Policy (CSP) |
-# ------------------------------------------------------------------------------
-
-# You can mitigate the risk of cross-site scripting and other content-injection
-# attacks by setting a Content Security Policy which whitelists trusted sources
-# of content for your site.
-
-# The example header below allows ONLY scripts that are loaded from the current
-# site's origin (no inline scripts, no CDN, etc). This almost certainly won't
-# work as-is for your site!
-
-# To get all the details you'll need to craft a reasonable policy for your site,
-# read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
-# see the specification: http://w3.org/TR/CSP).
-
-#
-# Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
-#
-# Header unset Content-Security-Policy
-#
-#
-
-# ------------------------------------------------------------------------------
-# | File access |
-# ------------------------------------------------------------------------------
-
-# Block access to directories without a default document.
-# Usually you should leave this uncommented because you shouldn't allow anyone
-# to surf through every directory on your server (which may includes rather
-# private places like the CMS's directories).
-
-
- Options -Indexes
-
-
-# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-# Block access to hidden files and directories.
-# This includes directories used by version control systems such as Git and SVN.
-
-
- RewriteCond %{SCRIPT_FILENAME} -d [OR]
- RewriteCond %{SCRIPT_FILENAME} -f
- RewriteRule "(^|/)\." - [F]
-
-
-# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-# Block access to backup and source files.
-# These files may be left by some text editors and can pose a great security
-# danger when anyone has access to them.
-
-
- Order allow,deny
- Deny from all
- Satisfy All
-
-
-# ------------------------------------------------------------------------------
-# | Secure Sockets Layer (SSL) |
-# ------------------------------------------------------------------------------
-
-# Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
-# prevent `https://www.example.com` when your certificate only allows
-# `https://secure.example.com`.
-
-#
-# RewriteCond %{SERVER_PORT} !^443
-# RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
-#
-
-# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-# Force client-side SSL redirection.
-
-# If a user types "example.com" in his browser, the above rule will redirect him
-# to the secure version of the site. That still leaves a window of opportunity
-# (the initial HTTP connection) for an attacker to downgrade or redirect the
-# request. The following header ensures that browser will ONLY connect to your
-# server via HTTPS, regardless of what the users type in the address bar.
-# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
-
-#
-# Header set Strict-Transport-Security max-age=16070400;
-#
-
-# ------------------------------------------------------------------------------
-# | Server software information |
-# ------------------------------------------------------------------------------
-
-# Avoid displaying the exact Apache version number, the description of the
-# generic OS-type and the information about Apache's compiled-in modules.
-
-# ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
-
-# ServerTokens Prod
-
-
-# ##############################################################################
-# # WEB PERFORMANCE #
-# ##############################################################################
-
-# ------------------------------------------------------------------------------
-# | Compression |
-# ------------------------------------------------------------------------------
-
-
-
- # Force compression for mangled headers.
- # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
-
-
- SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
- RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
-
-
-
- # Compress all output labeled with one of the following MIME-types
- # (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
- # and can remove the `` and `` lines
- # as `AddOutputFilterByType` is still in the core directives).
-
- AddOutputFilterByType DEFLATE application/atom+xml \
- application/javascript \
- application/json \
- application/rss+xml \
- application/vnd.ms-fontobject \
- application/x-font-ttf \
- application/x-web-app-manifest+json \
- application/xhtml+xml \
- application/xml \
- font/opentype \
- image/svg+xml \
- image/x-icon \
- text/css \
- text/html \
- text/plain \
- text/x-component \
- text/xml
-
-
-
-
-# ------------------------------------------------------------------------------
-# | Content transformations |
-# ------------------------------------------------------------------------------
-
-# Prevent some of the mobile network providers from modifying the content of
-# your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
-
-#
-# Header set Cache-Control "no-transform"
-#
-
-# ------------------------------------------------------------------------------
-# | ETag removal |
-# ------------------------------------------------------------------------------
-
-# Since we're sending far-future expires headers (see below), ETags can
-# be removed: http://developer.yahoo.com/performance/rules.html#etags.
-
-# `FileETag None` is not enough for every server.
-
- Header unset ETag
-
-
-FileETag None
-
-# ------------------------------------------------------------------------------
-# | Expires headers (for better cache control) |
-# ------------------------------------------------------------------------------
-
-# The following expires headers are set pretty far in the future. If you don't
-# control versioning with filename-based cache busting, consider lowering the
-# cache time for resources like CSS and JS to something like 1 week.
-
-
-
- ExpiresActive on
- ExpiresDefault "access plus 1 month"
-
- # CSS
- ExpiresByType text/css "access plus 1 year"
-
- # Data interchange
- ExpiresByType application/json "access plus 0 seconds"
- ExpiresByType application/xml "access plus 0 seconds"
- ExpiresByType text/xml "access plus 0 seconds"
-
- # Favicon (cannot be renamed!)
- ExpiresByType image/x-icon "access plus 1 week"
-
- # HTML components (HTCs)
- ExpiresByType text/x-component "access plus 1 month"
-
- # HTML
- ExpiresByType text/html "access plus 0 seconds"
-
- # JavaScript
- ExpiresByType application/javascript "access plus 1 year"
-
- # Manifest files
- ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
- ExpiresByType text/cache-manifest "access plus 0 seconds"
-
- # Media
- ExpiresByType audio/ogg "access plus 1 month"
- ExpiresByType image/gif "access plus 1 month"
- ExpiresByType image/jpeg "access plus 1 month"
- ExpiresByType image/png "access plus 1 month"
- ExpiresByType video/mp4 "access plus 1 month"
- ExpiresByType video/ogg "access plus 1 month"
- ExpiresByType video/webm "access plus 1 month"
-
- # Web feeds
- ExpiresByType application/atom+xml "access plus 1 hour"
- ExpiresByType application/rss+xml "access plus 1 hour"
-
- # Web fonts
- ExpiresByType application/font-woff "access plus 1 month"
- ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
- ExpiresByType application/x-font-ttf "access plus 1 month"
- ExpiresByType font/opentype "access plus 1 month"
- ExpiresByType image/svg+xml "access plus 1 month"
-
-
-
-# ------------------------------------------------------------------------------
-# | Filename-based cache busting |
-# ------------------------------------------------------------------------------
-
-# If you're not using a build process to manage your filename version revving,
-# you might want to consider enabling the following directives to route all
-# requests such as `/css/style.12345.css` to `/css/style.css`.
-
-# To understand why this is important and a better idea than `*.css?v231`, read:
-# http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
-
-#
-# RewriteCond %{REQUEST_FILENAME} !-f
-# RewriteCond %{REQUEST_FILENAME} !-d
-# RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
-#
-
-# ------------------------------------------------------------------------------
-# | File concatenation |
-# ------------------------------------------------------------------------------
-
-# Allow concatenation from within specific CSS and JS files, e.g.:
-# Inside of `script.combined.js` you could have
-#
-#
-# and they would be included into this single file.
-
-#
-#
-# Options +Includes
-# AddOutputFilterByType INCLUDES application/javascript application/json
-# SetOutputFilter INCLUDES
-#
-#
-# Options +Includes
-# AddOutputFilterByType INCLUDES text/css
-# SetOutputFilter INCLUDES
-#
-#
-
-# ------------------------------------------------------------------------------
-# | Persistent connections |
-# ------------------------------------------------------------------------------
-
-# Allow multiple requests to be sent over the same TCP connection:
-# http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.
-
-# Enable if you serve a lot of static content but, be aware of the
-# possible disadvantages!
-
-#
-# Header set Connection Keep-Alive
-#
diff --git a/html/app/favicon.ico b/html/app/favicon.ico
deleted file mode 100644
index 372714a..0000000
Binary files a/html/app/favicon.ico and /dev/null differ
diff --git a/html/app/images/logo.svg b/html/app/images/logo.svg
deleted file mode 100644
index 31e11ac..0000000
--- a/html/app/images/logo.svg
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/html/app/index.html b/html/app/index.html
deleted file mode 100644
index 6aa9128..0000000
--- a/html/app/index.html
+++ /dev/null
@@ -1,118 +0,0 @@
-
-
-
-
- hyperfox live
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- On {{details.date_start | date: 'MMM d, y H:mm:ss'}} a
- {{details.method}} request was issued to {{details.host}} by
- {{details.origin | origin}}.
-
-
- The request had a size of {{details.content_length | size}} and took
- {{details.time_taken | time}} to complete.
-
-
- The request took {{details.time_taken | time}} to complete.
-