--- /dev/null
+/*
+ *
+ * Copyright 2016 RIFT.IO Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import React from 'react';
+import { Link } from 'react-router';
+import Utils from 'utils/utils.js';
+import Crouton from 'react-crouton';
+import 'style/common.scss';
+
+//Temporary, until api server is on same port as webserver
+var rw = require('utils/rw.js');
+var API_SERVER = rw.getSearchParams(window.location).api_server;
+var UPLOAD_SERVER = rw.getSearchParams(window.location).upload_server;
+
+//
+// Internal classes/functions
+//
+
+class LogoutAppMenuItem extends React.Component {
+ handleLogout() {
+ Utils.clearAuthentication();
+ }
+ render() {
+ return (
+ <div className="app">
+ <h2>
+ <a onClick={this.handleLogout}>
+ Logout
+ </a>
+ </h2>
+ </div>
+ );
+ }
+}
+
+
+//
+// Exported classes and functions
+//
+
+//
+/**
+ * Skyquake Nav Component. Provides navigation functionality between all plugins
+ */
+export default class skyquakeNav extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {};
+ this.state.validateErrorEvent = 0;
+ this.state.validateErrorMsg = '';
+ }
+ validateError = (msg) => {
+ this.setState({
+ validateErrorEvent: true,
+ validateErrorMsg: msg
+ });
+ }
+ validateReset = () => {
+ this.setState({
+ validateErrorEvent: false
+ });
+ }
+ returnCrouton = () => {
+ return <Crouton
+ id={Date.now()}
+ message={this.state.validateErrorMsg}
+ type={"error"}
+ hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
+ onDismiss={this.validateReset}
+ />;
+ }
+ render() {
+ let html;
+ html = (
+ <div>
+ {this.returnCrouton()}
+ <nav className="skyquakeNav">
+ {buildNav.call(this, this.props.nav, this.props.currentPlugin)}
+ </nav>
+
+ </div>
+ )
+ return html;
+ }
+}
+skyquakeNav.defaultProps = {
+ nav: {}
+}
+/**
+ * Returns a React Component
+ * @param {object} link Information about the nav link
+ * @param {string} link.route Hash route that the SPA should resolve
+ * @param {string} link.name Link name to be displayed
+ * @param {number} index index of current array item
+ * @return {object} component A React LI Component
+ */
+//This should be extended to also make use of internal/external links and determine if the link should refer to an outside plugin or itself.
+export function buildNavListItem (k, link, index) {
+ let html = false;
+ if (link.type == 'external') {
+ this.hasSubNav[k] = true;
+ html = (
+ <li key={index}>
+ {returnLinkItem(link)}
+ </li>
+ );
+ }
+ return html;
+}
+
+/**
+ * Builds a link to a React Router route or a new plugin route.
+ * @param {object} link Routing information from nav object.
+ * @return {object} component returns a react component that links to a new route.
+ */
+export function returnLinkItem(link) {
+ let ref;
+ let route = link.route;
+ if(link.isExternal) {
+ ref = (
+ <a href={route}>{link.label}</a>
+ )
+ } else {
+ if(link.path && link.path.replace(' ', '') != '') {
+ route = link.path;
+ }
+ if(link.query) {
+ let query = {};
+ query[link.query] = '';
+ route = {
+ pathname: route,
+ query: query
+ }
+ }
+ ref = (
+ <Link to={route}>
+ {link.label}
+ </Link>
+ )
+ }
+ return ref;
+}
+
+/**
+ * Constructs nav for each plugin, along with available subnavs
+ * @param {array} nav List returned from /nav endpoint.
+ * @return {array} List of constructed nav element for each plugin
+ */
+export function buildNav(nav, currentPlugin) {
+ let navList = [];
+ let navListHTML = [];
+ let secondaryNav = [];
+ let self = this;
+ self.hasSubNav = {};
+ let secondaryNavHTML = (
+ <div className="secondaryNav" key="secondaryNav">
+ {secondaryNav}
+ <LogoutAppMenuItem />
+ </div>
+ )
+ for (let k in nav) {
+ if (nav.hasOwnProperty(k)) {
+ self.hasSubNav[k] = false;
+ let header = null;
+ let navClass = "app";
+ let routes = nav[k].routes;
+ let navItem = {};
+ //Primary plugin title and link to dashboard.
+ let route;
+ let NavList;
+ if (API_SERVER) {
+ route = routes[0].isExternal ? '/' + k + '/index.html?api_server=' + API_SERVER + '' + '&upload_server=' + UPLOAD_SERVER + '' : '';
+ } else {
+ route = routes[0].isExternal ? '/' + k + '/' : '';
+ }
+ let dashboardLink = returnLinkItem({
+ isExternal: routes[0].isExternal,
+ pluginName: nav[k].pluginName,
+ label: nav[k].label || k,
+ route: route
+ });
+ if (nav[k].pluginName == currentPlugin) {
+ navClass += " active";
+ }
+ NavList = nav[k].routes.map(buildNavListItem.bind(self, k));
+ navItem.priority = nav[k].priority;
+ navItem.order = nav[k].order;
+ navItem.html = (
+ <div key={k} className={navClass}>
+ <h2>{dashboardLink} {self.hasSubNav[k] ? <span className="oi" data-glyph="caret-bottom"></span> : ''}</h2>
+ <ul className="menu">
+ {
+ NavList
+ }
+ </ul>
+ </div>
+ );
+ navList.push(navItem)
+ }
+ }
+ //Sorts nav items by order and returns only the markup
+ navListHTML = navList.sort((a,b) => a.order - b.order).map(function(n) {
+ if((n.priority < 2)){
+ return n.html;
+ } else {
+ secondaryNav.push(n.html);
+ }
+ });
+ navListHTML.push(secondaryNavHTML);
+ return navListHTML;
+}