import { ApolloProvider } from "@apollo/client"
import API from "api"
import "bootstrap/dist/css/bootstrap.css"
import { jumpToTop } from "components/Page"
import "locale-compare-polyfill"
import App from "pages/App"
import React from "react"
import ReactDOM from "react-dom"
import { Provider } from "react-redux"
import { BrowserRouter, Route } from "react-router-dom"
import { persistStore } from "redux-persist"
import { PersistGate } from "redux-persist/lib/integration/react"
import "./bootstrapOverrides.css"
import "./index.css"
import configureStore from "./store/configureStore"

const store = configureStore()
const persistor = persistStore(store)

window.onerror = function(message, url, lineNumber, columnNumber) {
  API.logOnServer("ERROR", url, lineNumber + ":" + columnNumber, message)
  return false
}

ReactDOM.render(
  <Provider store={store}>
    <PersistGate persistor={persistor}>
      <ApolloProvider client={API.client}>
        <BrowserRouter onUpdate={jumpToTop}>
          <Route path="/" component={App} />
        </BrowserRouter>
      </ApolloProvider>
    </PersistGate>
  </Provider>,
  document.getElementById("root")
)

import { ApolloProvider } from "@apollo/client"
import API from "api"
import "bootstrap/dist/css/bootstrap.css"
import { jumpToTop } from "components/Page"
import "locale-compare-polyfill"
import App from "pages/App"
import React from "react"
import { createRoot } from "react-dom/client"
import { Provider } from "react-redux"
import { BrowserRouter, Route } from "react-router-dom"
import { persistStore } from "redux-persist"
import { PersistGate } from "redux-persist/lib/integration/react"
import "./bootstrapOverrides.css"
import "./index.css"
import configureStore from "./store/configureStore"

const store = configureStore()
const persistor = persistStore(store)

window.onerror = function(message, url, lineNumber, columnNumber) {
  API.logOnServer("ERROR", url, lineNumber + ":" + columnNumber, message)
  return false
}

const root = createRoot(document.getElementById("root"))

root.render(
  <Provider store={store}>
    <PersistGate persistor={persistor}>
      <ApolloProvider client={API.client}>
        <BrowserRouter onUpdate={jumpToTop}>
          <Route path="/" component={App} />
        </BrowserRouter>
      </ApolloProvider>
    </PersistGate>
  </Provider>
)

Use new react root api


import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { browserHistory, Router } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import { configure } from 'redux-auth';
import routes from './routes';
import registerServiceWorker from './registerServiceWorker';
import './stylesheets/main.css';

import configureStore from './redux/configureStore';

const store = configureStore();
const history = syncHistoryWithStore(browserHistory, store);

store.dispatch(configure(
  {
    apiUrl: 'http://localhost:3001',
    authProviderPaths: {
      facebook: '/auth/facebook',
      google: '/auth/google_oauth2'
    }
  },
  {
    isServer: false,
    cookies: document.cookie,
    currentLocation: window.location.href,
    cleanSession: true,
    clientOnly: true
  }
)).then(() => {
  render((
    <Provider store={store}>
      <Router history={history} routes={routes} />
    </Provider>
  ), document.getElementById('root'));
  registerServiceWorker();
});

import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import { browserHistory, Router } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import { configure } from 'redux-auth';
import routes from './routes';
import registerServiceWorker from './registerServiceWorker';
import './stylesheets/main.css';

import configureStore from './redux/configureStore';

const store = configureStore();
const history = syncHistoryWithStore(browserHistory, store);

store.dispatch(configure(
  {
    apiUrl: process.env.REACT_APP_GOOGLE_CALLBACK_URL || 'http://localhost:3001',
    authProviderPaths: {
      facebook: '/auth/facebook',
      google: '/auth/google_oauth2'
    }
  },
  {
    isServer: false,
    cookies: document.cookie,
    currentLocation: window.location.href,
    cleanSession: true,
    clientOnly: true
  }
)).then(() => {
  render((
    <Provider store={store}>
      <Router history={history} routes={routes} />
    </Provider>
  ), document.getElementById('root'));
  registerServiceWorker();
});

Add alternate callback for Heroku


import needle from 'needle';
import { SiteConf, getDate } from 'base';

export const postsApiHandler = (req, res)  => {
  needle('get', SiteConf.PostsApi)
    .then(resp => {
      const data = resp.body;
      const filter = req.params.filter;
      const pagination = data.meta.pagination;
      const posts = PostList(data.posts, filter);
      const result = { posts, pagination };
      res.json(result);
    })
    .catch(err => {
      console.log(333, err);
      res.status(500).json(err);
    });
};

export const PostList = (posts, filter) => {
  return posts.filter((post) => {
    const reg = new RegExp(`^(.+?)${ SiteConf.postOpeningSplitChar }`);
    const result = reg.exec(post.html);
    if (result) post.opening = result[1];
    else {
      let i = 0;
      let max = SiteConf.postOpeningChars;
      const words = post.html.split(' ');
      post.opening = '';
      for (i; i <= max ; i++) {
        post.opening += `${words[i]} `;
      }
      post.opening += '...</p>';

    }
    post.html = null;
    post.markdown = null;
    post.published_at = getDate(post.published_at);
    if (filter && post.tags[0]) {
      if (post.tags[0].slug === filter.split(':')[1]) return post;
      else return false;
    }
    else return post;
  }
  );
};

import needle from 'needle';
import { SiteConf, getDate } from 'base';

export const postsApiHandler = (req, res)  => {
  needle('get', SiteConf.PostsApi)
    .then(resp => {
      const data = resp.body;
      const filter = req.params.filter;
      const pagination = data.meta.pagination;
      const posts = PostList(data.posts, filter);
      const result = { posts, pagination };
      res.json(result);
    })
    .catch(err => {
      console.log(333, err);
      res.status(500).json(err);
    });
};

export const PostList = (posts, filter) => {
  return posts.filter((post) => {
    const reg = new RegExp(`^(.+?)${ SiteConf.postOpeningSplitChar }`);
    const result = reg.exec(post.html);
    if (result) post.opening = result[1];
    else {
      let i = 0;
      let max = SiteConf.postOpeningChars;
      const words = post.html.split(' ');
      post.opening = '';
      for (i; i <= max ; i++) {
        post.opening += `${words[i]} `;
      }
      post.opening += '...</p>';

    }
    post.html = null;
    post.markdown = null;
    post.published_at = getDate(post.published_at);
    if (filter) {
      if (post.tags[0] && post.tags[0].slug === filter.split(':')[1]) return post;
      else return false;
    }
    else return post;
  }
  );
};

Fix issue with post with no tag


import debugModule from "debug";
const debug = debugModule("debugger:store:common");
const reduxDebug = debugModule("debugger:redux");

import { compose, createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import createLogger from "redux-cli-logger";

export default function configureStore (reducer, saga, initialState, composeEnhancers) {
  const sagaMiddleware = createSagaMiddleware();

  if (!composeEnhancers) {
    composeEnhancers = compose;
  }

  const loggerMiddleware = createLogger({
    log: reduxDebug,
    stateTransformer: (state) => ({
      ...state,

      context: {
        list: ["..."],
        indexForAddress: {"...": "..."},
        indexForBinary: {"...": "..."},
      },

      trace: {
        ...state.trace,
        steps: ["..."]
      }
    })
  });

  let store = createStore(
    reducer, initialState,

    composeEnhancers(
      applyMiddleware(
        sagaMiddleware,
        loggerMiddleware
      )
    )
  );

  sagaMiddleware.run(saga);

  return store;
}

import debugModule from "debug";
const debug = debugModule("debugger:store:common");
const reduxDebug = debugModule("debugger:redux");

import { compose, createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import createLogger from "redux-cli-logger";

export default function configureStore (reducer, saga, initialState, composeEnhancers) {
  const sagaMiddleware = createSagaMiddleware();

  if (!composeEnhancers) {
    composeEnhancers = compose;
  }

  const loggerMiddleware = createLogger({
    log: reduxDebug,
    stateTransformer: (state) => ({
      ...state,

      data: {
        ...Object.assign(
          {},
          ...Object.entries(state.data)
            .map( ([context, scope]) => ({
              [context]: {
                ...Object.assign(
                  {},
                  ...Object.entries(scope)
                    .filter( ([id, {variables}]) => variables && variables.length > 0 )
                    .map(
                      ([id, {variables}]) => ({
                        [id]: (variables || []).map( (v) => v.name )
                      })
                    )
                )
              }
            }))
        )
      },

      context: {
        list: ["..."],
        indexForAddress: {"...": "..."},
        indexForBinary: {"...": "..."},
      },

      trace: {
        ...state.trace,
        steps: ["..."]
      }
    })
  });

  let store = createStore(
    reducer, initialState,

    composeEnhancers(
      applyMiddleware(
        sagaMiddleware,
        loggerMiddleware
      )
    )
  );

  sagaMiddleware.run(saga);

  return store;
}

Format data smaller in logger


//jQuery all client side
$(document).ready(function(){
	$('#txt').bind('keypress', function(e) {
		if(e.keyCode==13){
			console.log('test');
			$.getJSON('/check_code', {
		      text :  $('textarea#txt').val()
		    }, function(data) {
		    	appendGutter();
		    	$('#append_text').append("<h1>" + data + "</h1>");
		    	return false;
		    });

		  };
	});
});

//Track number of rows. The number of the current row would be used to append to certain element.
function appendGutter() {
	var textArea = document.getElementById("txt");
	var arrayOfLines = textArea.value.split("\n");
    var line_number = textArea.value.substr(0, textArea.selectionStart).split("\n").length;
    console.log(arrayOfLines[line_number-2]);
    console.log(line_number);
}

//jQuery all client side
$(document).ready(function(){
	$('#txt').bind('keypress', function(e) {
		if(e.keyCode==13){
			console.log('test');
			$.getJSON('/check_code', {
		      text :  $('textarea#txt').val()
		    }, function(data) {
		    	current_text = appendGutter(data);
		    	$('#append_text').append("<h1>" + current_text + "</h1>");
		    	return false;
		    });

		  };
	});
});

//Track number of rows. The number of the current row would be used to append to certain element.
function appendGutter(data) {
	var textArea = data;
	var arrayOfLines = textArea.split("\n");
    var line_number = textArea.substr(0, textArea.selectionStart).split("\n").length;
    if (line_number==1){
    	var current_text = arrayOfLines[0];
    } else {
    	var current_text = arrayOfLines[line_number-1];
    }
    return current_text
}

Append text based on current_line number


import React, { PropTypes } from 'react';
import { FormattedTime } from 'react-intl';
import { epochToDate } from '../_utils/DateUtils';
import { M } from '../_common';

const PurchaseConfirmation = ({ receipt }) => (

						{receipt.longcode}

					<M m="Purchase Price" />
					{receipt.buy_price}

					<M m="Purchase Time" />

					<FormattedTime value={epochToDate(receipt.purchase_time)} format="full" />

					<M m="Balance" />
					{receipt.balance_after}

		<button><M m="Back" /></button>

);

PurchaseConfirmation.propTypes = {
	proposal: PropTypes.object,
};

export default PurchaseConfirmation;

import React, { PropTypes } from 'react';
import { FormattedTime } from 'react-intl';
import { epochToDate } from '../_utils/DateUtils';
import { M, NumberPlain } from '../_common';

const PurchaseConfirmation = ({ receipt }) => (

				{`Contract Ref. ${receipt.contract_id}`}

						{receipt.longcode}

					<M m="Purchase Price" />
					{receipt.buy_price}

					<M m="Purchase Time" />

					<FormattedTime value={epochToDate(receipt.purchase_time)} format="full" />

					<M m="Balance" />

						<NumberPlain value={receipt.balance_after} />

		<button><M m="Back" /></button>

);

PurchaseConfirmation.propTypes = {
	proposal: PropTypes.object,
};

export default PurchaseConfirmation;

Fix purchase confirmation decimal points


'use strict';

module.exports = function(driver, connectorSpec) {
	connectorSpec.shouldBehaveLikeMusicSite(driver, {
		url: 'http://hypem.com/artist/Violet+Days+x+Win+and+Woo',
		playButtonSelector: '.tools .playdiv'
	});
};

'use strict';

module.exports = function(driver, connectorSpec) {
	connectorSpec.shouldBehaveLikeMusicSite(driver, {
		url: 'http://hypem.com/artist/Violet+Days+x+Win+and+Woo',
		playButtonSelector: '#playerPlay'
	});
};

Update play button selector for Hype Machine test.


const {i18n, React} = Serverboards
import Details from '../containers/details'

class DetailsTab extends React.Component{
  constructor(props){
    super(props)

    this.state={
      tab: "details"
    }
  }
  render(){
    let Section = () => null
    const section = this.state.tab

    switch(section){
      case "details":
        Section=Details
        break;
    }

    return (

          <a className={`item ${section == "details" ? "active" : ""}`}
             data-tooltip={i18n("Details")}
             data-position="left center"
             onClick={() => this.setState({tab:"details"})}
             >

          <a className={`item ${section == "edit" ? "active" : ""}`}
             data-tooltip={i18n("Edit")}
             data-position="left center"
             onClick={() => this.setState({tab:"edit"})}
             >

          <a className={`item ${section == "logs" ? "active" : ""}`}
             data-tooltip={i18n("Logs")}
             data-position="left center"
             onClick={() => this.setState({tab:"logs"})}
             >

    )
  }
}

export default DetailsTab

const {i18n, React} = Serverboards
import Details from '../containers/details'

class DetailsTab extends React.Component{
  constructor(props){
    super(props)

    this.state={
      tab: "details"
    }
  }
  render(){
    let Section = () => null
    const section = this.state.tab

    switch(section){
      case "details":
        Section=Details
        break;
    }

    return (

          <a className={`item ${section == "details" ? "active" : ""}`}
             data-tooltip={i18n("Details")}
             data-position="left center"
             onClick={() => this.setState({tab:"details"})}
             >

          <a className={`item ${section == "ssh" ? "active" : ""}`}
             data-tooltip={i18n("SSH Remote Terminal")}
             data-position="left center"
             onClick={() => this.setState({tab:"ssh"})}
             >

          <a className={`item ${section == "remote_desktop" ? "active" : ""}`}
             data-tooltip={i18n("Remote Desktop")}
             data-position="left center"
             onClick={() => this.setState({tab:"remote_desktop"})}
             >

    )
  }
}

export default DetailsTab

Use proper placeholders for future functionalities


var Promise = require("bluebird");

module.exports = (response, body) => {
  var outResponse = {
    statusCode: response.statusCode,
    headers: response.headers,
    body: body
  };

  if (response.statusCode != 200) {
    var errorResponse = outResponse;
    if (/\bjson\b/.test(response.headers['content-type'])) {
      var responseBody = JSON.parse(body);
      errorResponse.errorCode = responseBody.errorCode;
      errorResponse.message = responseBody.message;
      errorResponse.refId = responseBody.refId;

      if (responseBody.detail !== undefined) {
        errorResponse.detail = responseBody.detail;
      }
    } else {
      errorResponse.message = body;
    }

    return new Promise.reject(errorResponse);
  } else if (response.headers['content-type'] === 'application/json;charset=UTF-8') {
    outResponse.content = JSON.parse(body);
    return outResponse;
  } else {
    outResponse.content = body;
    return outResponse;
  }
};

var Promise = require("bluebird");

module.exports = (response, body) => {
  var outResponse = {
    statusCode: response.statusCode,
    headers: response.headers,
    body: body
  };

  if (response.statusCode != 200) {
    var errorResponse = outResponse;
    if (/\bapplication\/json\b/.test(response.headers['content-type'])) {
      var responseBody = JSON.parse(body);
      errorResponse.errorCode = responseBody.errorCode;
      errorResponse.message = responseBody.message;
      errorResponse.refId = responseBody.refId;

      if (responseBody.detail !== undefined) {
        errorResponse.detail = responseBody.detail;
      }
    } else {
      errorResponse.message = body;
    }

    return new Promise.reject(errorResponse);
  } else if (response.headers['content-type'] === 'application/json;charset=UTF-8') {
    outResponse.content = JSON.parse(body);
    return outResponse;
  } else {
    outResponse.content = body;
    return outResponse;
  }
};

Change content type check to check for 'application/json' not 'json'


/* global SW:true */

$(document).ready(function(){
  'use strict';
  console.log( 'Doing SW things!' );
  var card = new SW.Card();
  var helpdesk = card.services('helpdesk');
  helpdesk
    .request('tickets')
    .then( function(data){
      console.log( 'got data!' );
      var ticketCount = {};
      $.each(data.tickets, function(index, ticket){
        console.log( index );
        if (ticketCount[ticket.assignee.id]){
          ticketCount[ticket.assignee.id] += 1;
        } else {
          ticketCount[ticket.assignee.id] = 1;
        }
        console.log( 'ticketCount object' );
        console.log( ticketCount );
      });
    });
});

/* global SW:true */

$(document).ready(function(){
  'use strict';
  console.log( 'Doing SW things!' );
  var card = new SW.Card();
  var helpdesk = card.services('helpdesk');
  var assignmentCount = {};
  helpdesk
    .request('tickets')
    .then( function(data){
      console.log( 'got data!' );
      $.each(data.tickets, function(index, ticket){
        console.log( ticket.assignee.id );
        if (assignmentCount[ticket.assignee.id]){
          assignmentCount[ticket.assignee.id] += 1;
        } else {
          assignmentCount[ticket.assignee.id] = 1;
        }
        console.log( 'assignmentCount object' );
        console.log( assignmentCount );
      });

      console.log( assignmentCount + 'final' );
      var ticketTotal = 0;
      for (var property in assignmentCount) {
          ticketTotal += assignmentCount[property];
      }
      var unassignedTickets = data.tickets.length - ticketTotal;
      console.log('unassignedTickets');
      console.log(unassignedTickets);
    });

});

Troubleshoot script for displaying getting the counts of tickets assigned to each user.


import formatDistance from './_lib/formatDistance/index'
import formatLong from './_lib/formatLong/index'
import formatRelative from './_lib/formatRelative/index'
import localize from './_lib/localize/index'
import match from './_lib/match/index'

/**
 * @type {Locale}
 * @category Locales
 * @summary Albanian locale.
 * @language Shqip
 * @author Ardit Dine [@arditdine]{@link https://github.com/arditdine}
 */
var locale = {
  code: 'sq',
  formatDistance: formatDistance,
  formatLong: formatLong,
  formatRelative: formatRelative,
  localize: localize,
  match: match,
  options: {
    weekStartsOn: 1 /* Monday */,
    firstWeekContainsDate: 1
  }
}

export default locale

import formatDistance from './_lib/formatDistance/index'
import formatLong from './_lib/formatLong/index'
import formatRelative from './_lib/formatRelative/index'
import localize from './_lib/localize/index'
import match from './_lib/match/index'

/**
 * @type {Locale}
 * @category Locales
 * @summary Albanian locale.
 * @language Shqip
 * @iso-639-2 sqi
 * @author Ardit Dine [@arditdine]{@link https://github.com/arditdine}
 */
var locale = {
  code: 'sq',
  formatDistance: formatDistance,
  formatLong: formatLong,
  formatRelative: formatRelative,
  localize: localize,
  match: match,
  options: {
    weekStartsOn: 1 /* Monday */,
    firstWeekContainsDate: 1,
  },
}

export default locale

Add ISO-639-2 code to Albanian locale


var chai = require('chai');
var sinonChai = require('sinon-chai');
var sdk = require('flowxo-sdk');

chai.use(sinonChai);
chai.use(sdk.Chai);

chai.should();
chai.config.includeStack = true;

global.expect = chai.expect;
global.AssertionError = chai.AssertionError;
global.Assertion = chai.Assertion;
global.assert = chai.assert;
global.sinon = require('sinon');

var chai = require('chai');
var sinonChai = require('sinon-chai');
var sdk = require('flowxo-sdk');

chai.use(sinonChai);
chai.use(sdk.Chai);

chai.should();

// Don't truncate assertion display:
// allows us to view the full error message
// when a spec fails
chai.config.truncateThreshold = 0;

// Show error stack on failed spec
chai.config.includeStack = true;

global.expect = chai.expect;
global.AssertionError = chai.AssertionError;
global.Assertion = chai.Assertion;
global.assert = chai.assert;
global.sinon = require('sinon');

Remove chai truncation to read error messages on spec failure


import classNames from 'classnames';
import React, { PropTypes } from 'react';

const HeaderView = ({
    items,
    githubLink,
    onLabelClick
}) => (<div className="github-embed-nav">
    {items.map(({ shown, label }, index) =>
        <a
            className={classNames({
                'github-embed-nav-link': true,
                'github-embed-nav-link-shown': shown
            })}
            key={index}
            onClick={() => onLabelClick(index)}
        >
            {label}
        </a>
    )}

    <a
        className="github-embed-nav-on-github"
        target="_blank"
        rel="noopener noreferrer"
        href={githubLink}
    >
        On Github
    </div>
</nav>);

HeaderView.propTypes = {
    items: PropTypes.arrayOf(PropTypes.shape({
        label: PropTypes.string.isRequired,
        shown: PropTypes.bool.isRequired
    })),
    onLabelClick: PropTypes.func.isRequired,
    githubLink: PropTypes.string.isRequired
};

export default HeaderView;

import classNames from 'classnames';
import React, { PropTypes } from 'react';

const HeaderView = ({
    items,
    githubLink,
    onLabelClick
}) => (<div className="github-embed-nav">
    {items.map(({ shown, label }, index) =>
        <a
            className={classNames({
                'github-embed-nav-link': true,
                'github-embed-nav-link-shown': shown
            })}
            key={index}
            onClick={() => onLabelClick(index)}
        >
            {label}
        </a>
    )}

    <a
        className="github-embed-nav-on-github"
        target="_blank"
        rel="noopener noreferrer"
        href={githubLink}
    >
        On Github
    </a>
</div>);

HeaderView.propTypes = {
    items: PropTypes.arrayOf(PropTypes.shape({
        label: PropTypes.string.isRequired,
        shown: PropTypes.bool.isRequired
    })),
    onLabelClick: PropTypes.func.isRequired,
    githubLink: PropTypes.string.isRequired
};

export default HeaderView;

Fix syntax errors made in previous commit


import { h, Component } from 'preact';
import Dropzone from 'react-dropzone';
import style from './style';

export default class FileUpload extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isFileSelected: false,
      file: false,
    };

    this.handleFileDrop = this.handleFileDrop.bind(this);
  }

  handleFileDrop(acceptedFiles, rejectedFiles) {
    this.setState({
      ...this.state,
      isFileSelected: true,
      file: acceptedFiles[0],
    });
    this.props.confirmChooseFile(this.state.file);
  }

  render(props, state) {
    return (
      <div class={`${style['file-upload']} container`}>

          <Dropzone
            onDrop={this.handleFileDrop}
            multiple={false}
            class={style['file']}
            className={style['file']}
            accept=".stl"
          >

Přidejte model ve formátu STL přetáhnutím souboru nebo kliknutím

          </Dropzone>

        {/*<div class={`one column offset-by-two ${style['button']}`}>*/}
        {/*<button class="button-primary disabled" disabled={!state.isFileSelected} onClick={this.handleAnalyzeButtonClick} type="button">Go to details</button>*/}
        {/*
*/}

    );
  }
}

import { h, Component } from 'preact';
import Dropzone from 'react-dropzone';
import style from './style';

export default class FileUpload extends Component {
  constructor(props) {
    super(props);

    this.dropzoneRef = null;
    this.state = {
      isFileSelected: false,
      file: false,
    };

    this.handleFileDrop = this.handleFileDrop.bind(this);
  }

  handleFileDrop(acceptedFiles, rejectedFiles) {
    this.setState({
      ...this.state,
      isFileSelected: true,
      file: acceptedFiles[0],
    });
    this.props.confirmChooseFile(this.state.file);
  }

  render(props, state) {
    return (
      <div class={`${style['file-upload']} container`}>

          <Dropzone
            onDrop={this.handleFileDrop}
            multiple={false}
            class={style['file']}
            className={style['file']}
            accept=".stl"
            ref={(node) => { this.dropzoneRef = node; }}
          >

Přidejte model ve formátu STL přetáhnutím souboru nebo kliknutím

          </Dropzone>

        {/*<div class={`one column offset-by-two ${style['button']}`}>*/}
        {/*<button class="button-primary disabled" disabled={!state.isFileSelected} onClick={this.handleAnalyzeButtonClick} type="button">Go to details</button>*/}
        {/*
*/}

    );
  }
}

Add ref for file upload dropZone


import React from 'react';
import { storiesOf } from '@storybook/react';
import { text, select } from '@storybook/addon-knobs';
import OptionsHelper from '../../utils/helpers/options-helper';
import notes from './notes.md';
import Help from './help';

storiesOf('Help', module)
  .add('default', () => {
    const children = text('children', 'This is help text');
    const tooltipPosition = children ? select(
      'tooltipPosition',
      OptionsHelper.positions,
      Help.defaultProps.tooltipPosition
    ) : undefined;
    const tooltipAlign = children ? select(
      'tooltipAlign',
      OptionsHelper.alignAroundEdges,
      Help.defaultProps.tooltipAlign
    ) : undefined;
    const href = text('href', '');

    return (
      <Help
        tooltipPosition={ tooltipPosition }
        tooltipAlign={ tooltipAlign }
        href={ href }
      >
        {children}
      </Help>
    );
  }, {
    notes: { markdown: notes }
  });

import React from 'react';
import { storiesOf } from '@storybook/react';
import { text, select } from '@storybook/addon-knobs';
import OptionsHelper from '../../utils/helpers/options-helper';
import notes from './notes.md';
import Help from './help';

storiesOf('Help', module)
  .add('default', () => {
    const children = text('children', 'This is help text');
    const tooltipPosition = children ? select(
      'tooltipPosition',
      OptionsHelper.positions,
      'right'
    ) : undefined;
    const tooltipAlign = children ? select(
      'tooltipAlign',
      OptionsHelper.alignAroundEdges,
      Help.defaultProps.tooltipAlign
    ) : undefined;
    const href = text('href', '');

    return (
      <Help
        tooltipPosition={ tooltipPosition }
        tooltipAlign={ tooltipAlign }
        href={ href }
      >
        {children}
      </Help>
    );
  }, {
    notes: { markdown: notes }
  });

Update Help Props So Popup Remains On-Screen


import {BaseModel} from 'nxus-storage'

export default BaseModel.extend({
  attributes: {
    role: {
      model: 'users-role'
    },
    object: {
      model: 'subclass-must-define'
    },
    user: {
      model: 'users-user'
    }
  }
})

import {BaseModel} from 'nxus-storage'

export default BaseModel.extend({
  attributes: {
    role: {
      model: 'users-role'
    },
    object: {
      model: 'subclass-must-define'
    },
    user: {
      model: 'users-user'
    }
  },

  roleUsers(object) {
    return this.find({object: object}).populate('role').populate('user').then((roles) => {
      let ret = {}
      for (let r of roles) {
        let name = r.role.role
        if (!ret[name]) {
          ret[name] = []
        }
        ret[name].push(r.user)
      }
      return ret
    })
  }
})

Add ObjectRoleModel.roleUsers for user/role mapping


/**
 * Project: masterroot24.github.io
 */

$(document).ready(function () {

    /* Add the current year to the footer */
    function getYear() {
        var d = new Date();
        var n = d.getFullYear();
        return n.toString();
    }
    $('#year').html(getYear());

    /* Enable Bootstrap tooltips */
    $("body").tooltip({ selector: '[data-toggle=tooltip]' });

    /* Avatar Hover Zoom */
    $("#about-avatar").hover(function () {
            $(this).switchClass('avatar-md', 'avatar-lg');
        },
        function () {
            $(this).switchClass('avatar-lg', 'avatar-md');
        }
    );

    /* goBack() function */
    function goBack() {
        window.history.back()
    }

});

Remove unnecessary comment from Javascript file


/*
 * Author:        Pierre-Henry Soria <hello@ph7cms.com>
 * Copyright:     (c) 2015-2019, Pierre-Henry Soria. All Rights Reserved.
 * License:       GNU General Public License; See PH7.LICENSE.txt and PH7.COPYRIGHT.txt in the root directory.
 */

var $suggestionBox = (function () {
    $.get(pH7Url.base + 'ph7cms-helper/main/suggestionbox', function (oData) {
        $.colorbox({
            width: '240px',
            height: '235px',
            speed: 500,
            scrolling: false,
            html: $(oData).find('#box_block')
        })
    })
});

var $amendBoxBgColor = (function () {
    var aHexColors = [
        '#ffffff',
        '#eceff1',
        '#ffdcd8',
        '#d1dce5',
        '#f9fbe7',
        '#ffe0b2',
        '#ffecb3',
        '#fff9c4',
        '#ffccbc',
        '#e0f7fa',
        '#fce4ec',
        '#b2dfdb'
    ];

    var sRandomHexColor = aHexColors[Math.floor((Math.random() * aHexColors.length))];
    $('#cboxContent').css('background-color', sRandomHexColor + ' !important')
});

$(document).ready(function () {
    $suggestionBox();
    $amendBoxBgColor();
});

/*
 * Author:        Pierre-Henry Soria <hello@ph7cms.com>
 * Copyright:     (c) 2015-2020, Pierre-Henry Soria. All Rights Reserved.
 * License:       GNU General Public License; See PH7.LICENSE.txt and PH7.COPYRIGHT.txt in the root directory.
 */

var $suggestionBox = (function () {
    $.get(pH7Url.base + 'ph7cms-helper/main/suggestionbox', function (oData) {
        $.colorbox({
            width: '240px',
            height: '240px',
            speed: 500,
            scrolling: false,
            html: $(oData).find('#box_block')
        })
    })
});

var $amendBoxBgColor = (function () {
    var aHexColors = [
        '#ffffff',
        '#eceff1',
        '#ffdcd8',
        '#d1dce5',
        '#f9fbe7',
        '#ffe0b2',
        '#ffecb3',
        '#fff9c4',
        '#ffccbc',
        '#e0f7fa',
        '#fce4ec',
        '#b2dfdb'
    ];

    var sRandomHexColor = aHexColors[Math.floor((Math.random() * aHexColors.length))];
    $('#cboxContent').css('background-color', sRandomHexColor + ' !important')
});

$(document).ready(function () {
    $suggestionBox();
    $amendBoxBgColor();
});

Make the suggestion colorbox a square (increased the height)


"use strict";

var arrays       = require("../../utils/arrays"),
    GrammarError = require("../../grammar-error"),
    asts         = require("../asts"),
    visitor      = require("../visitor");

/*
 * Reports left recursion in the grammar, which prevents infinite recursion in
 * the generated parser.
 *
 * Both direct and indirect recursion is detected. The pass also correctly
 * reports cases like this:
 *
 *   start = "a"? start
 *
 * In general, if a rule reference can be reached without consuming any input,
 * it can lead to left recursion.
 */
function reportLeftRecursion(ast) {
  var check = visitor.build({
    rule: function(node, visitedRules) {
      check(node.expression, visitedRules.concat(node.name));
    },

    sequence: function(node, visitedRules) {
      arrays.every(node.elements, function(element) {
        if (element.type === "rule_ref") {
          check(element, visitedRules);
        }

        return !asts.alwaysAdvancesOnSuccess(ast, element);
      });
    },

    rule_ref: function(node, visitedRules) {
      if (arrays.contains(visitedRules, node.name)) {
        throw new GrammarError(
          "Left recursion detected for rule \"" + node.name + "\".",
          node.location
        );
      }

      check(asts.findRule(ast, node.name), visitedRules);
    }
  });

  check(ast, []);
}

module.exports = reportLeftRecursion;

"use strict";

var arrays       = require("../../utils/arrays"),
    GrammarError = require("../../grammar-error"),
    asts         = require("../asts"),
    visitor      = require("../visitor");

/*
 * Reports left recursion in the grammar, which prevents infinite recursion in
 * the generated parser.
 *
 * Both direct and indirect recursion is detected. The pass also correctly
 * reports cases like this:
 *
 *   start = "a"? start
 *
 * In general, if a rule reference can be reached without consuming any input,
 * it can lead to left recursion.
 */
function reportLeftRecursion(ast) {
  var visitedRules = [];

  var check = visitor.build({
    rule: function(node) {
      visitedRules.push(node.name);
      check(node.expression);
      visitedRules.pop(node.name);
    },

    sequence: function(node) {
      arrays.every(node.elements, function(element) {
        if (element.type === "rule_ref") {
          check(element);
        }

        return !asts.alwaysAdvancesOnSuccess(ast, element);
      });
    },

    rule_ref: function(node) {
      if (arrays.contains(visitedRules, node.name)) {
        throw new GrammarError(
          "Left recursion detected for rule \"" + node.name + "\".",
          node.location
        );
      }

      check(asts.findRule(ast, node.name));
    }
  });

  check(ast);
}

module.exports = reportLeftRecursion;

Change handling of the |visitedRules| array


var banner = $("#homepage-banner");

var imageNames = ['bot', 'soccer', 'teamCrop', 'working'];
var imageIndex = Math.floor((Math.random() * imageNames.length));
var imageUrl = "assets/images/homepage-banners/" + imageNames[imageIndex] + ".JPG";

$( document ).ready(function(){
  banner.css('background-image', 'url(' + imageUrl + ')');
});

var banner = $("#homepage-banner");

var imageNames = ['bot', 'soccer', 'teamCrop', 'working'];
var imageIndex = Math.floor((Math.random() * imageNames.length));
var imageUrl = "assets/images/homepage-banners/" + imageNames[imageIndex] + ".jpg";

banner.css('background-image', 'url(' + imageUrl + ')');

Fix delayed load and capital image extension


import co from 'co';
import test from 'blue-tape';
import agent from 'promisify-supertest';

import createApplication from '../src';

const setup = () => {
  return agent(createApplication());
};

test('GET /', (sub) => {
  sub.test('responds with OK status code', co.wrap(function* (assert) {
    const app = setup();

    yield app
    .get('/')
    .expect((response) => {
      assert.equal(response.statusCode, 200, 'status code should be 200');
    })
    .end();
  }));
});

import co from 'co';
import test from 'blue-tape';
import agent from 'promisify-supertest';

import createApplication from '../src';

const setup = () => {
  return agent(createApplication().callback());
};

test('GET /', (sub) => {
  sub.test('responds with OK status code', co.wrap(function* (assert) {
    const app = setup();

    yield app
    .get('/')
    .expect((response) => {
      assert.equal(response.statusCode, 200, 'status code should be 200');
    })
    .end();
  }));
});

Fix agent not finding address of app err.


'use strict';

const ContentNode = function (context) {

  const self = this;
  const api = require('../api');
  const config = require('../config');

  this.uuid = context.content.uuid;
  this.document = context.content;

  this.children = [];

  if (this.document.children && this.document.children.length === this.document.itemCount) {
    this.document.children.forEach(child => {
      self.children.push(new ContentNode({ content: child }));
    });
  }

  this.getChildren = () => {
    if (this.document.itemCount != this.children.length) {
      return api.getContentNode(this.uuid)
      .then(that => {
        self.document = that.document;
        self.children = that.children;

        return self.children;
      });
    } else {
      return Promise.resolve(this.children);
    }
  };

  this.getUrl = () => {
    return config.host + '/api/delivery' + escape(this.document.path);
  };

};

module.exports = ContentNode;

'use strict';

const ContentNode = function (context) {

  const self = this;
  const api = require('../api');
  const config = require('../config');

  this.uuid = context.content.uuid;
  this.document = context.content;

  this.children = [];

  if (this.document.children && this.document.children.length === this.document.itemCount) {
    this.document.children.forEach(child => {
      self.children.push(new ContentNode({ content: child }));
    });
  }

  this.getChildren = () => {
    if (this.document.itemCount != this.children.length) {
      return api.getContentNode(this.uuid)
      .then(that => {
        self.document = that.document;
        self.children = that.children;

        return self.children;
      });
    } else {
      return Promise.resolve(this.children);
    }
  };

  this.getUrl = () => {
    return config.host + '/api/delivery' + escape(this.document.path);
  };

  this.getVariantUrl = name => {
    const query = '?variant=' + encodeURIComponent(name);
    return config.host + '/api/delivery' + escape(this.document.path) + query;
  };

};

module.exports = ContentNode;

Add getVariantUrl to content node.


import DS from "ember-data";
import Ember from 'ember';

var singularize = Ember.String.singularize;
var camelize = Ember.String.camelize;

export default DS.RESTSerializer.extend({

  normalize: function(type, hash, prop) {
    var links = hash.links;
    for (var key in links) {
      var linkedData = links[key];
      if (linkedData.href) {
        hash[key] = linkedData.href;
      } else if (linkedData.ids) {
        hash[key] = linkedData.ids;
      } else {
        hash[key + '_id'] = linkedData.id;
      }
    }
    delete hash.links;
    return this._super(type, hash, prop);
  },

  normalizePayload: function(payload) {
    if (payload.linked) {
      var store = Ember.get(this, 'store');
      this.pushPayload(store, payload.linked);
      delete payload.linked;
    }
    return this._super(payload);
  },

  keyForRelationship: function(key, relationship) {
    if (relationship === 'belongsTo') {
      return key + '_id';
    }
    return key;
  }
});

import DS from "ember-data";
import Ember from 'ember';

var dasherize = Ember.String.dasherize;
var pluralize = Ember.String.pluralize;

export default DS.RESTSerializer.extend({

  normalize: function(type, hash, prop) {
    var links = hash.links;
    for (var key in links) {
      var linkedData = links[key];
      if (linkedData.href) {
        hash[key] = linkedData.href;
      } else if (linkedData.ids) {
        hash[key] = linkedData.ids;
      } else {
        hash[key + '_id'] = linkedData.id;
      }
    }
    delete hash.links;
    return this._super(type, hash, prop);
  },

  normalizePayload: function(payload) {
    if (payload.linked) {
      var store = Ember.get(this, 'store');
      this.pushPayload(store, payload.linked);
      delete payload.linked;
    }
    return this._super(payload);
  },

  keyForRelationship: function(key, relationship) {
    if (relationship === 'belongsTo') {
      return key + '_id';
    }
    return key;
  },

  serializeIntoHash: function(data, type, record, options) {
    var root = dasherize(pluralize(type.typeKey));
    data[root] = this.serialize(record, options);
  }
});

Make Serializer work with nest API


var messenger = require('../lib/messenger');
var session = require('../lib/session');

module.exports = function (server) {

  server.post('/messages', function (req, res, next) {
    var phoneNumber = req.params.From;
    var message = req.params.Body.toLowerCase();

    if (message === 'flush') {
      session.flushall(function () {
        messenger.send(phoneNumber, 'The database has been flushed.');
      });
    }

    session.get(phoneNumber, function(err, user) {
      if (err) {
        messenger.fail(phoneNumber);
        res.send(500);
      }
      if (user) {
        messenger.send(phoneNumber, 'Hello, old friend. (' + user + ')' );
      } else {
        session.set(phoneNumber, 'initial', function () {
          messenger.send(phoneNumber, 'Nice to meet you.');
        });
      }
    });

    res.send(200, {status: 'ok'});
  });

}

var messenger = require('../lib/messenger');
var session = require('../lib/session');

module.exports = function (server) {

  server.post('/messages', function (req, res, next) {
    var phoneNumber = req.params.From;
    var message = req.params.Body.toLowerCase();

    if (message === 'flush') {
      session.flushall(function () {
        messenger.send(phoneNumber, 'The database has been flushed.');
      });
      res.send(200);
      return;
    }

    session.get(phoneNumber, function(err, user) {
      if (err) {
        messenger.fail(phoneNumber);
        res.send(500);
      }
      if (user) {
        messenger.send(phoneNumber, 'Hello, old friend. (' + user + ')' );
      } else {
        session.set(phoneNumber, 'initial', function () {
          messenger.send(phoneNumber, 'Nice to meet you.');
        });
      }
    });

    res.send(200, {status: 'ok'});
  });

}

Return after flushing the database


'use strict';

/**
 * @ngdoc directive
 * @name angular-uploadcare.directive:UploadCare
 * @description Provides a directive for the Uploadcare widget.
 * # UploadCare
 */
angular.module('ng-uploadcare', [])
  .directive('uploadcareWidget', function () {
    return {
      restrict: 'E',
      replace: true,
      require: 'ngModel',
      template: '<input type="hidden" role="ng-uploadcare-uploader" />',
      scope: {
        onWidgetReady: '&',
        onUploadComplete: '&',
        onChange: '&',
      },
      controller: ['$scope', '$element', '$log', function($scope, $element, $log) {
        if(!uploadcare) {
          $log.error('Uploadcare script has not been loaded!.');
          return;
        }
        $scope.widget = uploadcare.Widget($element);
        $scope.onWidgetReady({widget: $scope.widget});
        $scope.widget.onUploadComplete(function(info) {
          $scope.onUploadComplete({info: info});
        });
        $scope.widget.onChange(function(file) {
          $scope.onChange({file: file});
        })
      }]
    };
  });

'use strict';

/**
 * @ngdoc directive
 * @name angular-uploadcare.directive:UploadCare
 * @description Provides a directive for the Uploadcare widget.
 * # UploadCare
 */
angular.module('ng-uploadcare', [])
  .directive('uploadcareWidget', function () {
    return {
      restrict: 'E',
      replace: true,
      require: 'ngModel',
      template: '<input type="hidden" role="ng-uploadcare-uploader" />',
      scope: {
        onWidgetReady: '&',
        onUploadComplete: '&',
        onChange: '&',
      },
      controller: ['$scope', '$element', '$log', function($scope, $element, $log) {
        if(!uploadcare) {
          $log.error('Uploadcare script has not been loaded!.');
          return;
        }
        $scope.widget = uploadcare.Widget($element);
        $scope.onWidgetReady({widget: $scope.widget});
        $scope.widget.onUploadComplete(function(info) {
          $scope.onUploadComplete({info: info});
        });
        $scope.widget.onChange(function(file) {
          $scope.$apply(function () {
            $parse($attrs.ngModel).assign($scope.$parent, $element.val());
          });
          $scope.onChange({file: file});
        })
      }]
    };
  });

Make ngModel for hidden input field work with parent scope


goog.require('app');

goog.provide('app.constants');

/**
 * @const
 * Constants for the module.
 * Access them like app.constants.SCREEN
 */
app.module.constant('constants', app.constants);

app.constants = {
  SCREEN : {
    SMARTPHONE : 620,
    TABLET : 1099,
    DEKTOP : 1400
  },
  STEPS : {
    'climbing_outdoor' : 4,
    'climbing_indoor' :  4,
    'hut' : 4,
    'gite' : 4,
    'shelter' : 4,
    'access' : 4,
    'camp_site' : 4,
    'local_product' : 4,
    'paragliding_takeoff' : 4,
    'paragliding_landing' : 4,
    'webcam': 4
  },
  REQUIRED_FIELDS : {
    waypoints: ['title' , 'lang', 'waypoint_type', 'elevation', 'longitude', 'latitude'],
    routes : ['title' , 'lang', 'activities', 'waypoints'],
    outings : ['title' , 'lang', 'date_start', 'routes', 'activities'],
    images: ['image_type']
  },
  documentEditing: {
    FORM_PROJ: 'EPSG:4326',
    DATA_PROJ: 'EPSG:3857'
  }
};

goog.require('app');

goog.provide('app.constants');

/**
 * @const
 * Constants for the module.
 * Access them like app.constants.SCREEN
 */
app.module.constant('constants', app.constants);

app.constants = {
  SCREEN: {
    SMARTPHONE : 620,
    TABLET : 1099,
    DEKTOP : 1400
  },
  STEPS: {
    'climbing_outdoor' : 4,
    'climbing_indoor' :  4,
    'hut' : 4,
    'gite' : 4,
    'shelter' : 4,
    'access' : 4,
    'camp_site' : 4,
    'local_product' : 4,
    'paragliding_takeoff' : 4,
    'paragliding_landing' : 4,
    'webcam': 4
  },
  REQUIRED_FIELDS: {
    'waypoints': ['title' , 'lang', 'waypoint_type', 'elevation', 'longitude', 'latitude'],
    'routes' : ['title' , 'lang', 'activities', 'waypoints'],
    'outings' : ['title' , 'lang', 'date_start', 'routes', 'activities'],
    'images': ['image_type']
  },
  documentEditing: {
    FORM_PROJ: 'EPSG:4326',
    DATA_PROJ: 'EPSG:3857'
  }
};

Make sure constant name is not renamed


import {
  parse,
  visit,
  visitWithTypeInfo,
  TypeInfo
} from 'graphql';

export default function parseQueryDocument(queryDocument, schema) {
  const typeInfo = new TypeInfo(schema);

  const ast = parse(queryDocument);

  function source(location) {
    return queryDocument.slice(location.start, location.end);
  }

  return visit(ast, visitWithTypeInfo(typeInfo, {
    leave: {
      Name: node => node.value,
      Document: node => node.definitions,
      OperationDefinition: ({ loc, name, operation, variableDefinitions, selectionSet }) => {
        return { name, operation, source: source(loc), variableDefinitions, selectionSet };
      },
      VariableDefinition: node => {
        const type = typeInfo.getInputType();
        return { name: node.variable, type: type };
      },
      Variable: node => node.name,
      SelectionSet: ({ selections }) => selections,
      Field: ({ kind, alias, name, arguments: args, directives, selectionSet }) => {
        const type = typeInfo.getType();
        return { kind, alias, name, type: type, selectionSet: selectionSet ? selectionSet : undefined }
      }
    }
  }));
}

import {
  parse,
  validate,
  visit,
  visitWithTypeInfo,
  TypeInfo
} from 'graphql';

export default function parseQueryDocument(queryDocument, schema) {
  const ast = parse(queryDocument);

  const validationErrors = validate(schema, ast);
  if (validationErrors && validationErrors.length > 0) {
    for (const error of validationErrors) {
      const location = error.locations[0];
      console.log(`graphql:${location.line}: error: ${error.message}`);
    }
    throw Error("Validation of GraphQL query document failed");
  }

  const typeInfo = new TypeInfo(schema);

  function source(location) {
    return queryDocument.slice(location.start, location.end);
  }

  return visit(ast, visitWithTypeInfo(typeInfo, {
    leave: {
      Name: node => node.value,
      Document: node => node.definitions,
      OperationDefinition: ({ loc, name, operation, variableDefinitions, selectionSet }) => {
        return { name, operation, source: source(loc), variableDefinitions, selectionSet };
      },
      VariableDefinition: node => {
        const type = typeInfo.getInputType();
        return { name: node.variable, type: type };
      },
      Variable: node => node.name,
      SelectionSet: ({ selections }) => selections,
      Field: ({ kind, alias, name, arguments: args, directives, selectionSet }) => {
        const type = typeInfo.getType();
        return { kind, alias, name, type: type, selectionSet: selectionSet ? selectionSet : undefined }
      }
    }
  }));
}

Validate query document and output Xcode compatible errors


var assertionsModifier = require('../src/modifiers/assertions');
var testsModifier = require('../src/modifiers/tests');
var assert = require('assert');
var fs = require('fs');
var noOfData = 2;

var oneToTenArray = [];
for(var i = 1; i <= noOfData; i++) {
	oneToTenArray.push(i);
}

var testData = oneToTenArray.map(function (i) {
	return fs.readFileSync('tests/data/' + i + '/actual.js').toString();
})

var expectedData = oneToTenArray.map(function (i) {
	return fs.readFileSync('tests/data/' + i + '/expected.js').toString();
})

describe('Assertions modules', function () {
	it('should have a working assertion modifier', function () {
		for(var i = 0; i < 1; i++) {
			var result = assertionsModifier(testData[i]);
			assert.equal(result, expectedData[i]);
		}
	});
});

describe('Tests working', function () {
	it('should have a workign tests modifier', function () {
		for(var i = 1; i < 2; i++) {
			var result = testsModifier(testData[i]);
			assert.equal(result, expectedData[i]);
		}
	});
});

var assertionsModifier = require('../src/modifiers/assertions');
var testsModifier = require('../src/modifiers/tests');
var globalsModifier = require('../src/modifiers/globals');
var assert = require('assert');
var fs = require('fs');
var noOfData = 3;

var oneToTenArray = [];
for(var i = 1; i <= noOfData; i++) {
	oneToTenArray.push(i);
}

var testData = oneToTenArray.map(function (i) {
	return fs.readFileSync('tests/data/' + i + '/actual.js').toString();
})

var expectedData = oneToTenArray.map(function (i) {
	return fs.readFileSync('tests/data/' + i + '/expected.js').toString();
})

describe('Assertions modules', function () {
	it('should have a working assertion modifier', function () {
		for(var i = 0; i < 1; i++) {
			var result = assertionsModifier(testData[i]);
			assert.equal(result, expectedData[i]);
		}
	});
});

describe('Tests working', function () {
	it('should have a workign tests modifier', function () {
		for(var i = 1; i < 2; i++) {
			var result = testsModifier(testData[i]);
			assert.equal(result, expectedData[i]);
		}
	});
});

describe('Globals working', function () {
	it('should have a workign tests modifier', function () {
		for(var i = 2; i < 3; i++) {
			var result = globalsModifier(testData[i]);
			assert.equal(result, expectedData[i]);
		}
	});
});

Add test module for globals


// Copyright (c) 2017 The Regents of the University of Michigan.
// All Rights Reserved. Licensed according to the terms of the Revised
// BSD License. See LICENSE.txt for details.

const log_tree = require("../lib/log_tree");

describe("a log_tree object", function() {
  var tree;

  describe("when created from nothing", function() {
    beforeEach(function() {
      tree = log_tree();
    });

    it("has a length of 0", function() {
      expect(tree.length).toBe(0);
    });

    it("has a name of ''", function() {
      expect(tree.name).toBe("");
    });

    it("remembers a name change", function() {
      tree.name = "matt";
      expect(tree.name).toBe("matt");
    });

    it("doesn't remember a length change", function() {
      tree.length = 1;
      expect(tree.length).toBe(0);
    });
  });

  describe("when created with a name", function() {
    it("remembers its name", function() {
      tree = log_tree("name");
      expect(tree.name).toBe("name");
    });
  });
});

// Copyright (c) 2017 The Regents of the University of Michigan.
// All Rights Reserved. Licensed according to the terms of the Revised
// BSD License. See LICENSE.txt for details.

const log_tree = require("../lib/log_tree");

describe("a log_tree object", function() {
  var tree;

  describe("when created from nothing", function() {
    beforeEach(function() {
      tree = log_tree();
    });

    it("has a length of 0", function() {
      expect(tree.length).toBe(0);
    });

    it("has a name of ''", function() {
      expect(tree.name).toBe("");
    });

    it("remembers a name change", function() {
      tree.name = "matt";
      expect(tree.name).toBe("matt");
    });

    it("doesn't remember a length change", function() {
      tree.length = 1;
      expect(tree.length).toBe(0);
    });
  });

  describe("when created with a name", function() {
    beforeEach(function() {
      tree = log_tree("name");
    });

    it("remembers its name", function() {
      expect(tree.name).toBe("name");
    });
  });
});

Move setup code to beforeEach


/* @flow */
import React, { PureComponent } from 'react';
import { Text } from 'react-native';
import { FormattedMessage } from 'react-intl';

import type { LocalizableText, Style } from '../types';

type Props = {
  text: LocalizableText,
  style?: Style,
};

export default class Label extends PureComponent<Props> {
  static contextTypes = {
    styles: () => null,
  };

  props: Props;

  render() {
    const { text, style, ...restProps } = this.props;
    const message = text.text || text;
    const { styles } = this.context;

    return (
      <Text style={[styles.label, style]} {...restProps}>
        <FormattedMessage id={message} defaultMessage={message} values={text.values} />
      </Text>
    );
  }
}

/* @flow */
import React, { PureComponent } from 'react';
import { Text } from 'react-native';
import TranslatedText from './TranslatedText';

import type { LocalizableText, Style } from '../types';

type Props = {
  text: LocalizableText,
  style?: Style,
};

export default class Label extends PureComponent<Props> {
  static contextTypes = {
    styles: () => null,
  };

  props: Props;

  render() {
    const { styles } = this.context;
    const { text, style, ...restProps } = this.props;

    return (
      <Text style={[styles.label, style]} {...restProps}>
        <TranslatedText text={text} />
      </Text>
    );
  }
}

Use TranslatedText component to get translated text.


import {
  applyMiddleware,
  combineReducers,
  createStore,
} from 'redux';
import thunk from 'redux-thunk';
import enhancer from './storeEnhancer';
import { composeWithDevTools } from 'redux-devtools-extension';

export default (preloadedState, preloadedReducers) => createStore(
  preloadedReducers ? combineReducers(preloadedReducers) : () => ({}),
  preloadedState,
  composeWithDevTools(
    applyMiddleware(thunk),
    enhancer(preloadedReducers),
  ),
);

import {
  applyMiddleware,
  combineReducers,
  createStore,
} from 'redux';
import thunk from 'redux-thunk';
import enhancer from './storeEnhancer';
import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction';

export default (preloadedState, preloadedReducers) => createStore(
  preloadedReducers ? combineReducers(preloadedReducers) : () => ({}),
  preloadedState,
  composeWithDevTools(
    applyMiddleware(thunk),
    enhancer(preloadedReducers),
  ),
);

Update Redux DevTools to log only in production env


import FormControlsAbstractSelectComponent from './abstract-select';
import { action } from '@ember/object';
import { arg } from 'ember-arg-types';
import { string, bool } from 'prop-types';
import { get } from '@ember/object';
import { A } from '@ember/array';

export default class FormControlsFfCheckboxSelectComponent extends FormControlsAbstractSelectComponent {
  @arg(string)
  for = 'id';

  @arg
  value = [];

  @arg(bool)
  showClearAll = false;

  get _showClearAll() {
    return this.showClearAll && this.value.length > 0;
  }

  @action
  idFor(item) {
    return `${this.for}-${this.isPrimitive ? item : get(item, this.idKey)}`;
  }

  @action
  labelFor(item) {
    return this.isPrimitive ? item : get(item, this.labelKey);
  }

  @action
  handleChange(value) {
    if (this.isSelected(value)) {
      this.args.onChange(this.value.filter((_) => !this._compare(_, value)));
    } else {
      this.args.onChange(A(this.value).toArray().concat(value));
    }
  }

  @action
  isSelected(value) {
    return !!this.value.find((_) => this._compare(_, value));
  }

  @action
  clearAll() {
    return this.args.onChange([]);
  }

  _compare(a, b) {
    return this.isPrimitive ? a === b : get(a, this.idKey) === get(b, this.idKey);
  }
}

import FormControlsAbstractSelectComponent from './abstract-select';
import { action } from '@ember/object';
import { arg } from 'ember-arg-types';
import { string, bool } from 'prop-types';
import { get } from '@ember/object';
import { A } from '@ember/array';
import { guidFor } from '@ember/object/internals';

export default class FormControlsFfCheckboxSelectComponent extends FormControlsAbstractSelectComponent {
  @arg(string)
  for = 'id';

  @arg
  value = [];

  @arg(bool)
  showClearAll = false;

  get _showClearAll() {
    return this.showClearAll && this.value.length > 0;
  }

  @action
  idFor(item) {
    return `${this.for}-${this.isPrimitive ? item : get(item, this.idKey)}-${guidFor(this)}`;
  }

  @action
  labelFor(item) {
    return this.isPrimitive ? item : get(item, this.labelKey);
  }

  @action
  handleChange(value) {
    if (this.isSelected(value)) {
      this.args.onChange(this.value.filter((_) => !this._compare(_, value)));
    } else {
      this.args.onChange(A(this.value).toArray().concat(value));
    }
  }

  @action
  isSelected(value) {
    return !!this.value.find((_) => this._compare(_, value));
  }

  @action
  clearAll() {
    return this.args.onChange([]);
  }

  _compare(a, b) {
    return this.isPrimitive ? a === b : get(a, this.idKey) === get(b, this.idKey);
  }
}

Add guid to checkbox select


export default {
	fetch(url) {
		return fetch(url, {
			headers: {
				accept: "application/json",
				authorization: "Basic " + btoa("nick:Nicholson")
			}
		}).then(response => response.json())
		  .then(function(data) {
			  console.log(data);
			  return data;
		  })
	}
}

export default {
	fetch(url) {
		return fetch(url, {
			credentials: "same-origin",
			headers: {
				accept: "application/json"
			}
		}).then(response => response.json())
		  .then(function(data) {
			  console.log(data);
			  return data;
		  })
	}
}

Send browser credentials with fetch instead of hard coding them


function replace_t_co_links() {
    $('.twitter-timeline-link').each(function(){

        if (!$(this).attr('data-url-expanded')) {
        data_expanded_url = $(this).attr('data-expanded-url');

        if (data_expanded_url) {
        $(this).attr('href', data_expanded_url);
        $(this).attr('data-url-expanded', '1');
            $('> .js-display-url', this).html(data_expanded_url); }}
    });
}

$(document).ready(function(){
    replace_t_co_links();
    console.error("Voila");
    var observer = new MutationObserver(function(mutations){
        replace_t_co_links();
    });

    var config = { attributes: true, childList: true, characterData: true, subtree: true };
    observer.observe(document.body, config);

});

function replace_t_co_links() {
    $('.twitter-timeline-link').each(function(){

        if (!$(this).attr('data-url-expanded')) {
        data_expanded_url = $(this).attr('data-expanded-url');

        if (data_expanded_url) {
        $(this).attr('href', data_expanded_url);
        $(this).attr('data-url-expanded', '1');
            $('> .js-display-url', this).html(data_expanded_url); }}
    });
}

$(document).ready(function(){
    replace_t_co_links();

    var observer = new MutationObserver(function(mutations){
        replace_t_co_links();
    });

    var config = { attributes: true, childList: true, characterData: true, subtree: true };
    observer.observe(document.body, config);

});

Remove a debug console error statement


var CURRENT_USER = {}

var CHAT_CHANNEL = "global";
location.search.slice(1).split("&").forEach(function(item){
    var param = item.split("=")[0];
    if (param == "name" || param == "uid") {
        CURRENT_USER[param] = item.split("=")[1];
    } else if (param == "channel") {
        CHAT_CHANNEL = item.split("=")[1];
    }
});
if (CURRENT_USER.name === undefined || CURRENT_USER.uid === undefined) {
    CURRENT_USER = undefined;
}

export {CHAT_CHANNEL, CURRENT_USER}

var CURRENT_USER = {}

var CHAT_CHANNEL = "global";
location.search.slice(1).split("&").forEach(function(item){
    var param = item.split("=")[0];
    if (param == "name" || param == "uid") {
        CURRENT_USER[param] = decodeURIComponent(item.split("=")[1]);
    } else if (param == "channel") {
        CHAT_CHANNEL = decodeURIComponent(item.split("=")[1]);
    }
});
if (CURRENT_USER.name === undefined || CURRENT_USER.uid === undefined) {
    CURRENT_USER = undefined;
}

export {CHAT_CHANNEL, CURRENT_USER}

Use decodeURIComponent when getting username param from iframe


import ButtonView from './button_view'
import React from 'react'
import classnames from 'classnames'
import styles from '../../assets/stylesheets/styles.scss'

export default class GameOverView extends React.PureComponent {
  render () {
    const bus = this.props.bus

    return (

Game Over

          <ButtonView onClick={() => bus.emit('restart')}>Restart</ButtonView>

    )
  }
}

import ButtonView from './button_view'
import React from 'react'
import classnames from 'classnames'
import styles from '../../assets/stylesheets/styles.scss'

export default class GameOverView extends React.PureComponent {
  render () {
    const bus = this.props.bus

    return (

Game Over

          <ButtonView onClick={() => bus.emit('restart')}>Play Again</ButtonView>

    )
  }
}

Change 'restart' to 'play again'


import strings from "../locales/locales";

/**
 * Returns localized strings based on 'locale' setting
 */
export function localizeStrings(state, props) {
  var lang = state.settings.locale || "en";
  strings.setLanguage(lang);
  return strings;
};

import strings from "../locales/locales";

/**
 * Returns localized strings based on 'locale' setting
 */
export function localizeStrings(state, props) {
  var lang = state.locale || state.settings.locale || "en";
  strings.setLanguage(lang);
  return strings;
};

Set the selector to look for a dynamically-set locale first, then the setting, then default to English


$( document ).ready(function() {
    console.log( "document loaded" );

startgame();
//addactiveclass();
});

function startgame (){
    start = $('.start-game');
    start.click(function(){
        console.log("Start game");
        start.toggleClass('pause');
        //start.toggleText('Pause');
        addactiveclass();
    });
}

function addactiveclass (){
    circle = $('.circle');
    console.log(Math.floor(Math.random()*4));

   num = Math.floor(Math.random()*4)

    if (num == 0) {

    } else if (num == 1) {

        circle.toggleClass("active");
    } else if (num == 2) {

    } else if (num == 3) {

    }
    $(".circle:eq(" + Math.floor(Math.random()*4) + ")").trigger('click');
}

Add start function and random math function


Package.describe({
  summary: 'JS simulation of MongoDB ObjectIDs',
  version: '1.0.7',
  documentation: null
});

Package.onUse(function (api) {
  api.export('MongoID');
  api.use(['ejson', 'id-map', 'random', 'ecmascript']);
  api.mainModule('id.js');
});

Package.describe({
  summary: 'JS simulation of MongoDB ObjectIDs',
  version: '1.0.7',
  documentation: null
});

Package.onUse(function (api) {
  api.export('MongoID');
  api.use(['ejson', 'random', 'ecmascript']);
  api.mainModule('id.js');
});

Remove id-map dependency from mongo-id


var Game = Game || {};

Game.SnakeGame = function(width, height, options) {
  'use strict';
  this.width = width;
  this.height = height;
  this.blockWidth = options.blockWidth || 20;
  this.blockHeight = options.blockHeight || 20;
  var columns = Math.round(this.width / this.blockWidth);
  var rows = Math.round(this.height / this.blockHeight);
  this.grid = new Array(columns);
  this.grid.forEach(function(g) {
    g = new Array(rows);
  });
};

Game.SnakeGame.prototype.DIRECTIONS = {
  UP: 'up',
  RIGHT: 'right',
  DOWN: 'down',
  LEFT: 'left'
};

Game.SnakeGame.prototype.update = function(inputHandler) {
  'use strict';
};

Game.SnakeGame.prototype.render = function(context) {
  'use strict';
  context.beginPath();
  context.fillStyle = '#000000';
  context.fillRect(0, 0, this.width, this.height);
  context.closePath();
};

var Game = Game || {};

Game.SnakeGame = function(width, height, options) {
  'use strict';
  this.width = width;
  this.height = height;
  this.blockWidth = options.blockWidth || 20;
  this.blockHeight = options.blockHeight || 20;
  var columns = Math.round(this.width / this.blockWidth);
  var rows = Math.round(this.height / this.blockHeight);
  var midCol = Math.floor(columns / 2);
  var midRow = Math.floor(rows / 2);
  this.snake = [[midCol, midRow], [midCol - 1, midRow], [midCol - 2, midRow]];
};

Game.SnakeGame.prototype.DIRECTIONS = {
  UP: 'up',
  RIGHT: 'right',
  DOWN: 'down',
  LEFT: 'left'
};

Game.SnakeGame.prototype.blockPosition = function(col, row) {
  'use strict';
  return {
    x: col * this.blockWidth,
    y: row * this.blockHeight
  };
};

Game.SnakeGame.prototype.update = function(inputHandler) {
  'use strict';
};

Game.SnakeGame.prototype.render = function(context) {
  'use strict';
  var coordinates;
  var self = this;

  context.beginPath();
  context.fillStyle = '#000000';
  context.fillRect(0, 0, this.width, this.height);
  context.closePath();

  context.beginPath();
  this.snake.forEach(function(s) {
    coordinates = self.blockPosition(s[0], s[1]);
    context.fillStyle = '#FFFFFF';
    context.fillRect(coordinates.x, coordinates.y, self.blockWidth,
                 self.blockHeight);
    context.lineWidth = "1";
    context.strokeStyle = '#000000';
    context.strokeRect(coordinates.x, coordinates.y, self.blockWidth,
                      self.blockHeight);
  });
  context.closePath();
};

Set up "grid" and create snake


import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { visit, click } from '@ember/test-helpers';

import { setupPretender } from 'skylines/tests/helpers/setup-pretender';

module('Acceptance | login', function(hooks) {
  setupApplicationTest(hooks);
  setupPretender(hooks);

  const LOGIN_DROPDOWN = '[data-test-login-dropdown]';
  const LOGIN_DROPDOWN_TOGGLE = `${LOGIN_DROPDOWN} a`;
  const LOGIN_EMAIL = '[data-test-input="login-email"]';

  test('login dropdown form stays visible when fields are focused', async function(assert) {
    await visit('/');

    await click(LOGIN_DROPDOWN_TOGGLE);
    assert.dom(LOGIN_DROPDOWN).hasClass('open');

    await click(LOGIN_EMAIL);
    assert.dom(LOGIN_DROPDOWN).hasClass('open');
  });
});

import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { visit, click } from '@ember/test-helpers';

import { setupPolly } from 'skylines/tests/helpers/setup-polly';

module('Acceptance | login', function(hooks) {
  setupApplicationTest(hooks);
  setupPolly(hooks, { recordIfMissing: false });

  const LOGIN_DROPDOWN = '[data-test-login-dropdown]';
  const LOGIN_DROPDOWN_TOGGLE = `${LOGIN_DROPDOWN} a`;
  const LOGIN_EMAIL = '[data-test-input="login-email"]';

  test('login dropdown form stays visible when fields are focused', async function(assert) {
    await visit('/');

    await click(LOGIN_DROPDOWN_TOGGLE);
    assert.dom(LOGIN_DROPDOWN).hasClass('open');

    await click(LOGIN_EMAIL);
    assert.dom(LOGIN_DROPDOWN).hasClass('open');
  });
});

Use Polly.js instead of Pretender


var RippleAddress = require('../models/ripple_address');

function respondToValidationErrors(req, res) {
	var errors = req.validationErrors();
	if (errors) {
		res.end({ error: util.inspect(errors) }, 400)
		return;
	}
}

module.exports = (function(){
  function userIndex(req, res) {
    RippleAddress.findAll({ where: { user_id: req.params.id }}).complete(function(err, addresses) {
      res.send({ success: true, ripple_addresses: addresses });
    })
	}

  function create(req, res) {
		req.checkBody('userId', 'Invalid bankAccountId')
			.notEmpty().isInt();
		req.checkBody('rippleAddress', 'Invalid currency')
			.notEmpty().isAlpha();
		req.checkBody('cashAmount', 'Invalid cashAmount')
			.notEmpty().isFloat();

		respondToValidationErrors(req, res);

		RippleAddress.create({
			user_id: req.body.userId,
			address: req.body.rippleAddress
		})
		.success(function(address){
			res.send(address);
		})
		.error(function(err){
			res.send({ error: err });
		});
	}

  function index(req,res) {
		RippleAddress.findAll()
		.success(function(addresses){
			res.send(addresses);
		})
		.error(function(err){
			res.send({ error: err });
		});
  }

  return {
		userIndex: userIndex,
		create: create,
		index: index
  }
})();

var RippleAddress = require('../models/ripple_address');

module.exports = (function(){
  function create(req, res) {
    req.validate('user_id', 'isInt');
    req.validate('ripple_address', 'isAlpha');
		req.validate('cash_amount', 'isFloat');

    if (req.user.admin || (req.user.id == req.body.user_id)) {
      RippleAddress.create({
        user_id: req.body.userId,
        address: req.body.rippleAddress
      }).complete(function(err, address){
        res.send({ ripple_address: address });
      });
    } else {
      res.status(401);
      res.send('Unauthorized');
    }
	}

  function index(req,res) {
    if (req.user.admin) {
      RippleAddress.findAll().complete(function(err, addresses) {
        res.send({ ripple_addresses: addresses });
      });
    } else {
      RippleAddress.findAll({ where: { user_id: req.user.id }})
        .complete(function(err, addresses) {
          res.send({ ripple_addresses: addresses });
      });
    }
  }

  return {
		create: create,
		index: index
  }
})();

Use new validations, clean up ripple addresses express controller.


"use strict";

define(["create-react-class", "prop-types", "react-dom-factories"],
   function(createReactClass, PropTypes, DOM)
   {
      var AgentLabel = createReactClass(
      {
         render: function()
         {
            var firstAgentToken = DOM.div(
            {
               className: (this.props.isFirstAgent ? "" : "dn"),
            }, DOM.img(
            {
               className: "dtc tc",
               src: this.props.resourceBase + "token/FirstPlayerToken32.png",
            }));
            var label = DOM.div(
            {
               key: "agentLabel",
               className: "b dtc f5 tc v-mid",
            }, "Agent: " + this.props.agentName);
            var threat = DOM.div(
            {
               key: "agentThreat",
               className: "b dtc f5 tc v-mid",
            }, "Threat Level: " + this.props.threatLevel);

            var panel = DOM.div(
            {
               key: "panel",
               className: "bg-lotr-dark dt lotr-light w-100",
            }, firstAgentToken, label, threat);

            return DOM.div(
            {
               key: "agentLabel",
            }, panel);
         },
      });

      AgentLabel.propTypes = {
         agentName: PropTypes.string.isRequired,
         isFirstAgent: PropTypes.bool.isRequired,
         resourceBase: PropTypes.string.isRequired,
         threatLevel: PropTypes.number.isRequired,
      };

      return AgentLabel;
   });

"use strict";

define(["create-react-class", "prop-types", "react-dom-factories"],
   function(createReactClass, PropTypes, DOM)
   {
      var AgentLabel = createReactClass(
      {
         render: function()
         {
            var firstAgentToken = DOM.div(
            {
               className: (this.props.isFirstAgent ? "" : "dn"),
               title: "First Player",
            }, DOM.img(
            {
               className: "dtc tc",
               src: this.props.resourceBase + "token/FirstPlayerToken32.png",
            }));
            var label = DOM.div(
            {
               key: "agentLabel",
               className: "b dtc f5 tc v-mid",
            }, "Agent: " + this.props.agentName);
            var threat = DOM.div(
            {
               key: "agentThreat",
               className: "b dtc f5 tc v-mid",
            }, "Threat Level: " + this.props.threatLevel);

            var panel = DOM.div(
            {
               key: "panel",
               className: "bg-lotr-dark dt lotr-light w-100",
            }, firstAgentToken, label, threat);

            return DOM.div(
            {
               key: "agentLabel",
            }, panel);
         },
      });

      AgentLabel.propTypes = {
         agentName: PropTypes.string.isRequired,
         isFirstAgent: PropTypes.bool.isRequired,
         resourceBase: PropTypes.string.isRequired,
         threatLevel: PropTypes.number.isRequired,
      };

      return AgentLabel;
   });

Put a tool tip on the first player token.


/**
 * Module dependencies.
 */

var Promise = require('bluebird');
var yaml    = require('js-yaml');
var _       = require('lodash');
var path    = require('path');
var logger  = require('winston');
var fs      = Promise.promisifyAll(require('fs'));

/*
 * Export the data object.
 */

module.exports = {

    /*
     * Load data from file, optionally with a set of defaults applied.
     * Returns a promise.
     *
     * @api private
     */

    load: function(filePath, defaults){
        defaults = defaults || {};
        var pathInfo = path.parse(path.resolve(filePath));
        var ext = pathInfo.ext.toLowerCase();

        if (ext == '.js') {
            try {
                var data = Promise.resolve(require(filePath));
            } catch(e) {
                var data = Promise.reject(e);
            }
        } else {
            var data = fs.readFileAsync(filePath).then(function(contents){
                switch(ext) {
                    case ".json":
                        return JSON.parse(contents);
                    break;
                    case ".yml":
                    case ".yaml":
                        return yaml.safeLoad(contents);
                    break;
                }
            });
        }

        return data.catch(function(e){
            logger.error('Error loading data file ' + filePath + ': ' + e.message);
            return {};
        }).then(function(data){
            return _.defaultsDeep(data, defaults);
        });

    }

};

/**
 * Module dependencies.
 */

var Promise = require('bluebird');
var yaml    = require('js-yaml');
var _       = require('lodash');
var path    = require('path');
var logger  = require('winston');
var fs      = Promise.promisifyAll(require('fs'));

/*
 * Export the data object.
 */

module.exports = {

    /*
     * Load data from file, optionally with a set of defaults applied.
     * Returns a promise.
     *
     * @api private
     */

    load: function(filePath, defaults){
        defaults = defaults || {};
        var pathInfo = path.parse(path.resolve(filePath));
        var ext = pathInfo.ext.toLowerCase();

        if (ext == '.js') {
            try {
                delete require.cache[require.resolve(filePath)]; // Always fetch a fresh copy
                var data = Promise.resolve(require(filePath));
            } catch(e) {
                var data = Promise.reject(e);
            }
        } else {
            var data = fs.readFileAsync(filePath).then(function(contents){
                switch(ext) {
                    case ".json":
                        return JSON.parse(contents);
                    break;
                    case ".yml":
                    case ".yaml":
                        return yaml.safeLoad(contents);
                    break;
                }
            });
        }

        return data.catch(function(e){
            logger.error('Error loading data file ' + filePath + ': ' + e.message);
            return {};
        }).then(function(data){
            return _.defaultsDeep(data, defaults);
        });

    }

};

Fix js config caching issue


const DocumentedItem = require('./item');

class DocumentedVarType extends DocumentedItem {
	registerMetaInfo(data) {
		this.directData = data;
	}

	serialize() {
		const names = [];
		for(const name of this.directData.names) names.push(this.constructor.splitVarName(name));
		return { types: names };
	}

	static splitVarName(str) {
		if(str === '*') return ['*', ''];
		const matches = str.match(/([\w]+)([^\w]+)/g);
		const output = [];
		if(matches) {
			for(const match of matches) {
				const groups = match.match(/([\w]+)([^\w]+)/);
				output.push([groups[1], groups[2]]);
			}
		} else {
			output.push([str.match(/(\w+)/g)[0], '']);
		}
		return output;
	}
}

/*
{
  "names":[
    "String"
  ]
}
*/

module.exports = DocumentedVarType;

const DocumentedItem = require('./item');

class DocumentedVarType extends DocumentedItem {
	registerMetaInfo(data) {
		this.directData = data;
	}

	serialize() {
		const names = [];
		for(const name of this.directData.names) names.push(this.constructor.splitVarName(name));
		return { types: names };
	}

	static splitVarName(str) {
		if(str === '*') return ['*'];
		const matches = str.match(/([\w]+)([^\w]+)/g);
		const output = [];
		if(matches) {
			for(const match of matches) {
				const groups = match.match(/([\w]+)([^\w]+)/);
				output.push([groups[1], groups[2]]);
			}
		} else {
			output.push([str.match(/(\w+)/g)[0]]);
		}
		return output;
	}
}

/*
{
  "names":[
    "String"
  ]
}
*/

module.exports = DocumentedVarType;

Remove second type element if empty


'use babel'

import Path from 'path'
import Less from 'less'

export const minifier = false
export const compiler = true
export function process(contents, {relativePath, absolutePath}, {state, config}) {
  state.ext = 'css'
  return Less.render(contents, Object.assign({}, config.less, {
    filename: relativePath,
    sourceMap: true,
    paths: [Path.dirname(absolutePath)]
  })).then(function(output) {
    if (output.imports.length) {
      state.imports = state.imports.concat(output.imports)
    }
    if (output.map) {
      state.sourceMap = output.map.toString()
    }
    return output.css
  })
}

'use babel'

import Path from 'path'
import Less from 'less'

export const minifier = false
export const compiler = true
export function process(contents, {rootDirectory, filePath, config, state}) {
  return Less.render(contents, Object.assign({}, config.less, {
    filename: Path.relative(rootDirectory, filePath),
    sourceMap: true,
    paths: [Path.dirname(filePath)]
  })).then(function(output) {
    state.imports = state.imports.concat(output.imports)
    return {
      contents: output.css,
      sourceMap: JSON.parse(output.map)
    }
  })
}

Upgrade package to ucompiler v3


/* global localStorage */

import axios from 'axios'

const FD_API_URL = process.env.FD_API_URL || 'http://localhost:9111/v3'

export default axios.create({
  baseURL: FD_API_URL,
  headers: {
    'Content-Type': 'application/json'
  }
})

/* global localStorage */

import axios from 'axios'

const FD_API_URL = process.env.FD_API_URL || 'http://localhost:9111/v3'

export default axios.create({
  baseURL: FD_API_URL,
  withCredentials: true,
  headers: {
    'Content-Type': 'application/json'
  }
})

Fix issue with websocket and Chrome (add Authorization header)


import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Query } from 'react-apollo';

import newChannelGroupsQuery from 'react/components/NewChannelForm/components/NewChannelGroups/queries/newChannelGroups';

import Pulldown from 'react/components/UI/Pulldown';
import AuthorOption from 'react/components/NewChannelForm/components/NewChannelGroups/components/AuthorOption';

export default class NewChannelGroups extends Component {
  static propTypes = {
    onChange: PropTypes.func.isRequired,
  }

  render() {
    const { onChange } = this.props;

    return (
      <Query query={newChannelGroupsQuery}>
        {({ data, errors, loading }) => {
          if (loading || errors) {
            return (

                <Pulldown
                  value={0}
                  options={{
                    0: <AuthorOption member={{ name: 'Me', __typename: 'me' }} />,
                  }}
                />

            );
          }

          const { me, me: { groups } } = data;

          return (

              <Pulldown
                value={0}
                onChange={onChange}
                options={{
                  0: <AuthorOption member={me} />,
                  ...groups.reduce((memo, group) => ({
                    ...memo, [group.id]: <AuthorOption member={group} />,
                  }), {}),
                }}
              />

          );
        }}
      </Query>
    );
  }
}

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Query } from 'react-apollo';

import newChannelGroupsQuery from 'react/components/NewChannelForm/components/NewChannelGroups/queries/newChannelGroups';

import Pulldown from 'react/components/UI/Pulldown';
import AuthorOption from 'react/components/AuthorOption';

export default class NewChannelGroups extends Component {
  static propTypes = {
    onChange: PropTypes.func.isRequired,
  }

  render() {
    const { onChange } = this.props;

    return (
      <Query query={newChannelGroupsQuery}>
        {({ data, errors, loading }) => {
          if (loading || errors) {
            return (

                <Pulldown
                  value={0}
                  options={{
                    0: <AuthorOption member={{ name: 'Me', __typename: 'me' }} />,
                  }}
                />

            );
          }

          const { me, me: { groups } } = data;

          return (

              <Pulldown
                value={0}
                onChange={onChange}
                options={{
                  0: <AuthorOption member={me} />,
                  ...groups.reduce((memo, group) => ({
                    ...memo, [group.id]: <AuthorOption member={group} />,
                  }), {}),
                }}
              />

          );
        }}
      </Query>
    );
  }
}

Use extracted option in NewChannel form


'use strict';
var test = require('tap').test
var util = require('util')
var fs = require('fs')

// mock fs.statSync to return signed uids/gids
var realStatSync = fs.statSync
fs.statSync = function(path) {
  var stats = realStatSync.call(fs, path)
  stats.uid = -2
  stats.gid = -2
  return stats
}

var gfs = require('../graceful-fs.js')

test('graceful fs uses same stats constructor as fs', function (t) {
  t.equal(gfs.Stats, fs.Stats, 'should reference the same constructor')

  if (!process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH) {
    t.equal(fs.statSync(__filename).uid, -2)
    t.equal(fs.statSync(__filename).gid, -2)
  }

  t.equal(gfs.statSync(__filename).uid, 0xfffffffe)
  t.equal(gfs.statSync(__filename).gid, 0xfffffffe)

  t.end()
})

test('does not throw when async stat fails', function (t) {
  fs.stat(__filename + ' this does not exist', function (er, stats) {
    t.ok(er)
    t.notOk(stats)
    t.end()
  })
})

'use strict';
var test = require('tap').test
var util = require('util')
var fs = require('fs')

// mock fs.statSync to return signed uids/gids
var realStatSync = fs.statSync
fs.statSync = function(path) {
  var stats = realStatSync.call(fs, path)
  stats.uid = -2
  stats.gid = -2
  return stats
}

var gfs = require('../graceful-fs.js')

test('graceful fs uses same stats constructor as fs', function (t) {
  t.equal(gfs.Stats, fs.Stats, 'should reference the same constructor')

  if (!process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH) {
    t.equal(fs.statSync(__filename).uid, -2)
    t.equal(fs.statSync(__filename).gid, -2)
  }

  t.equal(gfs.statSync(__filename).uid, 0xfffffffe)
  t.equal(gfs.statSync(__filename).gid, 0xfffffffe)

  t.end()
})

test('does not throw when async stat fails', function (t) {
  gfs.stat(__filename + ' this does not exist', function (er, stats) {
    t.ok(er)
    t.notOk(stats)
    t.end()
  })
})

test('does not throw when async stat fails', function (t) {
  t.throws(function() {
    gfs.statSync(__filename + ' this does not exist')
  }, /ENOENT/)
  t.end()
})

Test guarded access to `stats` in `fs.statSync()` wrapper


const userAgent = navigator.userAgent.toLowerCase()
const event = userAgent.match(/(iphone|ipod|ipad)/) ? 'touchstart' : 'click'

const directive = {
  instances: []
}

directive.onEvent = function (event) {
  directive.instances.forEach(({ el, fn }) => {
    if (event.target !== el && !el.contains(event.target)) {
      fn && fn(event)
    }
  })
}

directive.bind = function (el) {
  directive.instances.push({ el, fn: null })
  if (directive.instances.length === 1) {
    document.addEventListener(event, directive.onEvent)
  }
}

directive.update = function (el, binding) {
  if (typeof binding.value !== 'function') {
    throw new Error('Argument must be a function')
  }
  const instance = directive.instances.find(i => i.el === el)
  instance.fn = binding.value
}

directive.unbind = function (el) {
  const instanceIndex = directive.instances.findIndex(i => i.el === el)
  directive.instances.splice(instanceIndex, 1)
  if (directive.instances.length === 0) {
    document.removeEventListener(event, directive.onEvent)
  }
}

export default directive

const isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0));
const event = isTouch ? 'touchstart' : 'click'

const directive = {
  instances: []
}

directive.onEvent = function (event) {
  directive.instances.forEach(({ el, fn }) => {
    if (event.target !== el && !el.contains(event.target)) {
      fn && fn(event)
    }
  })
}

directive.bind = function (el) {
  directive.instances.push({ el, fn: null })
  if (directive.instances.length === 1) {
    document.addEventListener(event, directive.onEvent)
  }
}

directive.update = function (el, binding) {
  if (typeof binding.value !== 'function') {
    throw new Error('Argument must be a function')
  }
  const instance = directive.instances.find(i => i.el === el)
  instance.fn = binding.value
}

directive.unbind = function (el) {
  const instanceIndex = directive.instances.findIndex(i => i.el === el)
  directive.instances.splice(instanceIndex, 1)
  if (directive.instances.length === 0) {
    document.removeEventListener(event, directive.onEvent)
  }
}

export default directive

Use a more cross-platform touch detection method


var numRows = 4
var numCols = 4

var matrix = [
    [false, false, false, false],
    [false, false, false, false],
    [false, false, false, false],
    [false, false, false, false]
]

function getLightId(row, col) {
    return "#light-" + row + "-" + col
}

function setLightColor(row, col) {

    var lightId = getLightId(row, col)

    if (matrix[row][col] ) {
        $(lightId).css("background-color", "pink")
    } else {
        $(lightId).css("background-color", "gray")
    }
}

for (var row = 0; row < numRows; row++) {
    for (var col = 0; col < numCols; col++) {

        matrix[row][col]  = Math.random() < 0.5;

        setLightColor(row, col);
    }
}

function lightClick(row, col) {

    matrix[row][col] = !matrix[row][col];

    setLightColor(row, col);
}

var numRows = 4
var numCols = 4

var matrix = [
    [false, false, false, false],
    [false, false, false, false],
    [false, false, false, false],
    [false, false, false, false]
]

for (var row = 0; row < numRows; row++) {
    for (var col = 0; col < numCols; col++) {

        matrix[row][col]  = Math.random() < 0.5;

        setLightColor(row, col);
    }
}

function getLightId(row, col) {
    return "#light-" + row + "-" + col
}

function setLightColor(row, col) {

    var lightId = getLightId(row, col)

    if (matrix[row][col] ) {
        $(lightId).css("background-color", "pink")
    } else {
        $(lightId).css("background-color", "gray")
    }
}

function lightClick(row, col) {

    matrix[row][col] = !matrix[row][col];

    setLightColor(row, col);
}

Move matrix init to top


describe('Testing the first D3.js function drawKaryo', function() {
  var c;

  beforeEach(function() {
    c = drawKaryo();
    c.render();
  });

  afterEach(function() {
    d3.selectAll('svg').remove();
  });

  describe('the svg' ,function() {
    it('should be created', function() {
        expect(getSvg()).toBeNull();
    });

//    it('should have the correct height', function() {
//      expect(getSvg().attr('width')).toBe('500');
//    });
//
//    it('should have the correct width', function() {
//      expect(getSvg().attr('width')).toBe('500');
//    });
  });

  function getSvg() {
    return d3.select('svg');
  }

});

describe('Testing the first D3.js function drawKaryo', function() {
  var c;

  beforeEach(function() {
    c = drawKaryo();
    c.render();
  });

  afterEach(function() {
    d3.selectAll('svg').remove();
  });

  describe('the svg' ,function() {
    it('should be created', function() {
        expect(getSvg()).not.toBeNull();
    });

//    it('should have the correct height', function() {
//      expect(getSvg().attr('width')).toBe('500');
//    });
//
//    it('should have the correct width', function() {
//      expect(getSvg().attr('width')).toBe('500');
//    });
  });

  function getSvg() {
    return d3.select('svg');
  }

});

Test angepasst, jetzt erfolgreich: es existiert ein svg-Element


import tabsWithCards from "./tabs-with-cards.js";
import bookmarkButtons from "./bookmark-buttons.js";
import lazyLoadImages from "./lazy-load-images.js";

document.addEventListener("DOMContentLoaded", () => {
  tabsWithCards.init();
  bookmarkButtons.init();
  lazyLoadImages.init();
});

import tabsWithCards from "./tabs-with-cards.js";
import bookmarkButtons from "./bookmark-buttons.js";
import lazyLoadImages from "./lazy-load-images.js";
import csvGenerator from "./csv-generator.js";

document.addEventListener("DOMContentLoaded", () => {
  tabsWithCards.init();
  bookmarkButtons.init();
  lazyLoadImages.init();
  csvGenerator.init();
});

Implement CSV generator logic in collection view


defaultConf = {
    height: 100,
    width: 300,
    fingerprintLength: 4
};

describe("The constructor is supposed a proper Captcha object", function() {
    it('Constructor Captcha exists', function(){
        expect(Captcha).toBeDefined();
    });
    var captcha = new Captcha();
    it("Captcha object is not null", function(){
       expect(captcha).not.toBeNull();
    });
    it('captcha object should be an instance of Captcha class', function(){
        expect(captcha instanceof Captcha).toBeTruthy();
    });
    it('the height of the svg should be set to the configured height', function(){
        expect(Number(captcha.height)).toEqual(defaultConf.height);
    });
    it('the width of the svg should be set to the configured width', function(){
        expect(Number(captcha.width)).toEqual(defaultConf.width);
    });
    it('the length of the fingerprint should be set to the configured fingerprintLength', function(){
        expect(Number(captcha.fingerprintLength)).toEqual(defaultConf.fingerprintLength);
    });
});

describe('The getSVG method of Captcha objects is supposed to generate the captcha as SVG', function(){
    var captcha = new Captcha();
    it('getSVG method is supposed to be a function', function(){
        expect(typeof captcha.getSVG).toEqual('function');
    });
});

describe('The checkCaptcha method of Captcha objects is supposed to check if input is equal to fingerprint', function(){
    var captcha = new Captcha();
    it('checkCaptcha method is supposed to be a function', function(){
        expect(typeof captcha.checkCaptcha).toEqual('function');
    });
});

Add simple tets for new function. Test fails


// @flow
import Box from './box'
import React from 'react'
import {FlatButton} from 'material-ui'

import type {Props} from './clickable-box'

const ClickableBox = (props: Props & {children: any}) => {
  const {style, children, ...otherProps} = props

  // FlatButton on desktop doesn't support onLongPress, but we allow the common
  // ClickableBox component to pass one down for mobile, so strip it out here.
  if (otherProps.onLongPress) {
    delete otherProps.onLongPress
  }

  return (
    <FlatButton
      {...otherProps}
      rippleColor={'transparent'}
      hoverColor={'transparent'}
      style={styleFlatButton}
    >
      <Box style={style}>{children}</Box>
    </FlatButton>
  )
}

const styleFlatButton = {
  transition: 'none',
  transform: 'none',
  textAlign: 'left',
  height: undefined,
  width: '100%',
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'stretch',
  borderRadius: 0,
}

export default ClickableBox

// @flow
import Box from './box'
import React from 'react'
import {FlatButton} from 'material-ui'

import type {Props} from './clickable-box'

const ClickableBox = (props: Props & {children: any}) => {
  const {style, children, ...otherProps} = props

  // FlatButton on desktop doesn't support onLongPress, but we allow the common
  // ClickableBox component to pass one down for mobile, so strip it out here.
  if (otherProps.onLongPress) {
    delete otherProps.onLongPress
  }

  if (!otherProps.onClick) {
    delete otherProps.onClick
  }

  return (
    <FlatButton
      {...otherProps}
      rippleColor={'transparent'}
      hoverColor={'transparent'}
      style={styleFlatButton}
    >
      <Box style={style}>{children}</Box>
    </FlatButton>
  )
}

const styleFlatButton = {
  transition: 'none',
  transform: 'none',
  textAlign: 'left',
  height: undefined,
  width: '100%',
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'stretch',
  borderRadius: 0,
}

export default ClickableBox

Fix ClickableBox throw when clicked w/ null onClick


define(['jquery'], function($){
    "use strict";

    /**
     * Initializer
     * @param url
     */
    function init(url) {
        $('#connector_data_mapping_dynamic_datafield_datafield_button').click(function () {
            var name  	  = $('#connector_data_mapping_dynamic_datafield_datafield_name').val();
            var type  	  = $('#connector_data_mapping_dynamic_datafield_datafield_type').val();
            var d_default = $('#connector_data_mapping_dynamic_datafield_datafield_default').val();
            var access    = $('#connector_data_mapping_dynamic_datafield_datafield_access').val();

            if(name && type && access) {
                $.post(url, {name: name, type: type, deafult: d_default, visiblity: access}, function () {
                    window.location.reload();
                });
            }
        });
    }

    /**
     * Export/return dataFields
     * @param dataFields
     */
    return function(dataFields) {
        init(dataFields.url);
    };
});

define(['jquery'], function($){
    "use strict";

    /**
     * Initializer
     * @param url
     */
    function init(url) {
        $('#connector_data_mapping_dynamic_datafield_datafield_button').click(function () {
            var name  	  = $('#connector_data_mapping_dynamic_datafield_datafield_name').val();
            var type  	  = $('#connector_data_mapping_dynamic_datafield_datafield_type').val();
            var d_default = $('#connector_data_mapping_dynamic_datafield_datafield_default').val();
            var access    = $('#connector_data_mapping_dynamic_datafield_datafield_access').val();

            if(name && type && access) {
                $.post(url, {name: name, type: type, default: d_default, visibility: access}, function () {
                    window.location.reload();
                });
            }
        });
    }

    /**
     * Export/return dataFields
     * @param dataFields
     */
    return function(dataFields) {
        init(dataFields.url);
    };
});

Create Datafield in Magento ignores Default Value Create DataField in Magento ignores Visibility


var _ = require('lodash'),
    express = require('express'),
    STATIC_CACHE_AGE = 365 * 24 * 60 * 60 * 1000; // 1 year;

module.exports = function configureServer(config, app) {
  var app = app || express();

  function serveIndex(request, response) {
    return response.sendFile('index.html', {
      root: config.appAssets.html.dest,
      headers: {
          'Cache-Control': 'private'
        }
    });
  }

  if (_.get(config, 'server.options.logs')) {
    app.use(require('morgan')('dev'));
  }

  if (_.get(config, 'server.options.gzip')) {
    app.use(require('compression')());
  }

  app.use(function checkForIndex(request, response, next) {
    var isIndex = request.url.match(/index\.html$/);

    if (isIndex) {
      serveIndex(request, response);
    } else {
      next();
    }
  });

  // Serve static assets
  app.use(express.static(config.destPath, {
    maxAge: _.get(config, 'server.options.cacheAge', STATIC_CACHE_AGE),
    index: false
  }));

  // Fall back to serving index, if no file is found
  app.get('*', serveIndex);

  return app;
};

var _ = require('lodash'),
    express = require('express'),
    STATIC_CACHE_AGE = 365 * 24 * 60 * 60 * 1000; // 1 year;

module.exports = function configureServer(config, app) {
  var app = app || express();

  function serveIndex(request, response) {
    return response.sendFile('index.html', {
      root: config.appAssets.html.dest,
      headers: {
          'Cache-Control': 'private, no-cache, max-age=0'
        }
    });
  }

  if (_.get(config, 'server.options.logs')) {
    app.use(require('morgan')('dev'));
  }

  if (_.get(config, 'server.options.gzip')) {
    app.use(require('compression')());
  }

  app.use(function checkForIndex(request, response, next) {
    var isIndex = request.url.match(/index\.html$/);

    if (isIndex) {
      serveIndex(request, response);
    } else {
      next();
    }
  });

  // Serve static assets
  app.use(express.static(config.destPath, {
    maxAge: _.get(config, 'server.options.cacheAge', STATIC_CACHE_AGE),
    index: false
  }));

  // Fall back to serving index, if no file is found
  app.get('*', serveIndex);

  return app;
};

Tweak index.html Cache-Control header value in SPA