<commit_before>/**
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var DB_KEY = 'token';
var DB_NAME = 'push-notification-updates';
var DEFAULT_ICON = 'images/touch/homescreen192.png';
var SESSIONS_ENDPOINT = 'api/v1/schedule';
var UPDATES_ENDPOINT = 'api/v1/user/updates';
// Contains a mapping of notification tag values to the corresponding URL that should be opened
// when the notification is tapped/clicked.
var TAG_TO_DESTINATION_URL = {
  schedule: 'schedule#myschedule'
};
var UTM_SOURCE_PARAM = 'utm_source=notification';

/**
 * Loads a SW token value from IndexedDB.
 * @return {Promise} Resolves with the token, or null if there isn't one.
 */
function loadToken() {
  return simpleDB.open(DB_NAME).then(function(db) {
    return db.get(DB_KEY).then(function(token) {
      return token;
    });
  });
}

/**
 * Makes a request against the updates endpoint to determine which new notifications to display.
 * See https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @param {string} token A SW token associated with the current user.
 * @return {Promise} Resolves with a Response, or rejects on a network error.
 */
function fetchUpdates(token) {
  return fetch(new Request(UPDATES_ENDPOINT, {
    headers: {
      Authorization: token
    }
  }));
}

/**
 * Deserializes a JSON body from a Response object.
 * @param {Response} response An HTTP response object with a JSON body.
 * @return {Promise} Resolves with an object representing the JSON in the response body, or rejects
 *                   if the Response was an HTTP error or didn't contain JSON.
 */
function parseResponseJSON(response) {
  if (response.status >= 400) {
    throw Error('The request to ' + response.url + ' failed: ' +
                response.statusText + ' (' + response.status + ')');
  }
  return response.json();
}

/**
 * Processes the updates response to show notifications for each update.
 * @param {object} body See https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @return {Promise} Resolves with a SW token that should be used the next time updates are fetched.
 *                   Rejects if there are any errors displaying notifications.
 */
function processResponse(body) {
  var notifications = generateSessionNotifications(body.sessions)
    .concat(generateVideoNotifications(body.videos))
    .concat(generateIOExtNotifications(body.ioext));

  return Promise.all(notifications.map(function(notification) {
    return self.registration.showNotification(notification.title, notification);
  })).then(function() {
    return body.token;
  });
}

/**
 * For each session that's passed in as a parameter, constructs the data needed to display a
 * notification.
 * Additionally, updates the cached session feed with the latest data from the update, to ensure
 * that if the session detail page is opened while offline, the details are up to date.
 * @param {array} updatedSessions One or more sessions.
 * @return {array} An array of objects with data that can be passed directly to
 *                   registration.showNotification()
 */
function generateSessionNotifications(updatedSessions) {
  if (updatedSessions && updatedSessions.length) {
    // Ensure that we have an up-to-date sessions feed cached.
    // This will happen aysnchronously, independent from the notification creation, so it shouldn't be
    // necessary to wait on the promise resolutions.
    caches.open(shed.options.cacheName).then(function(cache) {
      cache.match(SESSIONS_ENDPOINT).then(function(response) {
        if (response) {
          // If there's a cached sessions feed, then update the changed fields and replace the cached
          // version with the updated version.
          parseResponseJSON(response).then(function(allSessions) {
            Object.keys(updatedSessions).forEach(function(sessionId) {
              allSessions[sessionId] = updatedSessions[sessionId];
            });

            cache.put(SESSIONS_ENDPOINT, new Response(JSON.stringify(allSessions)));
          });
        } else {
          // If there isn't anything already cached for the sessions feed, then cache the whole thing.
          shed.cache(SESSIONS_ENDPOINT);
        }
      });
    }).catch(function(error) {
      console.error('Could not update the cached sessions feed:', error);
    });

    return Object.keys(updatedSessions).map(function(sessionId) {
      var session = updatedSessions[sessionId];
      return {
        title: 'Some events in My Schedule have been updated',
        body: '"' + session.title + '" was updated.',
        icon: session.photoUrl || DEFAULT_ICON,
        tag: 'schedule'
      };
    });
  } else {
    return [];
  }
}

function generateVideoNotifications(videos) {
  // TODO: Implement.
  return [];
}

function generateIOExtNotifications(videos) {
  // TODO: Implement.
  return [];
}

/**
 * Writes a SW token value to IndexedDB.
 * @param {string} token A SW token, returned from
 *                 https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @return {Promise} Resolves if the token is written successfully, and rejects otherwise.
 */
function saveToken(token) {
  return simpleDB.open(DB_NAME).then(function(db) {
    return db.set(DB_KEY, token);
  });
}

self.addEventListener('push', function(event) {
  event.waitUntil(
    loadToken()
      .then(fetchUpdates)
      .then(parseResponseJSON)
      .then(processResponse)
      .then(saveToken)
      .catch(function(error) {
        console.error('Unable to handle event', event, 'due to error', error);
      })
  );
});

self.addEventListener('notificationclick', function(event) {
  var relativeUrl = TAG_TO_DESTINATION_URL[event.notification.tag] || '/';
  var url = new URL(relativeUrl, location.href);
  url.search += (url.search ? '&' : '') + UTM_SOURCE_PARAM;
  self.clients.openWindow(url.toString());
});
<commit_msg>More changes!<commit_after>/**
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var DB_KEY = 'token';
var DB_NAME = 'push-notification-updates';
var DEFAULT_ICON = 'images/touch/homescreen192.png';
var SCHEDULE_ENDPOINT = 'api/v1/schedule';
var UPDATES_ENDPOINT = 'api/v1/user/updates';
// Contains a mapping of notification tag values to the corresponding URL that should be opened
// when the notification is tapped/clicked.
var TAG_TO_DESTINATION_URL = {
  'session-details': 'schedule#myschedule'
};
var UTM_SOURCE_PARAM = 'utm_source=notification';

/**
 * Loads a SW token value from IndexedDB.
 * @return {Promise} Resolves with the token, or null if there isn't one.
 */
function loadToken() {
  return simpleDB.open(DB_NAME).then(function(db) {
    return db.get(DB_KEY).then(function(token) {
      return token;
    });
  });
}

/**
 * Makes a request against the updates endpoint to determine which new notifications to display.
 * See https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @param {string} token A SW token associated with the current user.
 * @return {Promise} Resolves with a Response, or rejects on a network error.
 */
function fetchUpdates(token) {
  return fetch(new Request(UPDATES_ENDPOINT, {
    headers: {
      Authorization: token
    }
  }));
}

/**
 * Deserializes a JSON body from a Response object.
 * @param {Response} response An HTTP response object with a JSON body.
 * @return {Promise} Resolves with an object representing the JSON in the response body, or rejects
 *                   if the Response was an HTTP error or didn't contain JSON.
 */
function parseResponseJSON(response) {
  if (response.status >= 400) {
    throw Error('The request to ' + response.url + ' failed: ' +
                response.statusText + ' (' + response.status + ')');
  }
  return response.json();
}

/**
 * Processes the updates response to show notifications for each update.
 * @param {object} body See https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @return {Promise} Resolves with a SW token that should be used the next time updates are fetched.
 *                   Rejects if there are any errors displaying notifications.
 */
function processResponse(body) {
  var notifications = generateSessionNotifications(body.sessions)
    .concat(generateVideoNotifications(body.videos))
    .concat(generateIOExtNotifications(body.ioext));

  return Promise.all(notifications.map(function(notification) {
    return self.registration.showNotification(notification.title, notification);
  })).then(function() {
    return body.token;
  });
}

/**
 * For each session that's passed in as a parameter, constructs the data needed to display a
 * notification.
 * Additionally, updates the cached session feed with the latest data from the update, to ensure
 * that if the session detail page is opened while offline, the details are up to date.
 * @param {array} updatedSessions One or more sessions.
 * @return {array} An array of objects with data that can be passed directly to
 *                   registration.showNotification()
 */
function generateSessionNotifications(updatedSessions) {
  var sessionIds = Object.keys(updatedSessions);
  if (sessionIds.length) {
    // Ensure that we have an up-to-date sessions feed cached.
    // This will happen aysnchronously, independent from the notification creation, so it shouldn't be
    // necessary to wait on the promise resolutions.
    caches.open(shed.options.cacheName).then(function(cache) {
      cache.match(SCHEDULE_ENDPOINT).then(function(response) {
        if (response) {
          // If there's a cached sessions feed, then update the changed fields and replace the cached
          // version with the updated version.
          parseResponseJSON(response).then(function(schedule) {
            Object.keys(updatedSessions).forEach(function(sessionId) {
              schedule.sessions[sessionId] = updatedSessions[sessionId];
            });

            cache.put(SCHEDULE_ENDPOINT, new Response(JSON.stringify(schedule)));
          });
        } else {
          // If there isn't anything already cached for the sessions feed, then cache the whole thing.
          shed.cache(SCHEDULE_ENDPOINT);
        }
      });
    }).catch(function(error) {
      console.error('Could not update the cached sessions feed:', error);
    });

    return sessionIds.filter(function(sessionId) {
      // TODO(jeffposnick): Handle notifications for video/start updates.
      return updatedSessions[sessionId].update === 'details';
    }).map(function(sessionId) {
      var session = updatedSessions[sessionId];
      return {
        title: 'Some events in My Schedule have been updated',
        body: '"' + session.title + '" was updated.',
        icon: session.photoUrl || DEFAULT_ICON,
        tag: 'session-details'
      };
    });
  } else {
    return [];
  }
}

function generateVideoNotifications(videos) {
  // TODO: Implement.
  return [];
}

function generateIOExtNotifications(videos) {
  // TODO: Implement.
  return [];
}

/**
 * Writes a SW token value to IndexedDB.
 * @param {string} token A SW token, returned from
 *                 https://github.com/GoogleChrome/ioweb2015/blob/master/docs/API.md#get-apiv1userupdates
 * @return {Promise} Resolves if the token is written successfully, and rejects otherwise.
 */
function saveToken(token) {
  return simpleDB.open(DB_NAME).then(function(db) {
    return db.set(DB_KEY, token);
  });
}

self.addEventListener('push', function(event) {
  event.waitUntil(
    loadToken()
      .then(fetchUpdates)
      .then(parseResponseJSON)
      .then(processResponse)
      .then(saveToken)
      .catch(function(error) {
        console.error('Unable to handle event', event, 'due to error', error);
      })
  );
});

self.addEventListener('notificationclick', function(event) {
  var relativeUrl = TAG_TO_DESTINATION_URL[event.notification.tag] || '/';
  var url = new URL(relativeUrl, location.href);
  url.search += (url.search ? '&' : '') + UTM_SOURCE_PARAM;
  self.clients.openWindow(url.toString());
});
<|endoftext|>


<commit_before>// import React from 'react';
// import { mount } from 'enzyme';
// import { ListGroupItem } from 'react-bootstrap';
// import Todo from 'components/todo';

// describe('Todo', () => {
//   const todo = {
//     id: 1,
//     isComplete: false,
//     name: 'Something to do'
//   };

//   describe('callbacks', () => {
//     const updateTodo = jest.fn();
//     const deleteTodo = jest.fn();
//     const todoComponent = mount(
//       <Todo
//         updateTodo={ updateTodo }
//         deleteTodo={ deleteTodo }
//         todo={ todo }
//       />
//     );

//     it('calls toggle() when clicking on li and mark todo as complete', () => {
//       todoComponent.find(ListGroupItem).at(0).simulate('click');

//       expect(updateTodo).toHaveBeenCalled();
//     });

//     it('calls delete() when clicking on delete icon', () => {
//       todoComponent.find('.glyphicon-trash').simulate('click');

//       expect(deleteTodo).toHaveBeenCalled();
//     });
//   });
// });
<commit_msg>add todo item specs<commit_after>import React from 'react';
import { mount } from 'enzyme';
import TodoItem from '../item';

describe('TodoItem', () => {
  let props;
  let component;
  const renderComponent = () => mount(<TodoItem { ...props } />);

  describe('callbacks', () => {
    const deleteTodo = jest.fn();
    const updateTodo = jest.fn();

    beforeEach(() => {
      props = {
        deleteTodo,
        updateTodo,
        todo: {
          id: 1,
          isComplete: false,
          name: 'Some Todo'
        }
      };
    });

    it('calls .updateTodo() when clicking on li and mark todo as complete', () => {
      component = renderComponent();
      component.find('ListGroupItem').simulate('click');

      expect(updateTodo).toHaveBeenCalledWith({
        id: 1,
        isComplete: true,
        name: 'Some Todo'
      });
    });

    it('calls .deleteTodo() when clicking on delete icon', () => {
      component = renderComponent();
      component.find('.glyphicon-trash').simulate('click');

      expect(deleteTodo).toHaveBeenCalledWith({
        id: 1,
        isComplete: false,
        name: 'Some Todo'
      });
    });
  });
});
<|endoftext|>


<commit_before>/**
 * RMS固有クラス
 */
var Rms = function() {

}

/**
 * 関数定義
 */
Rms.prototype = {

	/**
	 * CSSテーマ変更
	 *
	 * @param theme
	 */
	changeTheme : function(theme) {
		if (theme === $("#theme").val()) {
			// 現在と同じテーマの場合は何もしない
			return;
		}

		var cssBaseUrl = $("form").attr("action") + "fw/bootstrap/css/";
		var cssName = "bootswatch-theme-" + theme + ".min.css";

		// フェードアウト後にCSS変更
		$("body").fadeOut("normal", function() {
			$("#myTheme").attr("href", cssBaseUrl + cssName);
		});

		// N秒後にフェードイン
		setTimeout(function() {
			$("body").fadeIn("normal");
		}, 1500);

		// ローカルストレージにテーマを保存
		localStorage.setItem("theme", theme);
		// hiddenの値を更新
		$("#theme").val(theme);
	}
}

/*
 * グローバル変数
 */
// RMS共通関数
var rms = new Rms();
<commit_msg>CodeStyle修正<commit_after>/**
 * RMS固有クラス
 */
var Rms = function() {

};

/**
 * 関数定義
 */
Rms.prototype = {

	/**
	 * CSSテーマ変更
	 *
	 * @param theme
	 */
	changeTheme(theme) {
		if (theme === $("#theme").val()) {
			// 現在と同じテーマの場合は何もしない
			return;
		}

		var cssBaseUrl = $("form").attr("action") + "fw/bootstrap/css/";
		var cssName = "bootswatch-theme-" + theme + ".min.css";

		// フェードアウト後にCSS変更
		$("body").fadeOut("normal", function() {
			$("#myTheme").attr("href", cssBaseUrl + cssName);
		});

		// N秒後にフェードイン
		setTimeout(function() {
			$("body").fadeIn("normal");
		}, 1500);

		// ローカルストレージにテーマを保存
		localStorage.setItem("theme", theme);
		// hiddenの値を更新
		$("#theme").val(theme);
	}
};

/*
 * グローバル変数
 */
// RMS共通関数
var rms = new Rms();
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';

import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Tooltip from '@material-ui/core/Tooltip';
import IconButton from '@material-ui/core/IconButton';
import { withStyles } from '@material-ui/core/styles';
import SearchIcon from '@material-ui/icons/Search';
import RefreshIcon from '@material-ui/icons/Refresh';
import Container from '@material-ui/core/Container';
//import Chart from './Chart';
import moment from 'moment'

import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';

import DashboardCard from '../components/dashboard/card'
import DashboardCard2 from '../components/dashboard/card2'

import Title from '../components/dashboard/title';

import HeatMap from '../components/charts/heatMap'
import Pie from '../components/charts/pie'

import {DASHBOARD} from "../graphql/queries"
import graphql from '../graphql/client'

const styles = theme => ({
  paperll: {
    maxWidth: 936,
    //marginTop: '2em',
    //marginBottom: '2em',
    margin: 'auto',
    //overflow: 'hidden',
    //marginBottom: 20,
  },
  searchBar: {
    borderBottom: '1px solid rgba(0, 0, 0, 0.12)',
  },
  searchInput: {
    fontSize: theme.typography.fontSize,
  },
  block: {
    display: 'block',
  },
  addUser: {
    marginRight: theme.spacing(1),
  },
  contentWrapper: {
    margin: '40px 16px',
  },
  card: {
    minWidth: 275,
  },
  bullet: {
    display: 'inline-block',
    margin: '0 2px',
    transform: 'scale(0.8)',
  },
  title: {
    fontSize: 14,
  },
  pos: {
    marginBottom: 12,
  },

  content: {
    flexGrow: 1,
    height: '100vh',
    //overflow: 'auto',
  },
  container: {
    paddingTop: theme.spacing(4),
    paddingBottom: theme.spacing(4),
  },
  paper: {
    padding: theme.spacing(2),
    display: 'flex',
    overflow: 'auto',
    flexDirection: 'column',
  },
  fixedHeight: {
    height: 240,
  },

});

function Dashboard(props) {
  const { classes, app } = props;

  console.log(props)

  const initialData =  {
    loading: true,
    from: moment().add(-1, 'week'),
    to: moment(), //.add(-1, 'day')
  }

  const [dashboard, setDashboard] = React.useState(initialData)

  const bull = •;
  const fixedHeightPaper = clsx(classes.paper, classes.fixedHeight);

  return (
    <Paper className={classes.paper}>

      {/*
      <AppBar className={classes.searchBar}
        position="static" color="default" elevation={0}>
        <Toolbar>
          <Grid container spacing={10} alignItems="center">
            <Grid item>
              <SearchIcon className={classes.block} color="inherit" />
            </Grid>
            <Grid item xs>
              <TextField
                fullWidth
                placeholder="Search by email address, phone number, or user UID"
                InputProps={{
                  disableUnderline: true,
                  className: classes.searchInput,
                }}
              />
            </Grid>
            <Grid item>
              <Button variant="contained" color="primary" className={classes.addUser}>
                Add user
              </Button>
              <Tooltip title="Reload">
                <IconButton>
                  <RefreshIcon className={classes.block} color="inherit" />
                </IconButton>
              </Tooltip>
            </Grid>
          </Grid>
        </Toolbar>
      </AppBar>
      */}

      {
        /*

        <Grid container
           //className={classes.root}
           spacing={2}>
          <Grid item xs={12}>
            <Grid container
              //className={classes.demo}
              justify="center"
              spacing={1}>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

            </Grid>
          </Grid>
        </Grid>
        */
      }

        <Container maxWidth="lg" className={classes.container}>
          <Grid container spacing={3}>
            {/* Chart */}
            <Grid item xs={12} md={8} lg={9}>
              <Paper className={fixedHeightPaper}>
                {/*<Chart />*/}
                <Title>Visit activity</Title>
                 <DashboardItem
                    chartType={"heatMap"}
                    dashboard={dashboard}
                    app={app}
                    kind={"visits"}
                  />
              </Paper>
            </Grid>
            {/* Recent Deposits */}
            <Grid item xs={12} md={4} lg={3}>
              <Paper className={fixedHeightPaper}>
                <DashboardCard title={"Users browser"}>

                  <DashboardItem
                    chartType={"pie"}
                    dashboard={dashboard}
                    app={app}
                    kind={'browser'}
                  />

                </DashboardCard>

              </Paper>
            </Grid>

            <Grid item xs={4}>
              <Paper className={classes.paper}>
              <DashboardCard title={"Lead Os"}>
                  <DashboardItem
                    chartType={"pie"}
                    dashboard={dashboard}
                    app={app}
                    kind={'lead_os'}
                  />
                </DashboardCard>
              </Paper>
            </Grid>

            <Grid item xs={4}>
              <Paper className={classes.paper}>
              <DashboardCard title={"User Os"}>
                  <DashboardItem
                    chartType={"pie"}
                    dashboard={dashboard}
                    app={app}
                    kind={'user_os'}
                  />
                </DashboardCard>
              </Paper>
            </Grid>

            <Grid item xs={4}>
              <Paper className={classes.paper}>
              <DashboardCard title={"User country"}>
                  <DashboardItem
                    chartType={"pie"}
                    dashboard={dashboard}
                    app={app}
                    kind={'user_country'}
                  />
                </DashboardCard>
              </Paper>
            </Grid>

            <Grid item xs={12}>
              <Paper className={classes.paper}>
                <DashboardCard2 />
              </Paper>
            </Grid>
          </Grid>
        </Container>
        {/*<MadeWithLove />*/}

    </Paper>
  );
}

function DashboardItem(
  {
    app,
    kind,
    dashboard,
    chartType
  }){

  const [data, setData] = React.useState([])

  React.useEffect(()=>{
    getData()}
  , [])

  function getData(){
    graphql(DASHBOARD, {
      appKey: app.key,
      range: {
          from: dashboard.from,
          to: dashboard.to
        },
        kind: kind
      }, {
      success: (data)=>{
        setData(data.app.dashboard)
      },
      error: (err)=>{
        debugger
      }
    })
  }

  function renderChart(){
    switch (chartType) {
      case "heatMap":
        return <HeatMap
          data={data}
          from={dashboard.from}
          to={dashboard.to}
        />

      case "pie":
        return  <Pie
          data={data}
          from={dashboard.from}
          to={dashboard.to}
        />
      default:
        return
no chart type
;
    }
  }

  return (

      {
        data.length > 0 && renderChart()
      }

  )
}

Dashboard.propTypes = {
  classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(Dashboard);<commit_msg>Update Dashboard.js<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';

import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import Paper from '@material-ui/core/Paper';
import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Tooltip from '@material-ui/core/Tooltip';
import IconButton from '@material-ui/core/IconButton';
import { withStyles } from '@material-ui/core/styles';
import SearchIcon from '@material-ui/icons/Search';
import RefreshIcon from '@material-ui/icons/Refresh';
import Container from '@material-ui/core/Container';
//import Chart from './Chart';
import moment from 'moment'

import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';

import DashboardCard from '../components/dashboard/card'
import DashboardCard2 from '../components/dashboard/card2'

import Title from '../components/dashboard/title';
import Progress from '../shared/Progress'

import HeatMap from '../components/charts/heatMap'
import Pie from '../components/charts/pie'

import {DASHBOARD} from "../graphql/queries"
import graphql from '../graphql/client'

const styles = theme => ({
  paperll: {
    maxWidth: 936,
    //marginTop: '2em',
    //marginBottom: '2em',
    margin: 'auto',
    //overflow: 'hidden',
    //marginBottom: 20,
  },
  searchBar: {
    borderBottom: '1px solid rgba(0, 0, 0, 0.12)',
  },
  searchInput: {
    fontSize: theme.typography.fontSize,
  },
  block: {
    display: 'block',
  },
  addUser: {
    marginRight: theme.spacing(1),
  },
  contentWrapper: {
    margin: '40px 16px',
  },
  card: {
    minWidth: 275,
  },
  bullet: {
    display: 'inline-block',
    margin: '0 2px',
    transform: 'scale(0.8)',
  },
  title: {
    fontSize: 14,
  },
  pos: {
    marginBottom: 12,
  },

  content: {
    flexGrow: 1,
    height: '100vh',
    //overflow: 'auto',
  },
  container: {
    paddingTop: theme.spacing(4),
    paddingBottom: theme.spacing(4),
  },
  paper: {
    padding: theme.spacing(2),
    display: 'flex',
    overflow: 'auto',
    flexDirection: 'column',
  },
  fixedHeight: {
    height: 240,
  },

});

function Dashboard(props) {
  const { classes, app } = props;

  console.log(props)

  const initialData =  {
    loading: true,
    from: moment().add(-1, 'week'),
    to: moment(), //.add(-1, 'day')
  }

  const [dashboard, setDashboard] = React.useState(initialData)

  const bull = •;
  const fixedHeightPaper = clsx(classes.paper, classes.fixedHeight);

  return (
    <Paper className={classes.paper}>

      {/*
      <AppBar className={classes.searchBar}
        position="static" color="default" elevation={0}>
        <Toolbar>
          <Grid container spacing={10} alignItems="center">
            <Grid item>
              <SearchIcon className={classes.block} color="inherit" />
            </Grid>
            <Grid item xs>
              <TextField
                fullWidth
                placeholder="Search by email address, phone number, or user UID"
                InputProps={{
                  disableUnderline: true,
                  className: classes.searchInput,
                }}
              />
            </Grid>
            <Grid item>
              <Button variant="contained" color="primary" className={classes.addUser}>
                Add user
              </Button>
              <Tooltip title="Reload">
                <IconButton>
                  <RefreshIcon className={classes.block} color="inherit" />
                </IconButton>
              </Tooltip>
            </Grid>
          </Grid>
        </Toolbar>
      </AppBar>
      */}

      {
        /*

        <Grid container
           //className={classes.root}
           spacing={2}>
          <Grid item xs={12}>
            <Grid container
              //className={classes.demo}
              justify="center"
              spacing={1}>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

              <Grid item>
                <Card className={classes.card}>
                  <CardContent>
                    <Typography className={classes.title} color="textSecondary" gutterBottom>
                      Word of the Day
                    </Typography>
                    <Typography variant="h5" component="h2">
                      be
                      {bull}
                      nev
                      {bull}o{bull}
                      lent
                    </Typography>
                    <Typography className={classes.pos} color="textSecondary">
                      adjective
                    </Typography>
                    <Typography variant="body2" component="p">
                      well meaning and kindly.

                      {'"a benevolent smile"'}
                    </Typography>
                  </CardContent>
                  <CardActions>
                    <Button size="small">Learn More</Button>
                  </CardActions>
                </Card>
              </Grid>

            </Grid>
          </Grid>
        </Grid>
        */
      }

        <Container maxWidth="lg" className={classes.container}>
          <Grid container spacing={3}>
            {/* Chart */}
            <Grid item xs={12} md={8} lg={9}>
              <Paper className={fixedHeightPaper}>
                {/*<Chart />*/}
                <Title>Visit activity</Title>
                 <DashboardItem
                    chartType={"heatMap"}
                    dashboard={dashboard}
                    app={app}
                    kind={"visits"}
                  />
              </Paper>
            </Grid>
            {/* Recent Deposits */}
            <Grid item xs={12} md={4} lg={3}>
              <Paper className={fixedHeightPaper}>
                <DashboardCard title={"Users browser"}>

                  <DashboardItem
                    chartType={"pie"}
                    dashboard={dashboard}
                    app={app}
                    kind={'browser'}
                  />

                </DashboardCard>

              </Paper>
            </Grid>

            <Grid item xs={4}>
              <Paper className={classes.paper}>
              <DashboardCard title={"Lead Os"}>
                  <DashboardItem
                    chartType={"pie"}
                    dashboard={dashboard}
                    app={app}
                    kind={'lead_os'}
                  />
                </DashboardCard>
              </Paper>
            </Grid>

            <Grid item xs={4}>
              <Paper className={classes.paper}>
              <DashboardCard title={"User Os"}>
                  <DashboardItem
                    chartType={"pie"}
                    dashboard={dashboard}
                    app={app}
                    kind={'user_os'}
                  />
                </DashboardCard>
              </Paper>
            </Grid>

            <Grid item xs={4}>
              <Paper className={classes.paper}>
              <DashboardCard title={"User country"}>
                  <DashboardItem
                    chartType={"pie"}
                    dashboard={dashboard}
                    app={app}
                    kind={'user_country'}
                  />
                </DashboardCard>
              </Paper>
            </Grid>

            <Grid item xs={12}>
              <Paper className={classes.paper}>
                <DashboardCard2 />
              </Paper>
            </Grid>
          </Grid>
        </Container>
        {/*<MadeWithLove />*/}

    </Paper>
  );
}

function DashboardItem(
  {
    app,
    kind,
    dashboard,
    chartType
  }){

  const [data, setData] = React.useState([])
  const [loading, setLoading] = React.useState(true)

  React.useEffect(()=>{
    getData()}
  , [])

  function getData(){

    graphql(DASHBOARD, {
      appKey: app.key,
      range: {
          from: dashboard.from,
          to: dashboard.to
        },
        kind: kind
      }, {
      success: (data)=>{
        setData(data.app.dashboard)
        setLoading(false)
      },
      error: (err)=>{
        setLoading(false)
        debugger
      }
    })
  }

  function renderChart(){
    switch (chartType) {
      case "heatMap":
        return <HeatMap
          data={data}
          from={dashboard.from}
          to={dashboard.to}
        />

      case "pie":
        return  <Pie
          data={data}
          from={dashboard.from}
          to={dashboard.to}
        />
      default:
        return
no chart type
;
    }
  }

  return (

      {
        loading && <Progress/>
      }
      {
        data.length > 0 && renderChart()
      }

  )
}

Dashboard.propTypes = {
  classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(Dashboard);<|endoftext|>


<commit_before>/** @jsx React.DOM */
module.exports = function(item, i, items){
  var user = function(message, previous){
    function shouldNotDisplayAuthor(previousMsg, currentMsg) {
      return previousMsg && currentMsg && previousMsg.__user && currentMsg.__user &&
        currentMsg.__user === previousMsg.__user && previousMsg.get('action') != "JOIN"
    }

    if(shouldNotDisplayAuthor(previous, message)) {
        return;
    }
    var userData = message.__user;
    var avatar = userData.get('gh_avatar');
    var cn = "nick text " + (parseInt(userData.get('online'))?"online":"");
    return

        {userData.name}:

  }
  if(item.get('action') == "JOIN" || item.get('action') == "LEAVE"){
    return

        {item.__user.name + ' '}

;
  }
  if(!item.get('id')) return;
  var date = {
    hh:item.date.getHours(),
    mm:item.date.getMinutes()
  }
  if(date.hh < 10) date.hh = '0' + date.hh;
  if(date.mm < 10) date.mm = '0' + date.mm;
  return

    {user(item, items[i-1])}

        {item.date && ([date.hh, date.mm].join(":"))}

}
<commit_msg>move time indicator to function & add date as title & label for diff days messages<commit_after>/** @jsx React.DOM */
module.exports = function(item, i, items){
  var user = function(message, previous){
    function shouldNotDisplayAuthor(previousMsg, currentMsg) {
      return previousMsg && currentMsg && previousMsg.__user && currentMsg.__user &&
        currentMsg.__user === previousMsg.__user && previousMsg.get('action') != "JOIN"
    }

    if(shouldNotDisplayAuthor(previous, message)) {
        return;
    }
    var userData = message.__user;
    var avatar = userData.get('gh_avatar');
    var cn = "nick text " + (parseInt(userData.get('online'))?"online":"");
    return

        {userData.name}:

  }
  var formatedDate = function(date){
    return date.getUTCDate() + '/' + (+date.getUTCMonth() + 1)
  }
  var time = function(item, previous){
    var stringDate = formatedDate(item.date),
        shouldShow = !previous || (stringDate !== formatedDate(previous.date));

    var date = {
      hh:item.date.getHours(),
      mm:item.date.getMinutes()
    }
    if(date.hh < 10) date.hh = '0' + date.hh;
    if(date.mm < 10) date.mm = '0' + date.mm;
    return

      {item.date && ([date.hh, date.mm].join(":"))}

      {shouldShow && stringDate}

  }
  if(item.get('action') == "JOIN" || item.get('action') == "LEAVE"){
    return

        {item.__user.name + ' '}

;
  }
  if(!item.get('id')) return;
  return

    {user(item, items[i-1])}

      {time(item, items[i-1])}

}
<|endoftext|>


<commit_before>import EventPrototype from './prototype';
import { ERROR_PREFIX } from '../constants';

export default class CloseEvent extends EventPrototype {
  constructor(type, eventInitConfig = {}) {
    super();

    if (!type) {
      throw new TypeError(`${ERROR_PREFIX.EVENT.CLOSE} 1 argument required, but only 0 present.`);
    }

    if (typeof eventInitConfig !== 'object') {
      throw new TypeError(`${ERROR_PREFIX.EVENT.CLOSE} parameter 2 ('eventInitDict') is not an object`);
    }

    const { bubbles, cancelable, code, reason, wasClean } = eventInitConfig;

    this.type = `${type}`;
    this.timeStamp = Date.now();
    this.target = null;
    this.srcElement = null;
    this.returnValue = true;
    this.isTrusted = false;
    this.eventPhase = 0;
    this.defaultPrevented = false;
    this.currentTarget = null;
    this.cancelable = cancelable ? Boolean(cancelable) : false;
    this.canncelBubble = false;
    this.bubbles = bubbles ? Boolean(bubbles) : false;
    this.code = typeof code === 'number' ? parseInt(code, 10) : 0;
    this.reason = `${reason || ''}`;
    this.wasClean = wasClean ? Boolean(wasClean) : false;
  }
}
<commit_msg>[IMPR] Typo in CloseEvent.cancelBubble (#236)<commit_after>import EventPrototype from './prototype';
import { ERROR_PREFIX } from '../constants';

export default class CloseEvent extends EventPrototype {
  constructor(type, eventInitConfig = {}) {
    super();

    if (!type) {
      throw new TypeError(`${ERROR_PREFIX.EVENT.CLOSE} 1 argument required, but only 0 present.`);
    }

    if (typeof eventInitConfig !== 'object') {
      throw new TypeError(`${ERROR_PREFIX.EVENT.CLOSE} parameter 2 ('eventInitDict') is not an object`);
    }

    const { bubbles, cancelable, code, reason, wasClean } = eventInitConfig;

    this.type = `${type}`;
    this.timeStamp = Date.now();
    this.target = null;
    this.srcElement = null;
    this.returnValue = true;
    this.isTrusted = false;
    this.eventPhase = 0;
    this.defaultPrevented = false;
    this.currentTarget = null;
    this.cancelable = cancelable ? Boolean(cancelable) : false;
    this.cancelBubble = false;
    this.bubbles = bubbles ? Boolean(bubbles) : false;
    this.code = typeof code === 'number' ? parseInt(code, 10) : 0;
    this.reason = `${reason || ''}`;
    this.wasClean = wasClean ? Boolean(wasClean) : false;
  }
}
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/** Vary based on current platform. */
SC.NATURAL_SCROLLER_THICKNESS = 16;

/** @class

  Displays a horizontal or vertical scroller.  You will not usually need to
  work with scroller views directly, but you may override this class to
  implement your own custom scrollers.

  @extends SC.View
  @since SproutCore 1.0
*/
SC.ScrollerView = SC.View.extend({

  classNames: ['sc-scroller-view'],

  // ..........................................................
  // PROPERTIES
  //

  /**
   Used by the SC.ScrollView to properly layout the scrollers.
  */
  scrollerThickness: SC.NATURAL_SCROLLER_THICKNESS,

  /**
    The scroller offset value.  This value will adjust between the minimum
    and maximum values that you set. Default is 0.

    @property
  */
  value: function(key, val) {
    if (val !== undefined) {
      // Don't enforce the maximum now, because the scroll view could change
      // height and we want our content to stay put when it does.
      if (val >= 0) {
        this._value = val ;
      }
    } else {
      var value = this._value || 0 ; // default value is at top/left
      return Math.min(value, this.get('maximum')) ;
    }
  }.property('maximum').cacheable(),

  /**
    The maximum offset value for the scroller.  This will be used to calculate
    the internal height/width of the scroller itself. It is not necessarily
    the same as the height of a scroll view's content view.

    When set less than the height of the scroller, the scroller is disabled.

    @property {Number}
  */
  maximum: 0,

  /**
    YES if enable scrollbar, NO to disable it.  Scrollbars will automatically
    disable if the maximum scroll width does not exceed their capacity.

    @property
  */
  isEnabled: YES,

  /**
    Determine the layout direction.  Determines whether the scrollbar should
    appear horizontal or vertical.  This must be set when the view is created.
    Changing this once the view has been created will have no effect.

    @property
  */
  layoutDirection: SC.LAYOUT_VERTICAL,
  // ..........................................................
  // DISPLAY METRICS
  //

  capSize: 10,

  capOverlapSize: 7,

  downButtonSize: 17,

  upButtonSize: 23,

  trackLength: function() {
    var frame = this.get('frame'), size;

    switch (this.get('layoutDirection')) {
      case SC.LAYOUT_VERTICAL:
        size = frame.height;
        break;
      case SC.LAYOUT_HORIZONTAL:
        size = frame.width;
        break;
    }
    return size - (this.capSize-this.capOverlapSize) - this.downButtonSize - (this.upButtonSize-this.capOverlapSize);
  },

  /**
    Returns the owner view property the scroller should modify.  If this
    property is non-null and the owner view defines this property, then the
    scroller will automatically update this property whenever its own value
    changes.

    The default value of this property is computed based on the
    layoutDirection.  You can override this property to provide your own
    calculation if necessary or to return null if you want to disable this
    behavior.

    @property {String}
  */
  ownerScrollValueKey: function() {
    var key = null ;
    switch(this.get('layoutDirection')) {
      case SC.LAYOUT_VERTICAL:
        key = 'verticalScrollOffset' ;
        break ;
      case SC.LAYOUT_HORIZONTAL:
        key = 'horizontalScrollOffset' ;
        break ;
      default:
        key = null ;
    }
    return key ;
  }.property('layoutDirection').cacheable(),

  // ..........................................................
  // INTERNAL SUPPORT
  //

  displayProperties: 'maximum isEnabled layoutDirection'.w(),

  render: function(context, firstTime) {
    context.addClass('sc-scroller');

    switch (this.get('layoutDirection')) {
      case SC.LAYOUT_VERTICAL:
        context.addClass('vertical');
        break;
      case SC.LAYOUT_HORIZONTAL:
        context.addClass('horizontal');
        break;
    }
    if (firstTime) {
      context.push('

');
      context.push('

');
      context.push('

');
      context.push('

');
      context.push('

');
      this._top = 3;
    }
  },

  mouseDown: function(evt) {
    var target = evt.target;
    if (target.className.indexOf('thumb') >= 0 || target.parentElement.className.indexOf('thumb') >= 0) {
      console.log('thumb clicked');
    }
    this._thumbOffset = this.convertFrameFromView({ y: evt.pageY }).y - this._top ;
    this._startY = evt.pageY;
    this._startTop = this._top;
    console.log('Offset: %@'.fmt(this._thumbOffset));
    return YES;
  },

  mouseDragged: function(evt) {
    console.log('dragged');
    this._top = Math.max(0, this._startTop + (evt.pageY - this._startY));
    var frame = this.get('frame');
    // this.$('.thumb').css('top', this._top);
  this.set('value', Math.round((this._top / frame.height) * (this.get('maximum')-frame.height)));
    return YES;
  },

  // after 50msec, fire event again
  _sc_scroller_armScrollTimer: function() {
    if (!this._sc_scrollTimer) {
      SC.RunLoop.begin() ;
      var method = this._sc_scroller_scrollDidChange ;
      this._sc_scrollTimer = this.invokeLater(method, 50) ;
      SC.RunLoop.end() ;
    }
  },

  _sc_scroller_frameDidChange: function() {
    var max = this.get('maximum'), height = this.get('frame').height;
    height = Math.max((height/max)*height, 20);
    var thumb = this.$('.thumb');

    if (thumb) {
      thumb.css('height', height);
      this.$('.thumb .center').css('height', Math.max(height-14,0));
    }
    this._thumbHeight = height;
  }.observes('frame', 'maximum'),

  _sc_scroller_scrollDidChange: function() {
    var now = Date.now(), last = this._sc_lastScroll ;
    if (last && (now-last)<50) return this._sc_scroller_armScrollTimer() ;
    this._sc_scrollTimer = null ;
    this._sc_lastScroll = now ;

    SC.RunLoop.begin();

    if (!this.get('isEnabled')) return ; // nothing to do.

    var layer = this.get('layer'), scroll = 0 ;
    switch (this.get('layoutDirection')) {
      case SC.LAYOUT_VERTICAL:
        this._sc_scrollValue = scroll = layer.scrollTop ;
        break ;

      case SC.LAYOUT_HORIZONTAL:
        this._sc_scrollValue = scroll = layer.scrollLeft ;
        break ;
    }
    this.set('value', scroll) ; // will now enforce minimum and maximum

    SC.RunLoop.end();
  },

  /** @private */
  _sc_scroller_valueDidChange: function() {
    var v = this.get('value');
    if (v !== this._sc_scrollValue) {
      var thumb = this.$('.thumb'),
          max = this.get('maximum'),
          frame = this.get('frame');

      if (thumb) {
        switch (this.get('layoutDirection')) {
          case SC.LAYOUT_VERTICAL:
            console.log('v: %@ max: %@'.fmt(v, max));
            this._top = ((v / (max-frame.height)) * (frame.height - this._thumbHeight-36))+3;
            console.log('top: %@ pct: %@ f: %@'.fmt(this._top, (v/(max-frame.height)), frame.height));
            thumb.css('top', this._top);
            break ;
          case SC.LAYOUT_HORIZONTAL:
            // layer.scrollLeft = v ;
            break ;
        }
      }
    }
  }.observes('value')

});
<commit_msg>Scroll thumb should stay within the bounds of the track now.<commit_after>// ==========================================================================
// Project:   SproutCore - JavaScript Application Framework
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

/** Vary based on current platform. */
SC.NATURAL_SCROLLER_THICKNESS = 16;

/** @class

  Displays a horizontal or vertical scroller.  You will not usually need to
  work with scroller views directly, but you may override this class to
  implement your own custom scrollers.

  @extends SC.View
  @since SproutCore 1.0
*/
SC.ScrollerView = SC.View.extend({

  classNames: ['sc-scroller-view'],

  // ..........................................................
  // PROPERTIES
  //

  /**
   Used by the SC.ScrollView to properly layout the scrollers.
  */
  scrollerThickness: SC.NATURAL_SCROLLER_THICKNESS,

  /**
    The scroller offset value.  This value will adjust between the minimum
    and maximum values that you set. Default is 0.

    @property
  */
  value: function(key, val) {
    if (val !== undefined) {
      // Don't enforce the maximum now, because the scroll view could change
      // height and we want our content to stay put when it does.
      if (val >= 0) {
        this._value = val ;
      }
    } else {
      var value = this._value || 0 ; // default value is at top/left
      return Math.min(value, this.get('maximum')) ;
    }
  }.property('maximum').cacheable(),

  /**
    The maximum offset value for the scroller.  This will be used to calculate
    the internal height/width of the scroller itself. It is not necessarily
    the same as the height of a scroll view's content view.

    When set less than the height of the scroller, the scroller is disabled.

    @property {Number}
  */
  maximum: 0,

  /**
    YES if enable scrollbar, NO to disable it.  Scrollbars will automatically
    disable if the maximum scroll width does not exceed their capacity.

    @property
  */
  isEnabled: YES,

  /**
    Determine the layout direction.  Determines whether the scrollbar should
    appear horizontal or vertical.  This must be set when the view is created.
    Changing this once the view has been created will have no effect.

    @property
  */
  layoutDirection: SC.LAYOUT_VERTICAL,
  // ..........................................................
  // DISPLAY METRICS
  //

  capSize: 10,

  capOverlapSize: 7,

  downButtonSize: 17,

  upButtonSize: 23,

  trackLength: function() {
    var frame = this.get('frame'), size;

    switch (this.get('layoutDirection')) {
      case SC.LAYOUT_VERTICAL:
        size = frame.height;
        break;
      case SC.LAYOUT_HORIZONTAL:
        size = frame.width;
        break;
    }
    return size - (this.capSize-this.capOverlapSize) - this.downButtonSize - (this.upButtonSize-this.capOverlapSize);
  }.property(),

  /**
    Returns the owner view property the scroller should modify.  If this
    property is non-null and the owner view defines this property, then the
    scroller will automatically update this property whenever its own value
    changes.

    The default value of this property is computed based on the
    layoutDirection.  You can override this property to provide your own
    calculation if necessary or to return null if you want to disable this
    behavior.

    @property {String}
  */
  ownerScrollValueKey: function() {
    var key = null ;
    switch(this.get('layoutDirection')) {
      case SC.LAYOUT_VERTICAL:
        key = 'verticalScrollOffset' ;
        break ;
      case SC.LAYOUT_HORIZONTAL:
        key = 'horizontalScrollOffset' ;
        break ;
      default:
        key = null ;
    }
    return key ;
  }.property('layoutDirection').cacheable(),

  // ..........................................................
  // INTERNAL SUPPORT
  //

  displayProperties: 'maximum isEnabled layoutDirection'.w(),

  render: function(context, firstTime) {
    context.addClass('sc-scroller');

    switch (this.get('layoutDirection')) {
      case SC.LAYOUT_VERTICAL:
        context.addClass('vertical');
        break;
      case SC.LAYOUT_HORIZONTAL:
        context.addClass('horizontal');
        break;
    }
    if (firstTime) {
      context.push('

');
      context.push('

');
      context.push('

');
      context.push('

');
      context.push('

');
      this._top = 3;
    }
  },

  mouseDown: function(evt) {
    var target = evt.target;
    if (target.className.indexOf('thumb') >= 0 || target.parentElement.className.indexOf('thumb') >= 0) {
      this._thumbOffset = this.convertFrameFromView({ y: evt.pageY }).y - this._top ;
      this._startY = evt.pageY;
      this._startTop = this._top;
      return YES;
    }
  },

  mouseDragged: function(evt) {
    this._top = Math.max(0, this._startTop + (evt.pageY - this._startY));
    var frame = this.get('frame');
    // this.$('.thumb').css('top', this._top);
  this.set('value', Math.round((this._top / (this.get('trackLength')-this._thumbHeight)) * (this.get('maximum')-frame.height)));
    return YES;
  },

  _sc_scroller_frameDidChange: function() {
    var max = this.get('maximum'), height = this.get('frame').height;
    height = Math.ceil(Math.max((height/max)*height, 20));
    this._thumbHeight = height;
    var thumb = this.$('.thumb');

    if (thumb) {
      thumb.css('height', height);
      this.$('.thumb .center').css('height', Math.max(height-14,0));
    }
  }.observes('frame', 'maximum'),

  _sc_scroller_scrollDidChange: function() {
    var now = Date.now(), last = this._sc_lastScroll ;
    if (last && (now-last)<50) return this._sc_scroller_armScrollTimer() ;
    this._sc_scrollTimer = null ;
    this._sc_lastScroll = now ;

    SC.RunLoop.begin();

    if (!this.get('isEnabled')) return ; // nothing to do.

    var layer = this.get('layer'), scroll = 0 ;
    switch (this.get('layoutDirection')) {
      case SC.LAYOUT_VERTICAL:
        this._sc_scrollValue = scroll = layer.scrollTop ;
        break ;

      case SC.LAYOUT_HORIZONTAL:
        this._sc_scrollValue = scroll = layer.scrollLeft ;
        break ;
    }
    this.set('value', scroll) ; // will now enforce minimum and maximum

    SC.RunLoop.end();
  },

  /** @private */
  _sc_scroller_valueDidChange: function() {
    var v = this.get('value');
    if (v !== this._sc_scrollValue) {
      var thumb = this.$('.thumb'),
          max = this.get('maximum'),
          frame = this.get('frame');

      if (thumb) {
        switch (this.get('layoutDirection')) {
          case SC.LAYOUT_VERTICAL:
            var pct = (v/(max-this.get('frame').height));
            if (pct > 1) pct = 1;
            var totalLen = (this.get('trackLength')-this._thumbHeight);
            this._top = Math.round(pct * totalLen + 3);
            thumb.css('top', this._top);
            break ;
          case SC.LAYOUT_HORIZONTAL:
            // layer.scrollLeft = v ;
            break ;
        }
      }
    }
  }.observes('value')

});
<|endoftext|>


<commit_before>export default function (options) {
	var model = options.model
	var view = options.view

	var actions = {}
	var effects = options.effects || {}
	var reducers = options.reducers || options.update || {}
	var subs = options.subscriptions

	var router = options.router || Function.prototype

	var node
	var root
	var batch = []

	var hooks = merge({
		onAction: Function.prototype,
		onUpdate: Function.prototype,
		onError: function (err) {
			throw err
		}
	}, options.hooks)

	createActions(0, effects, actions)
	createActions(1, reducers, actions)

	ready(function () {
		root = options.root || document.body.appendChild(document.createElement("div"))

		if (typeof view === "function") {
			render(model, view)
		}

		router(function (newView) {
			render(model, view = newView ? newView : view, node)
		}, options)

		for (var key in subs) {
			subs[key](model, actions, hooks.onError)
		}
	})

	function createActions(isReducer, namespace, _actions, _name) {
		Object.keys(namespace).forEach(function (key) {
			if (!_actions[key]) {
				_actions[key] = {}
			}

			var name = _name ? _name + '.' + key : key
			var prop = namespace[key]

			if (typeof prop === "function") {
				_actions[key] = function (data) {
					hooks.onAction(name, data)

					if (isReducer) {
						hooks.onUpdate(model, model = merge(model, prop(model, data)), data)
						render(model, view, node)
					} else {
						prop(model, actions, data, hooks.onError)
					}
				}
			} else {
				createActions(isReducer, prop, _actions[key], name)
			}
		})
	}

	function ready(cb) {
		if (document.readyState !== "loading") {
			cb()
		} else {
			document.addEventListener("DOMContentLoaded", cb)
		}
	}

	function render(model, view, lastNode) {
		patch(root, node = view(model, actions), lastNode, 0)

		for (var i = 0; i < batch.length; i++) {
			batch[i]()
		}

		batch = []
	}

	function merge(a, b) {
		var obj = {}, key

		if (isPrimitive(typeof b) || Array.isArray(b)) {
			return b
		}

		for (key in a) {
			obj[key] = a[key]
		}
		for (key in b) {
			obj[key] = b[key]
		}

		return obj
	}

	function isPrimitive(type) {
		return type === "string" || type === "number" || type === "boolean"
	}

	function defer(fn, data) {
		setTimeout(function () {
			fn(data)
		}, 0)
	}

	function shouldUpdate(a, b) {
		return a.tag !== b.tag
			|| typeof a !== typeof b
			|| isPrimitive(typeof a) && a !== b
	}

	function createElementFrom(node) {
		var element

		if (isPrimitive(typeof node)) {
			element = document.createTextNode(node)

		} else {
			element = node.data && node.data.ns
				? document.createElementNS(node.data.ns, node.tag)
				: document.createElement(node.tag)

			for (var name in node.data) {
				if (name === "oncreate") {
					defer(node.data[name], element)
				} else {
					setElementData(element, name, node.data[name])
				}
			}

			for (var i = 0; i < node.children.length; i++) {
				var childNode = node.children[i]

				if (childNode !== undefined && typeof childNode !== "boolean" && childNode !== null) {
					element.appendChild(createElementFrom(childNode))
				}
			}
		}

		return element
	}

	function removeElementData(element, name, value) {
		element.removeAttribute(name === "className" ? "class" : name)

		if (typeof value === "boolean" || value === "true" || value === "false") {
			element[name] = false
		}
	}

	function setElementData(element, name, value, oldValue) {
		if (name === "style") {
			for (var i in value) {
				element.style[i] = value[i]
			}

		} else if (name[0] === "o" && name[1] === "n") {
			var event = name.substr(2)
			element.removeEventListener(event, oldValue)
			element.addEventListener(event, value)

		} else {
			if (value === "false" || value === false) {
				element.removeAttribute(name)
				element[name] = false
			} else {
				element.setAttribute(name, value)
				if (element.namespaceURI !== "http://www.w3.org/2000/svg") {
					element[name] = value
				}
			}
		}
	}

	function updateElementData(element, data, oldData) {
		for (var name in merge(oldData, data)) {
			var value = data[name]
			var oldValue = oldData[name]
			var realValue = element[name]

			if (value === undefined) {
				removeElementData(element, name, oldValue)

			} else if (name === "onupdate") {
				defer(value, element)

			} else if (value !== oldValue
				|| typeof realValue === "boolean" && realValue !== value) {
				setElementData(element, name, value, oldValue)
			}
		}
	}

	function patch(parent, node, oldNode, index) {
		if (oldNode === undefined) {
			parent.appendChild(createElementFrom(node))

		} else if (node === undefined) {
			var element = parent.childNodes[index]
			batch.push(parent.removeChild.bind(parent, element))

			if (oldNode && oldNode.data && oldNode.data.onremove) {
				defer(oldNode.data.onremove, element)
			}

		} else if (shouldUpdate(node, oldNode)) {
			var element = parent.childNodes[index]
			if (typeof node === "boolean") {
				parent.removeChild(element)

			} else {
				var newElement = createElementFrom(node)
				if (element === undefined) {
					parent.appendChild(newElement)
				} else {
					parent.replaceChild(newElement, element)
				}
			}

		} else if (node.tag) {
			var element = parent.childNodes[index]
			updateElementData(element, node.data, oldNode.data)

			var len = node.children.length, oldLen = oldNode.children.length

			for (var i = 0; i < len || i < oldLen; i++) {
				var child = node.children[i]
				if (child !== null) {
					patch(element, child, oldNode.children[i], i)
				}
			}
		}
	}
}
<commit_msg>Effect actions return the value their effect function returns.<commit_after>export default function (options) {
	var model = options.model
	var view = options.view

	var actions = {}
	var effects = options.effects || {}
	var reducers = options.reducers || options.update || {}
	var subs = options.subscriptions

	var router = options.router || Function.prototype

	var node
	var root
	var batch = []

	var hooks = merge({
		onAction: Function.prototype,
		onUpdate: Function.prototype,
		onError: function (err) {
			throw err
		}
	}, options.hooks)

	createActions(false, effects, actions)
	createActions(true, reducers, actions)

	ready(function () {
		root = options.root || document.body.appendChild(document.createElement("div"))

		if (typeof view === "function") {
			render(model, view)
		}

		router(function (newView) {
			render(model, view = newView ? newView : view, node)
		}, options)

		for (var key in subs) {
			subs[key](model, actions, hooks.onError)
		}
	})

	function createActions(isReducer, collection, container, name) {
		Object.keys(collection).forEach(function (namespace) {
			if (!container[namespace]) {
				container[namespace] = {}
			}

			var action = collection[namespace]

			if (typeof action === "function") {
				name = name ? name + "." + namespace : namespace

				container[namespace] = function (data) {
					hooks.onAction(name, data)

					if (isReducer) {
						hooks.onUpdate(model, model = merge(model, action(model, data)), data)
						render(model, view, node)
						return actions
					} else {
						return action(model, actions, data, hooks.onError)
					}
				}
			} else {
				createActions(isReducer, action, container[namespace], name)
			}
		})
	}

	function ready(cb) {
		if (document.readyState !== "loading") {
			cb()
		} else {
			document.addEventListener("DOMContentLoaded", cb)
		}
	}

	function render(model, view, lastNode) {
		patch(root, node = view(model, actions), lastNode, 0)

		for (var i = 0; i < batch.length; i++) {
			batch[i]()
		}

		batch = []
	}

	function merge(a, b) {
		var obj = {}, key

		if (isPrimitive(typeof b) || Array.isArray(b)) {
			return b
		}

		for (key in a) {
			obj[key] = a[key]
		}
		for (key in b) {
			obj[key] = b[key]
		}

		return obj
	}

	function isPrimitive(type) {
		return type === "string" || type === "number" || type === "boolean"
	}

	function defer(fn, data) {
		setTimeout(function () {
			fn(data)
		}, 0)
	}

	function shouldUpdate(a, b) {
		return a.tag !== b.tag
			|| typeof a !== typeof b
			|| isPrimitive(typeof a) && a !== b
	}

	function createElementFrom(node) {
		var element

		if (isPrimitive(typeof node)) {
			element = document.createTextNode(node)

		} else {
			element = node.data && node.data.ns
				? document.createElementNS(node.data.ns, node.tag)
				: document.createElement(node.tag)

			for (var name in node.data) {
				if (name === "oncreate") {
					defer(node.data[name], element)
				} else {
					setElementData(element, name, node.data[name])
				}
			}

			for (var i = 0; i < node.children.length; i++) {
				var childNode = node.children[i]

				if (childNode !== undefined && typeof childNode !== "boolean" && childNode !== null) {
					element.appendChild(createElementFrom(childNode))
				}
			}
		}

		return element
	}

	function removeElementData(element, name, value) {
		element.removeAttribute(name === "className" ? "class" : name)

		if (typeof value === "boolean" || value === "true" || value === "false") {
			element[name] = false
		}
	}

	function setElementData(element, name, value, oldValue) {
		if (name === "style") {
			for (var i in value) {
				element.style[i] = value[i]
			}

		} else if (name[0] === "o" && name[1] === "n") {
			var event = name.substr(2)
			element.removeEventListener(event, oldValue)
			element.addEventListener(event, value)

		} else {
			if (value === "false" || value === false) {
				element.removeAttribute(name)
				element[name] = false
			} else {
				element.setAttribute(name, value)
				if (element.nsURI !== "http://www.w3.org/2000/svg") {
					element[name] = value
				}
			}
		}
	}

	function updateElementData(element, data, oldData) {
		for (var name in merge(oldData, data)) {
			var value = data[name]
			var oldValue = oldData[name]
			var realValue = element[name]

			if (value === undefined) {
				removeElementData(element, name, oldValue)

			} else if (name === "onupdate") {
				defer(value, element)

			} else if (value !== oldValue
				|| typeof realValue === "boolean" && realValue !== value) {
				setElementData(element, name, value, oldValue)
			}
		}
	}

	function patch(parent, node, oldNode, index) {
		if (oldNode === undefined) {
			parent.appendChild(createElementFrom(node))

		} else if (node === undefined) {
			var element = parent.childNodes[index]
			batch.push(parent.removeChild.bind(parent, element))

			if (oldNode && oldNode.data && oldNode.data.onremove) {
				defer(oldNode.data.onremove, element)
			}

		} else if (shouldUpdate(node, oldNode)) {
			var element = parent.childNodes[index]
			if (typeof node === "boolean") {
				parent.removeChild(element)

			} else {
				var newElement = createElementFrom(node)
				if (element === undefined) {
					parent.appendChild(newElement)
				} else {
					parent.replaceChild(newElement, element)
				}
			}

		} else if (node.tag) {
			var element = parent.childNodes[index]
			updateElementData(element, node.data, oldNode.data)

			var len = node.children.length, oldLen = oldNode.children.length

			for (var i = 0; i < len || i < oldLen; i++) {
				var child = node.children[i]
				if (child !== null) {
					patch(element, child, oldNode.children[i], i)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>import React from 'react'
import { Modal } from 'components'
import { connect } from 'react-redux'

const ModalContainer = React.createClass({
  render () {
    return (<Modal />)
  },
})

function mapStateToProps ({modal, users}) {
  const duckTextLength = modal.duckText.length
  return {
    user: users[users.authedId] ? users[users.authedId.info] : {},
    duckText: modal.duckText,
    isOpen: modal.isOpen,
    isSubmitDisabled: duckTextLength <= 0 || duckTextLength < 140,
  }
}

export default ModalContainer
<commit_msg>Import and bind action creators<commit_after>import React from 'react'
import { bindActionCreators } from 'redux'
import { Modal } from 'components'
import { connect } from 'react-redux'
import * as modalActionCreators from 'redux/modules/modal'

const ModalContainer = React.createClass({
  render () {
    return (<Modal />)
  },
})

function mapStateToProps ({modal, users}) {
  const duckTextLength = modal.duckText.length
  return {
    user: users[users.authedId] ? users[users.authedId.info] : {},
    duckText: modal.duckText,
    isOpen: modal.isOpen,
    isSubmitDisabled: duckTextLength <= 0 || duckTextLength < 140,
  }
}

function mapDispatchToProps (dispatch) {
  return bindActionCreators(modalActionCreators, dispatch)
}

export default ModalContainer
<|endoftext|>


<commit_before>/* eslint prefer-arrow-callback: "off" */

const fs = require('fs')
const childProcess = require('child_process')
const pify = require('pify')

const fixturePaths = {
  problemForEach: '../fixtures/mockUtils-problem/higherOrder/forEach/forEach.js',
  problemMap: '../fixtures/mockUtils-problem/higherOrder/map/map.js',
  problemPrint: '../fixtures/mockUtils-problem/print/print.js',
  solutionForEach: '../fixtures/mockUtils-solution/higherOrder/forEach/forEach.js',
  solutionMap: '../fixtures/mockUtils-solution/higherOrder/map/map.js',
  solutionPrint: '../fixtures/mockUtils-solution/print/print.js'
}

const results = {}
results.problemForEach = `// Write a function that invokes a callback on each element in an array
// problem code
const forEach = function () {
  // your code here

}

// shared code
const droids = ['R2-D2', 'C-3PO', 'BB-8']

forEach(droids, console.log)
// R2-D2
// C-3PO
// BB-8
`

results.problemMap = `// Write a function that returns an array of mapped elements
// problem code
const map = function () {
  // your code here

}

// shared code
const numbers = [1, 2, 3, 4, 5]

map(numbers, n => n ** 2)
// [1, 4, 9, 16, 25]
`

results.problemPrint = `// Write a function that prints a value to the console
// problem code
const print = function () {
  // your code here

}

// shared code
const jedi = 'Qui-Gon'

print(jedi)
// Qui-Gon
`

results.solutionForEach = `// Write a function that invokes a callback on each element in an array
// solution code
const forEach = function (array, cb) {
  for (let i = 0; i < array.length; i++) {
    cb(array[i], i, array)
  }
}

// shared code
const droids = ['R2-D2', 'C-3PO', 'BB-8']

forEach(droids, console.log)
// R2-D2
// C-3PO
// BB-8
`

results.solutionMap = `// Write a function that returns an array of mapped elements
// solution code
const map = function (array, cb) {
  const result = []
  for (let i = 0; i < array.length; i++) {
    result.push(cb(array[i], i, array))
  }

  return result
}

// shared code
const numbers = [1, 2, 3, 4, 5]

map(numbers, n => n ** 2)
// [1, 4, 9, 16, 25]
`

results.solutionPrint = `// Write a function that prints a value to the console
// solution code
const print = function (value) {
  console.log(value)
}

// shared code
const jedi = 'Qui-Gon'

print(jedi)
// Qui-Gon
`

describe('CLI', function () {
  it('should write the correct content', function (done) {
    const pathKeys = Object.keys(fixturePaths)
    pify(childProcess.execFile)(`${__dirname}/../../bin/cli.js`, [`${__dirname}/../fixtures/mockUtils`])
      .then(() => {
        // Store output file contents in an array
        return Promise.all(pathKeys.map(pathKey => {
          const path = fixturePaths[pathKey]

          return pify(fs.readFile)(`${__dirname}/${path}`, 'utf8')
        }))
      })
      .then(files => {
        // Assert whether the contents of each output file is correct
        files.forEach((file, i) => {
          const pathKey = pathKeys[i]
          const result = results[pathKey]

          expect(file).toEqual(result)
          done()
        })
      })
      .catch(done.fail)
  })
})
<commit_msg>Use synchronous methods for tests<commit_after>/* eslint prefer-arrow-callback: "off" */

const {readFileSync} = require('fs')
const {execFileSync} = require('child_process')

const fixturePaths = {
  problemForEach: '../fixtures/mockUtils-problem/higherOrder/forEach/forEach.js',
  problemMap: '../fixtures/mockUtils-problem/higherOrder/map/map.js',
  problemPrint: '../fixtures/mockUtils-problem/print/print.js',
  solutionForEach: '../fixtures/mockUtils-solution/higherOrder/forEach/forEach.js',
  solutionMap: '../fixtures/mockUtils-solution/higherOrder/map/map.js',
  solutionPrint: '../fixtures/mockUtils-solution/print/print.js'
}

const results = {}
results.problemForEach = `// Write a function that invokes a callback on each element in an array
// problem code
const forEach = function () {
  // your code here

}

// shared code
const droids = ['R2-D2', 'C-3PO', 'BB-8']

forEach(droids, console.log)
// R2-D2
// C-3PO
// BB-8
`

results.problemMap = `// Write a function that returns an array of mapped elements
// problem code
const map = function () {
  // your code here

}

// shared code
const numbers = [1, 2, 3, 4, 5]

map(numbers, n => n ** 2)
// [1, 4, 9, 16, 25]
`

results.problemPrint = `// Write a function that prints a value to the console
// problem code
const print = function () {
  // your code here

}

// shared code
const jedi = 'Qui-Gon'

print(jedi)
// Qui-Gon
`

results.solutionForEach = `// Write a function that invokes a callback on each element in an array
// solution code
const forEach = function (array, cb) {
  for (let i = 0; i < array.length; i++) {
    cb(array[i], i, array)
  }
}

// shared code
const droids = ['R2-D2', 'C-3PO', 'BB-8']

forEach(droids, console.log)
// R2-D2
// C-3PO
// BB-8
`

results.solutionMap = `// Write a function that returns an array of mapped elements
// solution code
const map = function (array, cb) {
  const result = []
  for (let i = 0; i < array.length; i++) {
    result.push(cb(array[i], i, array))
  }

  return result
}

// shared code
const numbers = [1, 2, 3, 4, 5]

map(numbers, n => n ** 2)
// [1, 4, 9, 16, 25]
`

results.solutionPrint = `// Write a function that prints a value to the console
// solution code
const print = function (value) {
  console.log(value)
}

// shared code
const jedi = 'Qui-Gon'

print(jedi)
// Qui-Gon
`

describe('CLI', function () {
  it('should write the correct content', function (done) {
    try {
      const pathKeys = Object.keys(fixturePaths)
      execFileSync(`${__dirname}/../../bin/cli.js`, [`${__dirname}/../fixtures/mockUtils`])
      const files = pathKeys.map(pathKey => {
        const path = fixturePaths[pathKey]

        return readFileSync(`${__dirname}/${path}`, 'utf8')
      })

      files.forEach((file, i) => {
        const pathKey = pathKeys[i]
        const result = results[pathKey]

        expect(file).toEqual(result)
        done()
      })
    } catch (err) {
      done.fail(err)
    }
  })
})
<|endoftext|>


<commit_before>'use strict';
/* A newable factory to handle xml files using the Perseus Treebank Schema
 *
 * The constructor functions takes a configuration object (that typically
 * contains a resource object for this service).
 *
 */
angular.module('arethusa').factory('TreebankRetriever', [
  'configurator',
  'documentStore',
  'retrieverHelper',
  'idHandler',
  'commons',
  function (configurator, documentStore, retrieverHelper, idHandler, commons) {
    function parseDocument(json, docId) {
      var sentences = arethusaUtil.toAry(json.treebank.sentence);
      return parseSentences(sentences, docId);
    }

    function parseSentences(sentences, docId) {
      return sentences.map(function(sentence) {
        var cite = extractCiteInfo(sentence);
        var words = arethusaUtil.toAry(sentence.word);
        return parseSentence(sentence, sentence._id, docId, cite);
      });
    }

    function parseSentence(sentence, id, docId, cite) {
      var words = aU.toAry(sentence.word);
      var tokens = {};

      var artificials = extractArtificials(words, id);

      angular.forEach(words, function (word, i) {
        var token = parseWord(word, id, docId, artificials);
        tokens[token.id] = token;
      });

      var sentenceObj = commons.sentence(tokens, cite);
      retrieverHelper.generateId(sentenceObj, id, id, docId);

      return sentenceObj;
    }

    function parseWord(word, sentenceId, docId, artificials) {
      // One could formalize this to real rules that are configurable...
      //
      // Remember that attributes of the converted xml are prefixed with underscore
      var token = commons.token(word._form, sentenceId);

      parseMorphology(token, word);
      parseRelation(token, word);
      parseSg(token, word);
      parseArtificial(token, word);
      parseHead(token, word, artificials);

      parseTermination(token);

      var internalId = generateInternalId(word, sentenceId);
      var sourceId   = word._id;
      retrieverHelper.generateId(token, internalId, sourceId, docId);

      return token;
    }

    function parseHead(token, word, artificials) {
      var headId = word._head;
      if (angular.isDefined(headId) && headId !== "") {
        var newHead = {};
        var artHeadId = artificials[headId];
        var sentenceId = token.sentenceId;
        newHead.id = artHeadId ? artHeadId : idHandler.getId(headId, sentenceId);

        token.head = newHead;
      }
    }

    function parseMorphology(token, word) {
      token.morphology = {
        lemma: word._lemma,
        postag: word._postag
      };

      var gloss = word._gloss;
      if (gloss) {
        token.morphology.gloss = gloss;
      }
    }

    function parseRelation(token, word) {
      var relation = word._relation;
      var label = (relation && relation !== 'nil') ? relation : '';

      token.relation = {
        label: label
      };
    }

    function parseSg(token, word) {
      var sg = word._sg;
      if (sg && !sg.match(/^\s*$/)) {
        token.sg = { ancestors: sg.split(' ') };
      }
    }

    function parseArtificial(token, word) {
      if (word._artificial) {
        token.artificial = true;
        token.type = word._artificial;
      }
    }

    function parseTermination(token) {
      if (aU.isTerminatingPunctuation(token.string)) {
        token.terminator = true;
      }
    }

    // Helpers

    function extractArtificials(words, sentenceId) {
      return arethusaUtil.inject({}, words, function(memo, word, i) {
        extractArtificial(memo, word, sentenceId);
      });
    }

    function extractArtificial(memo, word, sentenceId) {
      if (word._artificial) {
        memo[word._id] = padWithSentenceId(word._insertion_id, sentenceId);
      }
    }

    function generateInternalId(word, sentenceId) {
      if (word._artificial) {
        return padWithSentenceId(word._insertion_id, sentenceId);
      } else {
        return idHandler.getId(word._id, sentenceId);
      }
    }

    // This is for backwards compatibility - we still might encounter documents, which
    // stored the insertion id without the sentence id. This is a little hacky but a
    // must have.
    function padWithSentenceId(id, sentenceId) {
      return (id.match(/-/)) ? id : idHandler.padIdWithSId(id, sentenceId);
    }

    // Try to support the new as well as the old schema for now
    function extractCiteInfo(sentence) {
      var cite = sentence._cite;
      if (cite) {
        return cite;
      } else {
        var docId = sentence._document_id;
        var subdoc = sentence._subdoc;
        if (subdoc) {
          return docId + ':' + subdoc;
        } else {
          return docId;
        }
      }
    }

    function findAdditionalConfInfo(json) {
      var linkInfo = json.treebank.link;
      var links =  linkInfo ? arethusaUtil.toAry(linkInfo) : [];
      var confs = arethusaUtil.inject({}, links, function(memo, link) {
        memo[link._title] = link._href;
      });
      var format = json.treebank._format;
      if (format) {
        // For backwards compatibility to older days
        if (format == 'aldt') {
          format = 'aldt2' + json.treebank['_xml:lang'];
        }
        confs.fullFile = format;
      }
      return confs;
    }

    return function (conf) {
      var self = this;
      var resource = configurator.provideResource(conf.resource);
      var docId = conf.docIdentifier;

      this.preselections = retrieverHelper.getPreselections(conf);

      this.parse = function(xml, callback) {
        var json = arethusaUtil.xml2json(xml);
        var moreConf = findAdditionalConfInfo(json);
        var doc = commons.doc(xml, json, moreConf);

        documentStore.addDocument(docId, doc);
        callback(parseDocument(json, docId));
      };

      this.get = function (callback) {
        resource.get().then(function (res) {
          self.parse(res.data, callback);
        });
      };
    };
  }
]);
<commit_msg>Parse editors/annotators in TreebankRetriever<commit_after>'use strict';
/* A newable factory to handle xml files using the Perseus Treebank Schema
 *
 * The constructor functions takes a configuration object (that typically
 * contains a resource object for this service).
 *
 */
angular.module('arethusa').factory('TreebankRetriever', [
  'configurator',
  'documentStore',
  'retrieverHelper',
  'idHandler',
  'commons',
  'editors',
  function (configurator, documentStore, retrieverHelper,
            idHandler, commons, editors) {
    function parseDocument(json, docId) {
      var annotators = arethusaUtil.toAry(json.treebank.annotator || []);
      parseEditors(annotators, docId);
      var sentences = arethusaUtil.toAry(json.treebank.sentence);
      return parseSentences(sentences, docId);
    }

    function parseEditors(annotators, docId) {
      angular.forEach(annotators, function(annotator, i) {
        if (isHumanAnnotator(annotator)) {
          editors.addEditor(docId, {
            name: annotator.short,
            fullName: annotator.name,
            page: annotator.url,
            mail: annotator.address
          });
        }
      });
    }

    function parseSentences(sentences, docId) {
      return sentences.map(function(sentence) {
        var cite = extractCiteInfo(sentence);
        var words = arethusaUtil.toAry(sentence.word);
        return parseSentence(sentence, sentence._id, docId, cite);
      });
    }

    function parseSentence(sentence, id, docId, cite) {
      var words = aU.toAry(sentence.word);
      var tokens = {};

      var artificials = extractArtificials(words, id);

      angular.forEach(words, function (word, i) {
        var token = parseWord(word, id, docId, artificials);
        tokens[token.id] = token;
      });

      var sentenceObj = commons.sentence(tokens, cite);
      retrieverHelper.generateId(sentenceObj, id, id, docId);

      return sentenceObj;
    }

    function parseWord(word, sentenceId, docId, artificials) {
      // One could formalize this to real rules that are configurable...
      //
      // Remember that attributes of the converted xml are prefixed with underscore
      var token = commons.token(word._form, sentenceId);

      parseMorphology(token, word);
      parseRelation(token, word);
      parseSg(token, word);
      parseArtificial(token, word);
      parseHead(token, word, artificials);

      parseTermination(token);

      var internalId = generateInternalId(word, sentenceId);
      var sourceId   = word._id;
      retrieverHelper.generateId(token, internalId, sourceId, docId);

      return token;
    }

    function parseHead(token, word, artificials) {
      var headId = word._head;
      if (angular.isDefined(headId) && headId !== "") {
        var newHead = {};
        var artHeadId = artificials[headId];
        var sentenceId = token.sentenceId;
        newHead.id = artHeadId ? artHeadId : idHandler.getId(headId, sentenceId);

        token.head = newHead;
      }
    }

    function parseMorphology(token, word) {
      token.morphology = {
        lemma: word._lemma,
        postag: word._postag
      };

      var gloss = word._gloss;
      if (gloss) {
        token.morphology.gloss = gloss;
      }
    }

    function parseRelation(token, word) {
      var relation = word._relation;
      var label = (relation && relation !== 'nil') ? relation : '';

      token.relation = {
        label: label
      };
    }

    function parseSg(token, word) {
      var sg = word._sg;
      if (sg && !sg.match(/^\s*$/)) {
        token.sg = { ancestors: sg.split(' ') };
      }
    }

    function parseArtificial(token, word) {
      if (word._artificial) {
        token.artificial = true;
        token.type = word._artificial;
      }
    }

    function parseTermination(token) {
      if (aU.isTerminatingPunctuation(token.string)) {
        token.terminator = true;
      }
    }

    // Helpers

    function extractArtificials(words, sentenceId) {
      return arethusaUtil.inject({}, words, function(memo, word, i) {
        extractArtificial(memo, word, sentenceId);
      });
    }

    function extractArtificial(memo, word, sentenceId) {
      if (word._artificial) {
        memo[word._id] = padWithSentenceId(word._insertion_id, sentenceId);
      }
    }

    function generateInternalId(word, sentenceId) {
      if (word._artificial) {
        return padWithSentenceId(word._insertion_id, sentenceId);
      } else {
        return idHandler.getId(word._id, sentenceId);
      }
    }

    // This is for backwards compatibility - we still might encounter documents, which
    // stored the insertion id without the sentence id. This is a little hacky but a
    // must have.
    function padWithSentenceId(id, sentenceId) {
      return (id.match(/-/)) ? id : idHandler.padIdWithSId(id, sentenceId);
    }

    // Try to support the new as well as the old schema for now
    function extractCiteInfo(sentence) {
      var cite = sentence._cite;
      if (cite) {
        return cite;
      } else {
        var docId = sentence._document_id;
        var subdoc = sentence._subdoc;
        if (subdoc) {
          return docId + ':' + subdoc;
        } else {
          return docId;
        }
      }
    }

    function findAdditionalConfInfo(json) {
      var linkInfo = json.treebank.link;
      var links =  linkInfo ? arethusaUtil.toAry(linkInfo) : [];
      var confs = arethusaUtil.inject({}, links, function(memo, link) {
        memo[link._title] = link._href;
      });
      var format = json.treebank._format;
      if (format) {
        // For backwards compatibility to older days
        if (format == 'aldt') {
          format = 'aldt2' + json.treebank['_xml:lang'];
        }
        confs.fullFile = format;
      }
      return confs;
    }

    function isHumanAnnotator(annotator) {
      // Machine services don't come with a name attached to them
      return annotator.name && annotator.short;
    }

    return function (conf) {
      var self = this;
      var resource = configurator.provideResource(conf.resource);
      var docId = conf.docIdentifier;

      this.preselections = retrieverHelper.getPreselections(conf);

      this.parse = function(xml, callback) {
        var json = arethusaUtil.xml2json(xml);
        var moreConf = findAdditionalConfInfo(json);
        var doc = commons.doc(xml, json, moreConf);

        documentStore.addDocument(docId, doc);
        callback(parseDocument(json, docId));
      };

      this.get = function (callback) {
        resource.get().then(function (res) {
          self.parse(res.data, callback);
        });
      };
    };
  }
]);
<|endoftext|>