<commit_before>// @flow

import { table } from 'table';
import { red } from 'chalk';
import R from 'ramda';

import gql from '../gql';
import { runGQLQuery } from '../query';
import {
  printErrors,
  printNoConfigError,
  printGraphQLErrors,
} from '../printErrors';

import typeof Yargs from 'yargs';
import type { BaseArgs } from '.';

const tableConfig = {
  columns: {
    // $FlowIssue: Flow doesn't understand numbers as keys https://github.com/facebook/flow/issues/380
    0: {
      alignment: 'left',
      minWidth: 15,
    },
    // $FlowIssue: Flow doesn't understand numbers as keys https://github.com/facebook/flow/issues/380
    1: {
      alignment: 'left',
      minWidth: 15,
    },
  },
};

// Common filter
const onlyValues = ([, value]: [string, string]) =>
  value != null && value !== '';

const name = 'info';
const description = 'Show info about sites or sitegroups';

export async function setup(yargs: Yargs): Promise<Object> {
  return yargs
    .usage(`$0 ${name} [site@branch] - ${description}`)
    .options({
      sitegroup: {
        demandOption: false,
        describe: 'Specify a sitegroup to show information about',
        type: 'string',
      },
    })
    .alias('s', 'sitegroup')
    .example(
      `$0 ${name}`,
      'Show information about the sitegroup configured in .amazeeio.yml',
    )
    .example(
      `$0 ${name} mysite`,
      'Show information about site "mysite" (only works if the site only has a single branch) (sitegroup as stated by config)',
    )
    .example(
      `$0 ${name} mysite@prod`,
      'Show information about branch "prod" of site "mysite" (sitegroup as stated by config)',
    )
    .example(
      `$0 ${name} -s mysitegroup mysite`,
      'Show information about site "mysite" in sitegroup "somesitegroup"',
    ).argv;
}

type SiteGroupInfoArgs = {
  sitegroup: string,
  clog: typeof console.log,
  cerr: typeof console.error,
};

export async function sitegroupInfo({
  sitegroup,
  clog,
  cerr,
}: SiteGroupInfoArgs): Promise<number> {
  const query = gql`
    query querySites($sitegroup: String!) {
      siteGroupByName(name: $sitegroup) {
        gitUrl
        siteGroupName
        slack {
          webhook
          channel
          informStart
          informChannel
        }
        client {
          clientName
        }
        sites {
          siteName
          siteBranch
        }
      }
    }
  `;

  const result = await runGQLQuery({
    query,
    variables: { sitegroup },
  });

  const { errors } = result;
  if (errors != null) {
    return printGraphQLErrors(cerr, ...errors);
  }

  const sitegroupData = R.path(['data', 'siteGroupByName'])(result);

  if (sitegroupData == null) {
    return printErrors(clog, `No sitegroup '${sitegroup}' found`);
  }

  const sites = R.compose(
    R.map(({ siteName, siteBranch }) => `${siteName}:${siteBranch}`),
    R.pathOr([], ['data', 'siteGroupByName', 'sites']),
  )(result);

  const formatSlack = (slack) => {
    if (slack == null) {
      return '';
    }

    const webhook = R.prop('webhook', slack);
    const channel = R.prop('channel', slack);

    return `${channel} -> ${webhook}`;
  };

  const tableBody = [
    ['Sitegroup Name', R.prop('siteGroupName', sitegroupData)],
    ['Git Url', R.prop('gitUrl', sitegroupData)],
    ['Slack', formatSlack(R.prop('slack', sitegroupData))],
    ['Sites', R.join(', ', sites)],
  ];

  const tableData = R.filter(onlyValues)(tableBody);

  clog(`Details for '${sitegroup}':`);
  clog(table(tableData, tableConfig));

  return 0;
}

type SiteInfoArgs = {
  site: string,
  branch?: string,
  sitegroup: string,
  clog: typeof console.log,
  cerr: typeof console.error,
};

export async function siteInfo({
  sitegroup,
  site,
  branch,
  clog,
  cerr,
}: SiteInfoArgs): Promise<number> {
  // site[@branch]
  const siteBranchStr = `${site}${branch != null ? `@${branch}` : ''}`;

  const query = gql`
    query querySites($sitegroup: String!) {
      siteGroupByName(name: $sitegroup) {
        gitUrl
        sites {
          id
          siteHost
          siteName
          siteBranch
          siteEnvironment
          serverInfrastructure
          serverIdentifier
          serverNames
          webRoot
          drupalVersion
          SSLCertificateType
          FPMProfile
          domains
          redirectDomains
          redirects
          uid
          dbUser
          cron {
            type
            minute
          }
          customCron
          envVariables
          noPrefixenvVariables
          phpValues
          phpAdminFlags
          xdebug
          nginxSitespecific
          nginxSiteconfig
          solrEnabled
          redisEnabled
          sshKeys
          phpVersion
          redirectToHttps
          ensure
          upstreamURL
          apc
          basicAuth {
            username
          }
        }
      }
    }
  `;

  const result = await runGQLQuery({
    query,
    variables: { sitegroup },
  });

  const { errors } = result;
  if (errors != null) {
    return printGraphQLErrors(cerr, ...errors);
  }

  // There might be a case where I just have one site + branch,
  // so we will just filter for branch, if a branch was actually
  // given. If there are multiple matches, we will tell the user
  // anyways
  const matchesSiteAndBranch = ({ siteName, siteBranch }) => {
    if (branch != null && siteBranch !== branch) {
      return false;
    }
    return siteName === site;
  };

  const nodes = R.compose(
    R.filter(matchesSiteAndBranch),
    R.pathOr([], ['data', 'siteGroupByName', 'sites']),
  )(result);

  if (nodes.length === 0) {
    clog(red(`No site '${site}' found in sitegroup '${sitegroup}'`));
    return 0;
  }

  // For the case if there was no branch name given to begin with
  if (nodes.length > 1) {
    clog(
      'Multiple sites found with the same name but different branches. The branch can be specified with the "siteName@siteBranch" syntax. For example:',
    );
    R.forEach(({ siteName, siteBranch }) =>
      clog(`-> ${siteName}@${siteBranch}`),
    )(nodes);
    return 0;
  }

  clog(`Details for '${sitegroup} -> ${siteBranchStr}':`);

  // nodes only contains one element, extract it
  const [node] = nodes;

  const formatCron = (cron) => {
    if (cron == null) {
      return '';
    }

    return `Type: ${cron.type} - Minute: ${cron.minute}`;
  };

  const formatArray = (arr) => {
    if (arr == null) {
      return '';
    }

    return R.join(', ', arr);
  };

  // We want to list these fields from the node result
  const tableBody = [
    ['ID', R.prop('id', node)],
    ['Site Name', R.prop('siteName', node)],
    ['Site Branch', R.prop('siteBranch', node)],
    ['Site Env', R.prop('siteEnvironment', node)],
    ['User Id', R.prop('uid', node)],
    ['Server Names', R.prop('serverNames', node)],
    ['Webroot', R.prop('webRoot', node)],
    ['Domains', formatArray(R.prop('domains', node))],
    ['Redirect Domains', formatArray(R.prop('redirectDomains', node))],
    ['SSL Certificate Type', R.prop('SSLCertificateType', node)],
    ['Cron', formatCron(R.prop('cron', node))],
    ['Solr Enabled', R.prop('solrEnabled', node)],
  ];

  const tableData = R.filter(onlyValues)(tableBody);

  clog(table(tableData, tableConfig));

  return 0;
}

type Args = BaseArgs & {
  sitegroup: ?string,
};

export async function run(args: Args): Promise<number> {
  const { config, clog, cerr } = args;

  // FIXME: doesn't handle empty config file case correctly
  if (config == null) {
    return printNoConfigError(cerr);
  }

  const [siteAndBranch] = args._.slice(1);
  const sitegroup = args.sitegroup || config.sitegroup;

  if (siteAndBranch == null) {
    return sitegroupInfo({ sitegroup, clog, cerr });
  }

  const [site, branch] = siteAndBranch.split('@');

  return siteInfo({
    site,
    branch,
    sitegroup,
    clog,
    cerr,
  });
}

export default {
  setup,
  name,
  description,
  run,
};
<commit_msg>Add todo to split up the info command<commit_after>// @flow

import { table } from 'table';
import { red } from 'chalk';
import R from 'ramda';

import gql from '../gql';
import { runGQLQuery } from '../query';
import {
  printErrors,
  printNoConfigError,
  printGraphQLErrors,
} from '../printErrors';

import typeof Yargs from 'yargs';
import type { BaseArgs } from '.';

const tableConfig = {
  columns: {
    // $FlowIssue: Flow doesn't understand numbers as keys https://github.com/facebook/flow/issues/380
    0: {
      alignment: 'left',
      minWidth: 15,
    },
    // $FlowIssue: Flow doesn't understand numbers as keys https://github.com/facebook/flow/issues/380
    1: {
      alignment: 'left',
      minWidth: 15,
    },
  },
};

// Common filter
const onlyValues = ([, value]: [string, string]) =>
  value != null && value !== '';

// TODO: This can probably be split up into two commands (`site` and `sitegroup`):
// - io site mysite
// - io site mysite@prod
// - io site mysite -s mysitegroup
// - io sitegroup mysitegroup
const name = 'info';
const description = 'Show info about sites or sitegroups';

export async function setup(yargs: Yargs): Promise<Object> {
  return yargs
    .usage(`$0 ${name} [site@branch] - ${description}`)
    .options({
      sitegroup: {
        demandOption: false,
        describe: 'Specify a sitegroup to show information about',
        type: 'string',
      },
    })
    .alias('s', 'sitegroup')
    .example(
      `$0 ${name}`,
      'Show information about the sitegroup configured in .amazeeio.yml',
    )
    .example(
      `$0 ${name} mysite`,
      'Show information about site "mysite" (only works if the site only has a single branch) (sitegroup as stated by config)',
    )
    .example(
      `$0 ${name} mysite@prod`,
      'Show information about branch "prod" of site "mysite" (sitegroup as stated by config)',
    )
    .example(
      `$0 ${name} -s mysitegroup mysite`,
      'Show information about site "mysite" in sitegroup "somesitegroup"',
    ).argv;
}

type SiteGroupInfoArgs = {
  sitegroup: string,
  clog: typeof console.log,
  cerr: typeof console.error,
};

export async function sitegroupInfo({
  sitegroup,
  clog,
  cerr,
}: SiteGroupInfoArgs): Promise<number> {
  const query = gql`
    query querySites($sitegroup: String!) {
      siteGroupByName(name: $sitegroup) {
        gitUrl
        siteGroupName
        slack {
          webhook
          channel
          informStart
          informChannel
        }
        client {
          clientName
        }
        sites {
          siteName
          siteBranch
        }
      }
    }
  `;

  const result = await runGQLQuery({
    query,
    variables: { sitegroup },
  });

  const { errors } = result;
  if (errors != null) {
    return printGraphQLErrors(cerr, ...errors);
  }

  const sitegroupData = R.path(['data', 'siteGroupByName'])(result);

  if (sitegroupData == null) {
    return printErrors(clog, `No sitegroup '${sitegroup}' found`);
  }

  const sites = R.compose(
    R.map(({ siteName, siteBranch }) => `${siteName}:${siteBranch}`),
    R.pathOr([], ['data', 'siteGroupByName', 'sites']),
  )(result);

  const formatSlack = (slack) => {
    if (slack == null) {
      return '';
    }

    const webhook = R.prop('webhook', slack);
    const channel = R.prop('channel', slack);

    return `${channel} -> ${webhook}`;
  };

  const tableBody = [
    ['Sitegroup Name', R.prop('siteGroupName', sitegroupData)],
    ['Git Url', R.prop('gitUrl', sitegroupData)],
    ['Slack', formatSlack(R.prop('slack', sitegroupData))],
    ['Sites', R.join(', ', sites)],
  ];

  const tableData = R.filter(onlyValues)(tableBody);

  clog(`Details for '${sitegroup}':`);
  clog(table(tableData, tableConfig));

  return 0;
}

type SiteInfoArgs = {
  site: string,
  branch?: string,
  sitegroup: string,
  clog: typeof console.log,
  cerr: typeof console.error,
};

export async function siteInfo({
  sitegroup,
  site,
  branch,
  clog,
  cerr,
}: SiteInfoArgs): Promise<number> {
  // site[@branch]
  const siteBranchStr = `${site}${branch != null ? `@${branch}` : ''}`;

  const query = gql`
    query querySites($sitegroup: String!) {
      siteGroupByName(name: $sitegroup) {
        gitUrl
        sites {
          id
          siteHost
          siteName
          siteBranch
          siteEnvironment
          serverInfrastructure
          serverIdentifier
          serverNames
          webRoot
          drupalVersion
          SSLCertificateType
          FPMProfile
          domains
          redirectDomains
          redirects
          uid
          dbUser
          cron {
            type
            minute
          }
          customCron
          envVariables
          noPrefixenvVariables
          phpValues
          phpAdminFlags
          xdebug
          nginxSitespecific
          nginxSiteconfig
          solrEnabled
          redisEnabled
          sshKeys
          phpVersion
          redirectToHttps
          ensure
          upstreamURL
          apc
          basicAuth {
            username
          }
        }
      }
    }
  `;

  const result = await runGQLQuery({
    query,
    variables: { sitegroup },
  });

  const { errors } = result;
  if (errors != null) {
    return printGraphQLErrors(cerr, ...errors);
  }

  // There might be a case where I just have one site + branch,
  // so we will just filter for branch, if a branch was actually
  // given. If there are multiple matches, we will tell the user
  // anyways
  const matchesSiteAndBranch = ({ siteName, siteBranch }) => {
    if (branch != null && siteBranch !== branch) {
      return false;
    }
    return siteName === site;
  };

  const nodes = R.compose(
    R.filter(matchesSiteAndBranch),
    R.pathOr([], ['data', 'siteGroupByName', 'sites']),
  )(result);

  if (nodes.length === 0) {
    clog(red(`No site '${site}' found in sitegroup '${sitegroup}'`));
    return 0;
  }

  // For the case if there was no branch name given to begin with
  if (nodes.length > 1) {
    clog(
      'Multiple sites found with the same name but different branches. The branch can be specified with the "siteName@siteBranch" syntax. For example:',
    );
    R.forEach(({ siteName, siteBranch }) =>
      clog(`-> ${siteName}@${siteBranch}`),
    )(nodes);
    return 0;
  }

  clog(`Details for '${sitegroup} -> ${siteBranchStr}':`);

  // nodes only contains one element, extract it
  const [node] = nodes;

  const formatCron = (cron) => {
    if (cron == null) {
      return '';
    }

    return `Type: ${cron.type} - Minute: ${cron.minute}`;
  };

  const formatArray = (arr) => {
    if (arr == null) {
      return '';
    }

    return R.join(', ', arr);
  };

  // We want to list these fields from the node result
  const tableBody = [
    ['ID', R.prop('id', node)],
    ['Site Name', R.prop('siteName', node)],
    ['Site Branch', R.prop('siteBranch', node)],
    ['Site Env', R.prop('siteEnvironment', node)],
    ['User Id', R.prop('uid', node)],
    ['Server Names', R.prop('serverNames', node)],
    ['Webroot', R.prop('webRoot', node)],
    ['Domains', formatArray(R.prop('domains', node))],
    ['Redirect Domains', formatArray(R.prop('redirectDomains', node))],
    ['SSL Certificate Type', R.prop('SSLCertificateType', node)],
    ['Cron', formatCron(R.prop('cron', node))],
    ['Solr Enabled', R.prop('solrEnabled', node)],
  ];

  const tableData = R.filter(onlyValues)(tableBody);

  clog(table(tableData, tableConfig));

  return 0;
}

type Args = BaseArgs & {
  sitegroup: ?string,
};

export async function run(args: Args): Promise<number> {
  const { config, clog, cerr } = args;

  // FIXME: doesn't handle empty config file case correctly
  if (config == null) {
    return printNoConfigError(cerr);
  }

  const [siteAndBranch] = args._.slice(1);
  const sitegroup = args.sitegroup || config.sitegroup;

  if (siteAndBranch == null) {
    return sitegroupInfo({ sitegroup, clog, cerr });
  }

  const [site, branch] = siteAndBranch.split('@');

  return siteInfo({
    site,
    branch,
    sitegroup,
    clog,
    cerr,
  });
}

export default {
  setup,
  name,
  description,
  run,
};
<|endoftext|>


<commit_before>scrivi = {

    init: function() {
        this.richiesta_ricerca();
    },

    richiesta_ricerca: function() {
        $('#cerca_utente').on('click', function() {
            scrivi.cerca_utente();
        });
        $('#testo').on('keyup', function(e) {
            if (e.keyCode == 13) {
                scrivi.cerca_utente();
            }
        });
    },

    cerca_utente: function() {
        $('#messaggio').html('
');
        $('#testo').css('border-color', '#757575');
        var testo = $('#testo').val();
        if (testo.length > 0) {
            $('.caricamento').css('display', 'inline');
            var richiesta = {
                testo: testo
            };
            $.ajax({
                url: 'cerca_utente',
                method: 'POST',
                contentType: 'application/json',
                dataType: 'json',
                data: JSON.stringify(richiesta),
                success: function(risposta) {
                    $('.caricamento').css('display', 'none');
                    if (risposta.risultati) {
                        for (var i = 0; i < risposta.risultati.length; i++) {
                            var username = risposta.risultati[i][0];
                            var nome = risposta.risultati[i][1];
                            var cognome = risposta.risultati[i][2];
                            risposta.risultati[i] = {
                                username: username,
                                nome: nome,
                                cognome: cognome
                            };
                        }
                        $.get('/html/templates.html', function(contenuto) {
                            var template = $(contenuto).filter('#cerca_utente').html();
                            $('#risultati').html(Mustache.render(template, risposta));
                        });
                    } else {
                        $('#testo').val('');
                        $('#testo').css('border-color', 'red');
                        errore.messaggio('Nessuna corrispondenza trovata!');
                    }
                },
                error: function() {
                    $('.caricamento').css('display', 'none');
                    errore.messaggio('Errore del server!');
                }
            });
        } else {
            $('#testo').css('border-color', 'red');
            errore.messaggio('Inserisci uno criterio di ricerca!');
        }
    },

    nuova_conversazione: function(destinatario) {
        var mittente = utente.username;
        if (mittente != destinatario) {
            window.location.href = '/conversazione?con=' + destinatario;
        } else {
            errore.messaggio('Vuoi parlare da solo?');
        }
    }

};

$(document).ready(scrivi.init());
<commit_msg>tolto errore inutile<commit_after>scrivi = {

    init: function() {
        this.richiesta_ricerca();
    },

    richiesta_ricerca: function() {
        $('#cerca_utente').on('click', function() {
            scrivi.cerca_utente();
        });
        $('#testo').on('keyup', function(e) {
            if (e.keyCode == 13) {
                scrivi.cerca_utente();
            }
        });
    },

    cerca_utente: function() {
        $('#messaggio').html('
');
        $('#testo').css('border-color', '#757575');
        var testo = $('#testo').val();
        if (testo.length > 0) {
            $('.caricamento').css('display', 'inline');
            var richiesta = {
                testo: testo
            };
            $.ajax({
                url: 'cerca_utente',
                method: 'POST',
                contentType: 'application/json',
                dataType: 'json',
                data: JSON.stringify(richiesta),
                success: function(risposta) {
                    $('.caricamento').css('display', 'none');
                    if (risposta.risultati) {
                        for (var i = 0; i < risposta.risultati.length; i++) {
                            var username = risposta.risultati[i][0];
                            var nome = risposta.risultati[i][1];
                            var cognome = risposta.risultati[i][2];
                            risposta.risultati[i] = {
                                username: username,
                                nome: nome,
                                cognome: cognome
                            };
                        }
                        $.get('/html/templates.html', function(contenuto) {
                            var template = $(contenuto).filter('#cerca_utente').html();
                            $('#risultati').html(Mustache.render(template, risposta));
                        });
                    } else {
                        $('#testo').val('');
                        $('#testo').css('border-color', 'red');
                        errore.messaggio('Nessuna corrispondenza trovata!');
                    }
                },
                error: function() {
                    $('.caricamento').css('display', 'none');
                    errore.messaggio('Errore del server!');
                }
            });
        } else {
            $('#testo').css('border-color', 'red');
            errore.messaggio('Inserisci uno criterio di ricerca!');
        }
    },

    nuova_conversazione: function(destinatario) {
        var mittente = utente.username;
        if (mittente != destinatario) {
            window.location.href = '/conversazione?con=' + destinatario;
        }
    }

};

$(document).ready(scrivi.init());
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { BrowserRouter, Route, Link, Redirect, Switch } from 'react-router-dom'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import Leaderboard from './Leaderboard.container'
import Menubar from './Menubar.container'
import MyQuiz from './MyQuiz.container'
import Page404 from './404.container'
import * as QuizActions from '../actions'

function PrivateRoute ({component: Component, authed}) {
  return (
    <Route
      render={(props) => authed === true
        ? <Component {...props} />
        : <Redirect to={{pathname: '/', state: {from: props.location}}} />}
    />
  )
}

class App extends Component {
  constructor() {
    super();
    this.handleLogin = this.handleLogin.bind(this)
    this.handleLogout = this.handleLogout.bind(this)
  }

  componentDidMount() {
    this.props.actions.init()
  }

  handleLogin() {
    this.props.actions.login()
  }

  handleLogout() {
    this.props.actions.logout()
  }

  render() {
    return (
      <BrowserRouter>

          <Menubar onLoginClick={this.handleLogin} onLogoutClick={this.handleLogout} />
          <Switch>
            <Route exact path="/"
              component={(props) => <Leaderboard {...props} />} />
              <PrivateRoute path='/myQuiz' component={(props) => <MyQuiz {...props} />} />
            <Route render={() => <Page404 />} />
          </Switch>

      </BrowserRouter>
    );
  }
}
export default connect(
  null,
  (dispatch) => {
    return { actions: bindActionCreators(QuizActions, dispatch) }
  }
)(App)
<commit_msg>Get authed data<commit_after>import React, { Component, PropTypes } from 'react'
import { BrowserRouter, Route, Link, Redirect, Switch } from 'react-router-dom'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import Leaderboard from './Leaderboard.container'
import Menubar from './Menubar.container'
import MyQuiz from './MyQuiz.container'
import Page404 from './404.container'
import * as QuizActions from '../actions'

function PrivateRoute ({component: Component, authed}) {
  return (
    <Route
      render={(props) => authed === true
        ? <Component {...props} />
        : <Redirect to={{pathname: '/', state: {from: props.location}}} />}
    />
  )
}

class App extends Component {
  constructor() {
    super();
    this.handleLogin = this.handleLogin.bind(this)
    this.handleLogout = this.handleLogout.bind(this)
  }

  componentDidMount() {
    this.props.actions.init()
  }

  handleLogin() {
    this.props.actions.login()
  }

  handleLogout() {
    this.props.actions.logout()
  }

  render() {
    return (
      <BrowserRouter>

          <Menubar onLoginClick={this.handleLogin} onLogoutClick={this.handleLogout} />
          <Switch>
            <Route exact path="/"
              component={(props) => <Leaderboard {...props} />} />
              <PrivateRoute authed={this.props.authed} path='/myQuiz' component={(props) => <MyQuiz {...props} />} />
            <Route render={() => <Page404 />} />
          </Switch>

      </BrowserRouter>
    );
  }
}
export default connect(
  (state) => ({
    authed: state.app.authed,
  }),
  (dispatch) => {
    return { actions: bindActionCreators(QuizActions, dispatch) }
  }
)(App)
<|endoftext|>


<commit_before>scrollChatToBottom = function() {
    $("#roomContainer").scrollTop($("#roomContainer").height());
};
<commit_msg>Added a simple helper to scroll to a message<commit_after>scrollChatToBottom = function() {
    $("#roomContainer").scrollTop($("#roomContainer").height());
};

scrollToMessage = function(messageId){
    var message = $("#"+messageId);
    if(message){
        var container = $("#roomContainer");
        container.scrollTop(container.scrollTop() + message.offset().top);
    }
};
<|endoftext|>


<commit_before>if(typeof define!=='function'){var define=require('amdefine')(module);}

define([
  'onion/class_declarations',
  'onion/event_emitter',
  'onion/type',
  'jquery'
], function(classDeclarations, eventEmitter, Type, $){

  var isFunction = function (object) {
    return typeof object === 'function'
  }

  return Type.sub('View')

    .proto(eventEmitter)

    .use(classDeclarations, 'onDom')
    .use(classDeclarations, 'insertChildOfType')

    .proto({
      $: function () {
        return $(this.dom)
      },

      attachTo: function (dom) {
        this.dom = $(dom)[0]
        this.__setUpDomListeners__()
        return this
      },

      onDom: function (selector, event, newEvent, argumentsMapper) {
        var self = this
        this.$().on(event, selector, function (event) {
          event.stopPropagation()
          event.preventDefault()

          if(argumentsMapper) {
            self.emit(newEvent, argumentsMapper.call(self, this, event))
          } else {
            self.emit(newEvent)
          }
        })
        return this
      },

      __setUpDomListeners__: function () {
        this.__applyClassDeclarations__('onDom')
      },

      appendTo: function (element) {
        this.$().appendTo(element)
        return this
      },

      find: function (selector) {
        return this.$().find(selector)
      },

      findFromData: function (dataAttr, value) {
        return this.find('[data-' + dataAttr + ']').filter(function () {
          return $(this).data(dataAttr).match(new RegExp('\\b' + value + '\\b'))
        })
      },

      elemWithData: function (dataAttr, value) {
        var $elems = this.findFromData(dataAttr, value)
        return $elems.length ? $elems[0] : null
      },

      renderHTML: function (html) {
        html = html.trim()
        var newHtml = $(html)
        if ( !html.match(/^<.+>$/m) || newHtml.length != 1 ) {
          throw new Error("renderHTML only takes HTML wrapped in a single tag - you gave:\n" + html)
        }
        this.$().replaceWith(newHtml)
        this.attachTo(newHtml)
        return this
      },

      render: function () {},

      insertChild: function (childView, opts) {
        this.__applyInsertRules__(childView, opts) || this.__defaultInsertChild__(childView, opts)
      },

      // attachChild: function (type, mapping) {
      //   var rules = this.__insertRule__(type)
      //   rules.push(['attach', mapping])
      // },

      insertChildOfType: function (type, rule) {
        var rule = isFunction(rule) ? rule : this[rule]
        this.__insertRulesForType__(type).push(rule)
      },

      __applyInsertRules__: function (childView, opts) {
        var type = childView.constructor.name
        return this.__insertRulesForType__(type).some(function(rule){
          return rule.call(this, childView, opts)
        }, this)
      },

      __defaultInsertChild__: function (childView, opts) {
        var element,
            type = childView.constructor.name
        if(element = this.elemWithData('append', type)) {
          childView.appendTo(element)
        } else if(element = this.elemWithData('attach', type)) {
          childView.attachTo(element)
        } else {
          if(childView.appendTo) childView.appendTo(this.$())
        }
      },

      __insertRulesForType__: function (type) {
        return this.__insertRules__[type] = this.__insertRules__[type] || []
      },

      toHTML: function () {
        return this.dom.outerHTML
      },

      destroy: function () {
        this.$().remove()
      }
    })

    .after('init', function (options) {
      if(!options) options = {}

      if(options.attachTo) {
        this.attachTo(options.attachTo)
      } else {
        this.dom = $('<script>', {type: 'application/vnd.onionjs.placeholder', 'data-view-class': this.constructor.name})[0]
      }
      this.models = options.models || {}
      this.__insertRules__ = []
      this.__applyClassDeclarations__('insertChildOfType')
    })

})

<commit_msg>renamed View#elemWithData -> elementWithData<commit_after>if(typeof define!=='function'){var define=require('amdefine')(module);}

define([
  'onion/class_declarations',
  'onion/event_emitter',
  'onion/type',
  'jquery'
], function(classDeclarations, eventEmitter, Type, $){

  var isFunction = function (object) {
    return typeof object === 'function'
  }

  return Type.sub('View')

    .proto(eventEmitter)

    .use(classDeclarations, 'onDom')
    .use(classDeclarations, 'insertChildOfType')

    .proto({
      $: function () {
        return $(this.dom)
      },

      attachTo: function (dom) {
        this.dom = $(dom)[0]
        this.__setUpDomListeners__()
        return this
      },

      onDom: function (selector, event, newEvent, argumentsMapper) {
        var self = this
        this.$().on(event, selector, function (event) {
          event.stopPropagation()
          event.preventDefault()

          if(argumentsMapper) {
            self.emit(newEvent, argumentsMapper.call(self, this, event))
          } else {
            self.emit(newEvent)
          }
        })
        return this
      },

      __setUpDomListeners__: function () {
        this.__applyClassDeclarations__('onDom')
      },

      appendTo: function (element) {
        this.$().appendTo(element)
        return this
      },

      find: function (selector) {
        return this.$().find(selector)
      },

      findFromData: function (dataAttr, value) {
        return this.find('[data-' + dataAttr + ']').filter(function () {
          return $(this).data(dataAttr).match(new RegExp('\\b' + value + '\\b'))
        })
      },

      elementWithData: function (dataAttr, value) {
        var $elems = this.findFromData(dataAttr, value)
        return $elems.length ? $elems[0] : null
      },

      renderHTML: function (html) {
        html = html.trim()
        var newHtml = $(html)
        if ( !html.match(/^<.+>$/m) || newHtml.length != 1 ) {
          throw new Error("renderHTML only takes HTML wrapped in a single tag - you gave:\n" + html)
        }
        this.$().replaceWith(newHtml)
        this.attachTo(newHtml)
        return this
      },

      render: function () {},

      insertChild: function (childView, opts) {
        this.__applyInsertRules__(childView, opts) || this.__defaultInsertChild__(childView, opts)
      },

      // attachChild: function (type, mapping) {
      //   var rules = this.__insertRule__(type)
      //   rules.push(['attach', mapping])
      // },

      insertChildOfType: function (type, rule) {
        var rule = isFunction(rule) ? rule : this[rule]
        this.__insertRulesForType__(type).push(rule)
      },

      __applyInsertRules__: function (childView, opts) {
        var type = childView.constructor.name
        return this.__insertRulesForType__(type).some(function(rule){
          return rule.call(this, childView, opts)
        }, this)
      },

      __defaultInsertChild__: function (childView, opts) {
        var element,
            type = childView.constructor.name
        if(element = this.elementWithData('append', type)) {
          childView.appendTo(element)
        } else if(element = this.elementWithData('attach', type)) {
          childView.attachTo(element)
        } else {
          if(childView.appendTo) childView.appendTo(this.$())
        }
      },

      __insertRulesForType__: function (type) {
        return this.__insertRules__[type] = this.__insertRules__[type] || []
      },

      toHTML: function () {
        return this.dom.outerHTML
      },

      destroy: function () {
        this.$().remove()
      }
    })

    .after('init', function (options) {
      if(!options) options = {}

      if(options.attachTo) {
        this.attachTo(options.attachTo)
      } else {
        this.dom = $('<script>', {type: 'application/vnd.onionjs.placeholder', 'data-view-class': this.constructor.name})[0]
      }
      this.models = options.models || {}
      this.__insertRules__ = []
      this.__applyClassDeclarations__('insertChildOfType')
    })

})

<|endoftext|>


<commit_before>(function(){

    var routineAppControllers = angular.module('routineApp.controllers', ['routineApp.filters',
                'ngCookies',
                ]);

    routineAppControllers.controller('RoutineListController', ['RoutineService', function(RoutineService) {

        var vm = this;
        console.log('HIT');
        vm.getRoutines = function() {
            RoutineService.getRoutines()
            .success(function(response){
                vm.routines = response;
            })
            .error(function() {
                // Need error handling
            });
        }

        vm.getRoutines();

    }]);

    routineAppControllers.controller('RoutineAddRoutineController', ['RoutineService', '$cookies', '$state', function(RoutineService, $cookies, $state) {

        var vm = this;

        vm.createRoutine = function(routineJson){
            RoutineService.createRoutine(routineJson)
            .success(function(response){

            })
            .error(function(){
                // Need error handling
            });
        };

        vm.submit = function() {

            var routineObj = {};

            routineObj.name = vm.routine.name;

            $cookies.put('routine', routineObj.name);

            var routineJson = angular.toJson(routineObj);

            vm.createRoutine(routineJson);

            $state.go('add_exercises');

        };

    }]);

    routineAppControllers.controller('RoutineAddExercisesController', ['$cookies', 'RoutineService', '$state', function($cookies, RoutineService, $state) {

        var ctrl = this;

        ctrl.exercises = [{position: '1'}, {position: '2'}, {position: '3'}, {position: '4'}, {position: '5'}];

        ctrl.addNewExercise = function() {
            var newExercisePosition = ctrl.exercises.length + 1;
            ctrl.exercises.push({'position': newExercisePosition});
        };

        ctrl.removeExercise = function() {
            var lastExercise = ctrl.exercises.length - 1;
            ctrl.exercises.splice(lastExercise);
        };

        ctrl.postExercises = function(exercisesJson){
            RoutineService.postExercises(exercisesJson).then(function(response){
                console.log(response);
            });
        };

        ctrl.submit = function($event) {

            $event.preventDefault();

            var exerciseObjs = [];

            angular.forEach(ctrl.exercises, function(exercise, index){
                if (exercise.name) {

                    var exerciseObj = {};
                    exerciseObj.position = exercise.position;
                    exerciseObj.name = exercise.name;
                    exerciseObj.completion_time = "00:" + exercise.minutes + ":" + exercise.seconds;
                    exerciseObj.routine = $cookies.get('routine');

                    exerciseObjs.push(exerciseObj);

                }
            });

            var exercisesJson = angular.toJson(exerciseObjs);
            ctrl.postExercises(exercisesJson);

            $state.go('list');
        };

    }]);

    routineAppControllers.controller('RoutineEditController', ['RoutineService', '$location', function(RoutineService, $location) {

        var ctrl = this;

        var routineName = $location.url().split('/')[2];

        ctrl.routine = {name:routineName, total_time: '00:00:00'};

        ctrl.routine.old_name = ctrl.routine.name;

        ctrl.exercises = [];

        ctrl.getRoutine = function(routineName) {
            RoutineService.getRoutine(routineName).then(function(response){
                ctrl.exercises = response.data;
                ctrl.formatCompletionTimes(ctrl.exercises);
            });
        };

        ctrl.getRoutine(ctrl.routine.name)

        // Format the completion time to fit in the form
        ctrl.formatCompletionTimes = function(exercises){
            angular.forEach(exercises, function(exercise, index){

                var completion_time = exercise.completion_time.split(":");

                exercise.minutes = completion_time[1];
                exercise.seconds = completion_time[2];

            });
        }

        ctrl.addNewExercise = function() {
            var newExercisePosition = ctrl.exercises.length + 1;
            ctrl.exercises.push({'position': newExercisePosition});
        };

        ctrl.removeExercise = function() {
            var lastExercise = ctrl.exercises.length - 1;

            // delete exercise from the database
            var exerciseToDelete = ctrl.exercises[lastExercise];
            exerciseToDelete.routine = ctrl.routine.name;
            ctrl.postExerciseDelete(exerciseToDelete);

            ctrl.exercises.splice(lastExercise);
        };

        ctrl.postExercise = function(exercise) {
            RoutineService.postExercise(exercise).then(function(response){
                console.log(response);
            });
        };

        ctrl.postRoutine = function(routine) {
            RoutineService.postRoutine(routine).then(function(response){
                console.log(response);
            });
        };

        ctrl.postRoutineDelete = function(routine) {
            RoutineService.postRoutineDelete(routine).then(function(response){
                console.log(response);
            });
        };

        ctrl.postExerciseDelete = function(exercise) {
            RoutineService.postExerciseDelete(exercise).then(function(response){
                console.log(response);
            });
        };

        ctrl.submit = function($event) {

            $event.preventDefault();

            // Prepare and post the routine
            var routineJson = angular.toJson(ctrl.routine);
            ctrl.postRoutine(routineJson);

            var exerciseObjs = [];

            angular.forEach(ctrl.exercises, function(exercise, index){

                var exerciseObj = {};

                exerciseObj.position = exercise.position;
                exerciseObj.name = exercise.name;
                exerciseObj.completion_time = "00:" + exercise.minutes + ":" + exercise.seconds;
                exerciseObj.routine = ctrl.routine.name;

                var exerciseJson = angular.toJson(exerciseObj);

                ctrl.postExercise(exerciseJson);

            });

            // If the routine name has changed then delete the old routine
            if (ctrl.routine.name != ctrl.routine.old_name) {
                ctrl.postRoutineDelete(ctrl.routine);
            }

        };

    }]);

    routineAppControllers.controller('RoutineUseController', ['$interval', 'RoutineService', '$location', function($interval, RoutineService, $location){

        var ctrl = this;

        var routineName = $location.url().split('/')[2];

        ctrl.routine = {name:routineName, total_time: '00:00:00'};

        console.log(ctrl.routine);

        ctrl.getRoutine = function() {
            RoutineService.getRoutine(ctrl.routine.name).then(function(response){

                ctrl.routine = response.data[0].routine;
                ctrl.exercises = response.data;
                ctrl.total_exercises = ctrl.exercises.length;
                ctrl.current_exercise = setCurrentExercise(ctrl.exercises, ctrl.current_position);

            })
        }

        ctrl.getRoutine();

        ctrl.current_position = 1;

        var count_down;
        var reset = false;
        ctrl.timerStartStop = function(){

            // If the timer has run through completely, reset it
            if (reset) {
                exerciseReset();
            }

            // If already running, stop the timer
            if (angular.isDefined(count_down)) {
                $interval.cancel(count_down);
                count_down = undefined;
            } else {

                count_down = $interval(function(){

                    if (exerciseHasEnded(ctrl.current_exercise.count_down_time)) {

                        ctrl.current_position == ctrl.current_position++;

                        // If the last exercise has finished, stop the timer
                        // Otherwise move on to the next exercise
                        if (ctrl.current_position > ctrl.total_exercises) {
                            $interval.cancel(count_down);
                            count_down = undefined;
                            ctrl.current_position == ctrl.current_position--;
                            reset = true;
                        } else {
                            ctrl.current_exercise = setCurrentExercise(ctrl.exercises, ctrl.current_position);
                        }

                    } else {
                        countDown(ctrl.current_exercise.count_down_time);
                    }

                }, 1000);
            }

        };

        ctrl.timerReset = function(){

            if (angular.isDefined(count_down)) {
                $interval.cancel(count_down);
                count_down = undefined;
            }

            exerciseReset();

        };

        function exerciseReset(){

            ctrl.current_position = 1;
            ctrl.current_exercise = setCurrentExercise(ctrl.exercises, ctrl.current_position);

            reset = false;

        }

        function exerciseHasEnded(time){
            return time.seconds == 0 && time.minutes == 00;
        }

        function setCurrentExercise(exercises, position){

            var indexPosition = position - 1;
            var current_exercise = exercises[indexPosition];

            current_exercise.count_down_time = completionTimeToObj(current_exercise.completion_time);

            return current_exercise;
        }

        function countDown(time){

            if (time.seconds > 0 && time.minutes > -1){
                time.seconds == time.seconds--;
                // return time;
            } else if (time.minutes > 0) {
                time.minutes == time.minutes--;
                time.seconds = 60;
                // return time;
            }

        };

        function completionTimeToObj(completion_time){

            // Not starting at index of 0 because django stores times as 00:00:00
            var completionSplit = completion_time.split(":");
            var minutes = parseInt(completionSplit[1]);
            var seconds = parseInt(completionSplit[2]);

            return {
                minutes: minutes,
                seconds: seconds
            }

        };

    }]);

})();
<commit_msg>Client - Fixed create routine functionality<commit_after>(function(){

    var routineAppControllers = angular.module('routineApp.controllers', ['routineApp.filters',
                'ngCookies',
                ]);

    routineAppControllers.controller('RoutineListController', ['RoutineService', function(RoutineService) {

        var vm = this;
        console.log('HIT');
        vm.getRoutines = function() {
            RoutineService.getRoutines()
            .success(function(response){
                vm.routines = response;
            })
            .error(function() {
                // Need error handling
            });
        }

        vm.getRoutines();

    }]);

    routineAppControllers.controller('RoutineAddRoutineController', ['RoutineService', '$cookies', '$state', function(RoutineService, $cookies, $state) {

        var vm = this;

        vm.createRoutine = function(routineJson){
            RoutineService.createRoutine(routineJson)
            .success(function(response){

            })
            .error(function(){
                // Need error handling
            });
        };

        vm.submit = function() {

            var routineObj = {};

            routineObj.name = vm.routine.name;

            $cookies.put('routine', routineObj.name);

            var routineJson = angular.toJson(routineObj);

            vm.createRoutine(routineJson);

            $state.go('create_exercises');

        };

    }]);

    routineAppControllers.controller('RoutineAddExercisesController', ['$cookies', 'RoutineService', '$state', function($cookies, RoutineService, $state) {

        var ctrl = this;

        ctrl.exercises = [{position: '1'}, {position: '2'}, {position: '3'}, {position: '4'}, {position: '5'}];

        ctrl.addNewExercise = function() {
            var newExercisePosition = ctrl.exercises.length + 1;
            ctrl.exercises.push({'position': newExercisePosition});
        };

        ctrl.removeExercise = function() {
            var lastExercise = ctrl.exercises.length - 1;
            ctrl.exercises.splice(lastExercise);
        };

        ctrl.postExercises = function(exercisesJson){
            RoutineService.postExercises(exercisesJson).then(function(response){
                console.log(response);
            });
        };

        ctrl.submit = function($event) {

            $event.preventDefault();

            var exerciseObjs = [];

            angular.forEach(ctrl.exercises, function(exercise, index){
                if (exercise.name) {

                    var exerciseObj = {};
                    exerciseObj.position = exercise.position;
                    exerciseObj.name = exercise.name;
                    exerciseObj.completion_time = "00:" + exercise.minutes + ":" + exercise.seconds;
                    exerciseObj.routine = $cookies.get('routine');

                    exerciseObjs.push(exerciseObj);

                }
            });

            var exercisesJson = angular.toJson(exerciseObjs);
            ctrl.postExercises(exercisesJson);

            $state.go('list');
        };

    }]);

    routineAppControllers.controller('RoutineEditController', ['RoutineService', '$location', function(RoutineService, $location) {

        var ctrl = this;

        var routineName = $location.url().split('/')[2];

        ctrl.routine = {name:routineName, total_time: '00:00:00'};

        ctrl.routine.old_name = ctrl.routine.name;

        ctrl.exercises = [];

        ctrl.getRoutine = function(routineName) {
            RoutineService.getRoutine(routineName).then(function(response){
                ctrl.exercises = response.data;
                ctrl.formatCompletionTimes(ctrl.exercises);
            });
        };

        ctrl.getRoutine(ctrl.routine.name)

        // Format the completion time to fit in the form
        ctrl.formatCompletionTimes = function(exercises){
            angular.forEach(exercises, function(exercise, index){

                var completion_time = exercise.completion_time.split(":");

                exercise.minutes = completion_time[1];
                exercise.seconds = completion_time[2];

            });
        }

        ctrl.addNewExercise = function() {
            var newExercisePosition = ctrl.exercises.length + 1;
            ctrl.exercises.push({'position': newExercisePosition});
        };

        ctrl.removeExercise = function() {
            var lastExercise = ctrl.exercises.length - 1;

            // delete exercise from the database
            var exerciseToDelete = ctrl.exercises[lastExercise];
            exerciseToDelete.routine = ctrl.routine.name;
            ctrl.postExerciseDelete(exerciseToDelete);

            ctrl.exercises.splice(lastExercise);
        };

        ctrl.postExercise = function(exercise) {
            RoutineService.postExercise(exercise).then(function(response){
                console.log(response);
            });
        };

        ctrl.postRoutine = function(routine) {
            RoutineService.postRoutine(routine).then(function(response){
                console.log(response);
            });
        };

        ctrl.postRoutineDelete = function(routine) {
            RoutineService.postRoutineDelete(routine).then(function(response){
                console.log(response);
            });
        };

        ctrl.postExerciseDelete = function(exercise) {
            RoutineService.postExerciseDelete(exercise).then(function(response){
                console.log(response);
            });
        };

        ctrl.submit = function($event) {

            $event.preventDefault();

            // Prepare and post the routine
            var routineJson = angular.toJson(ctrl.routine);
            ctrl.postRoutine(routineJson);

            var exerciseObjs = [];

            angular.forEach(ctrl.exercises, function(exercise, index){

                var exerciseObj = {};

                exerciseObj.position = exercise.position;
                exerciseObj.name = exercise.name;
                exerciseObj.completion_time = "00:" + exercise.minutes + ":" + exercise.seconds;
                exerciseObj.routine = ctrl.routine.name;

                var exerciseJson = angular.toJson(exerciseObj);

                ctrl.postExercise(exerciseJson);

            });

            // If the routine name has changed then delete the old routine
            if (ctrl.routine.name != ctrl.routine.old_name) {
                ctrl.postRoutineDelete(ctrl.routine);
            }

        };

    }]);

    routineAppControllers.controller('RoutineUseController', ['$interval', 'RoutineService', '$location', function($interval, RoutineService, $location){

        var ctrl = this;

        var routineName = $location.url().split('/')[2];

        ctrl.routine = {name:routineName, total_time: '00:00:00'};

        console.log(ctrl.routine);

        ctrl.getRoutine = function() {
            RoutineService.getRoutine(ctrl.routine.name).then(function(response){

                ctrl.routine = response.data[0].routine;
                ctrl.exercises = response.data;
                ctrl.total_exercises = ctrl.exercises.length;
                ctrl.current_exercise = setCurrentExercise(ctrl.exercises, ctrl.current_position);

            })
        }

        ctrl.getRoutine();

        ctrl.current_position = 1;

        var count_down;
        var reset = false;
        ctrl.timerStartStop = function(){

            // If the timer has run through completely, reset it
            if (reset) {
                exerciseReset();
            }

            // If already running, stop the timer
            if (angular.isDefined(count_down)) {
                $interval.cancel(count_down);
                count_down = undefined;
            } else {

                count_down = $interval(function(){

                    if (exerciseHasEnded(ctrl.current_exercise.count_down_time)) {

                        ctrl.current_position == ctrl.current_position++;

                        // If the last exercise has finished, stop the timer
                        // Otherwise move on to the next exercise
                        if (ctrl.current_position > ctrl.total_exercises) {
                            $interval.cancel(count_down);
                            count_down = undefined;
                            ctrl.current_position == ctrl.current_position--;
                            reset = true;
                        } else {
                            ctrl.current_exercise = setCurrentExercise(ctrl.exercises, ctrl.current_position);
                        }

                    } else {
                        countDown(ctrl.current_exercise.count_down_time);
                    }

                }, 1000);
            }

        };

        ctrl.timerReset = function(){

            if (angular.isDefined(count_down)) {
                $interval.cancel(count_down);
                count_down = undefined;
            }

            exerciseReset();

        };

        function exerciseReset(){

            ctrl.current_position = 1;
            ctrl.current_exercise = setCurrentExercise(ctrl.exercises, ctrl.current_position);

            reset = false;

        }

        function exerciseHasEnded(time){
            return time.seconds == 0 && time.minutes == 00;
        }

        function setCurrentExercise(exercises, position){

            var indexPosition = position - 1;
            var current_exercise = exercises[indexPosition];

            current_exercise.count_down_time = completionTimeToObj(current_exercise.completion_time);

            return current_exercise;
        }

        function countDown(time){

            if (time.seconds > 0 && time.minutes > -1){
                time.seconds == time.seconds--;
                // return time;
            } else if (time.minutes > 0) {
                time.minutes == time.minutes--;
                time.seconds = 60;
                // return time;
            }

        };

        function completionTimeToObj(completion_time){

            // Not starting at index of 0 because django stores times as 00:00:00
            var completionSplit = completion_time.split(":");
            var minutes = parseInt(completionSplit[1]);
            var seconds = parseInt(completionSplit[2]);

            return {
                minutes: minutes,
                seconds: seconds
            }

        };

    }]);

})();
<|endoftext|>


<commit_before>// Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours
function adjustedTimeFormat() {
  return moment
    .localeData()
    .longDateFormat('LT')
    .replace(/HH/i, 'H');
}

export class DatePicker extends BlazeComponent {
  template() {
    return 'datepicker';
  }

  onCreated(defaultTime = '1970-01-01 08:00:00') {
    this.error = new ReactiveVar('');
    this.card = this.data();
    this.date = new ReactiveVar(moment.invalid());
    this.defaultTime = defaultTime;
  }

  startDayOfWeek() {
    const currentUser = Meteor.user();
    if (currentUser) {
      return currentUser.getStartDayOfWeek();
    } else {
      return 1;
    }
  }

  onRendered() {
    const $picker = this.$('.js-datepicker')
      .datepicker({
        todayHighlight: true,
        todayBtn: 'linked',
        language: TAPi18n.getLanguage(),
        weekStart: this.startDayOfWeek(),
        calendarWeeks: true,
      })
      .on(
        'changeDate',
        function(evt) {
          this.find('#date').value = moment(evt.date).format('L');
          this.error.set('');
          const timeInput = this.find('#time');
          timeInput.focus();
          if (!timeInput.value && this.defaultTime) {
            const currentHour = evt.date.getHours();
            const defaultMoment = moment(
              currentHour > 0 ? evt.date : this.defaultTime,
            ); // default to 8:00 am local time
            timeInput.value = defaultMoment.format('LT');
          }
        }.bind(this),
      );

    if (this.date.get().isValid()) {
      $picker.datepicker('update', this.date.get().toDate());
    }
  }

  showDate() {
    if (this.date.get().isValid()) return this.date.get().format('L');
    return '';
  }
  showTime() {
    if (this.date.get().isValid()) return this.date.get().format('LT');
    return '';
  }
  dateFormat() {
    return moment.localeData().longDateFormat('L');
  }
  timeFormat() {
    return moment.localeData().longDateFormat('LT');
  }

  events() {
    return [
      {
        'keyup .js-date-field'() {
          // parse for localized date format in strict mode
          const dateMoment = moment(this.find('#date').value, 'L', true);
          if (dateMoment.isValid()) {
            this.error.set('');
            this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
          }
        },
        'keyup .js-time-field'() {
          // parse for localized time format in strict mode
          const dateMoment = moment(
            this.find('#time').value,
            adjustedTimeFormat(),
            true,
          );
          if (dateMoment.isValid()) {
            this.error.set('');
          }
        },
        'submit .edit-date'(evt) {
          evt.preventDefault();

          // if no time was given, init with 12:00
          const time =
            evt.target.time.value ||
            moment(new Date().setHours(12, 0, 0)).format('LT');
          const newTime = moment(time, adjustedTimeFormat(), true);
          const newDate = moment(evt.target.date.value, 'L', true);
          const dateString = `${evt.target.date.value} ${time}`;
          const newCompleteDate = moment(
            dateString,
            `L ${adjustedTimeFormat()}`,
            true,
          );
          if (!newTime.isValid()) {
            this.error.set('invalid-time');
            evt.target.time.focus();
          }
          if (!newDate.isValid()) {
            this.error.set('invalid-date');
            evt.target.date.focus();
          }
          if (newCompleteDate.isValid()) {
            this._storeDate(newCompleteDate.toDate());
            Popup.close();
          } else if (!this.error) {
            this.error.set('invalid');
          }
        },
        'click .js-delete-date'(evt) {
          evt.preventDefault();
          this._deleteDate();
          Popup.close();
        },
      },
    ];
  }
}
<commit_msg>Card Details Popup, edit dates now closes not the Popup<commit_after>// Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours
function adjustedTimeFormat() {
  return moment
    .localeData()
    .longDateFormat('LT')
    .replace(/HH/i, 'H');
}

export class DatePicker extends BlazeComponent {
  template() {
    return 'datepicker';
  }

  onCreated(defaultTime = '1970-01-01 08:00:00') {
    this.error = new ReactiveVar('');
    this.card = this.data();
    this.date = new ReactiveVar(moment.invalid());
    this.defaultTime = defaultTime;
  }

  startDayOfWeek() {
    const currentUser = Meteor.user();
    if (currentUser) {
      return currentUser.getStartDayOfWeek();
    } else {
      return 1;
    }
  }

  onRendered() {
    const $picker = this.$('.js-datepicker')
      .datepicker({
        todayHighlight: true,
        todayBtn: 'linked',
        language: TAPi18n.getLanguage(),
        weekStart: this.startDayOfWeek(),
        calendarWeeks: true,
      })
      .on(
        'changeDate',
        function(evt) {
          this.find('#date').value = moment(evt.date).format('L');
          this.error.set('');
          const timeInput = this.find('#time');
          timeInput.focus();
          if (!timeInput.value && this.defaultTime) {
            const currentHour = evt.date.getHours();
            const defaultMoment = moment(
              currentHour > 0 ? evt.date : this.defaultTime,
            ); // default to 8:00 am local time
            timeInput.value = defaultMoment.format('LT');
          }
        }.bind(this),
      );

    if (this.date.get().isValid()) {
      $picker.datepicker('update', this.date.get().toDate());
    }
  }

  showDate() {
    if (this.date.get().isValid()) return this.date.get().format('L');
    return '';
  }
  showTime() {
    if (this.date.get().isValid()) return this.date.get().format('LT');
    return '';
  }
  dateFormat() {
    return moment.localeData().longDateFormat('L');
  }
  timeFormat() {
    return moment.localeData().longDateFormat('LT');
  }

  events() {
    return [
      {
        'keyup .js-date-field'() {
          // parse for localized date format in strict mode
          const dateMoment = moment(this.find('#date').value, 'L', true);
          if (dateMoment.isValid()) {
            this.error.set('');
            this.$('.js-datepicker').datepicker('update', dateMoment.toDate());
          }
        },
        'keyup .js-time-field'() {
          // parse for localized time format in strict mode
          const dateMoment = moment(
            this.find('#time').value,
            adjustedTimeFormat(),
            true,
          );
          if (dateMoment.isValid()) {
            this.error.set('');
          }
        },
        'submit .edit-date'(evt) {
          evt.preventDefault();

          // if no time was given, init with 12:00
          const time =
            evt.target.time.value ||
            moment(new Date().setHours(12, 0, 0)).format('LT');
          const newTime = moment(time, adjustedTimeFormat(), true);
          const newDate = moment(evt.target.date.value, 'L', true);
          const dateString = `${evt.target.date.value} ${time}`;
          const newCompleteDate = moment(
            dateString,
            `L ${adjustedTimeFormat()}`,
            true,
          );
          if (!newTime.isValid()) {
            this.error.set('invalid-time');
            evt.target.time.focus();
          }
          if (!newDate.isValid()) {
            this.error.set('invalid-date');
            evt.target.date.focus();
          }
          if (newCompleteDate.isValid()) {
            this._storeDate(newCompleteDate.toDate());
            Popup.back();
          } else if (!this.error) {
            this.error.set('invalid');
          }
        },
        'click .js-delete-date'(evt) {
          evt.preventDefault();
          this._deleteDate();
          Popup.back();
        },
      },
    ];
  }
}
<|endoftext|>


<commit_before>"use strict"

const RSH = require('./request-signature-helper').RequestSignatureHelper
const Throttler = require('./throttler')
const locale = require('./locale')

const http = require('http')
const xml2js = require('xml2js')

const defaultXml2JsOptions = {
    explicitArray: false
}

class OperationHelper {
    constructor(params) {
        params = params || {}

        // check requried params
        if (typeof(params.awsId) === 'undefined') {
            throw new Error('Missing AWS Id param')
        }
        if (typeof(params.awsSecret) === 'undefined') {
            throw new Error('Missing AWS Secret param')
        }
        if (typeof(params.assocId) === 'undefined') {
            throw new Error('Missing Associate Id param')
        }

        // set instance variables from params
        this.awsId = params.awsId
        this.awsSecret = params.awsSecret
        this.assocId = params.assocId
        this.endPoint = params.endPoint || locale.getEndpointForLocale(params.locale)
        this.scheme = params.scheme
        this.baseUri = params.baseUri || OperationHelper.defaultBaseUri
        this.xml2jsOptions = Object.assign({}, defaultXml2JsOptions, params.xml2jsOptions)
        this.throttler = new Throttler(params.maxRequestsPerSecond)

        // set version
        if (typeof(params.version) === 'string') OperationHelper.version = params.version
    }

    getSignatureHelper() {
        if (typeof(this.signatureHelper) === 'undefined') {
            var params = {}
            params[RSH.kAWSAccessKeyId] = this.awsId
            params[RSH.kAWSSecretKey] = this.awsSecret
            params[RSH.kEndPoint] = this.endPoint
            this.signatureHelper = new RSH(params)
        }
        return this.signatureHelper
    }

    generateParams(operation, params) {
        params.Service = OperationHelper.service
        params.Version = OperationHelper.version
        params.Operation = operation
        params.AWSAccessKeyId = this.awsId
        params.AssociateTag = this.assocId
        return params
    }

    generateUri(operation, params) {
        params = this.generateParams(operation, params)
        var helper = this.getSignatureHelper()
        params = helper.sign(params)
        var queryString = helper.canonicalize(params)
        return this.baseUri + '?' + queryString
    }

    execute(operation, params, callback) {
        const throttledAction = () => this._execute(operation, params, callback)
        return this.throttler.execute(throttledAction)
    }

    _execute(operation, params, callback) {
        if (typeof(operation) === 'undefined') {
            throw new Error('Missing operation parameter')
        }
        if (typeof(params) === 'undefined') {
            params = {}
        }

        var uri = this.generateUri(operation, params)
        var host = this.endPoint
        var scheme = this.scheme
        var xml2jsOptions = this.xml2jsOptions

        var options = {
            hostname: host,
            path: uri,
            method: 'GET'
        }

        if (scheme) options['scheme'] = scheme;

        var responseBody = ''

        return new Promise((resolve, reject) => {
            var request = http.request(options, function (response) {
                response.setEncoding('utf8')

                response.on('data', function (chunk) {
                    responseBody += chunk
                })

                response.on('end', function () {
                    xml2js.parseString(responseBody, xml2jsOptions, function (err, result) {
                        if (callback) callback(err, result, responseBody)
                        if (err) reject(err)
                        else resolve({
                            result,
                            responseBody
                        })
                    })
                })

            })

            request.on('error', function (err) {
                if (callback) callback(err)
                reject(err)
            })

            request.end()
        })
    }
}

OperationHelper.version = '2013-08-01'
OperationHelper.service = 'AWSECommerceService'
OperationHelper.defaultBaseUri = '/onca/xml'

exports.OperationHelper = OperationHelper
<commit_msg>avoid unhandled rejection warnings when using callback interface<commit_after>"use strict"

const RSH = require('./request-signature-helper').RequestSignatureHelper
const Throttler = require('./throttler')
const locale = require('./locale')

const http = require('http')
const xml2js = require('xml2js')

const defaultXml2JsOptions = {
    explicitArray: false
}

class OperationHelper {
    constructor(params) {
        params = params || {}

        // check requried params
        if (typeof(params.awsId) === 'undefined') {
            throw new Error('Missing AWS Id param')
        }
        if (typeof(params.awsSecret) === 'undefined') {
            throw new Error('Missing AWS Secret param')
        }
        if (typeof(params.assocId) === 'undefined') {
            throw new Error('Missing Associate Id param')
        }

        // set instance variables from params
        this.awsId = params.awsId
        this.awsSecret = params.awsSecret
        this.assocId = params.assocId
        this.endPoint = params.endPoint || locale.getEndpointForLocale(params.locale)
        this.scheme = params.scheme
        this.baseUri = params.baseUri || OperationHelper.defaultBaseUri
        this.xml2jsOptions = Object.assign({}, defaultXml2JsOptions, params.xml2jsOptions)
        this.throttler = new Throttler(params.maxRequestsPerSecond)

        // set version
        if (typeof(params.version) === 'string') OperationHelper.version = params.version
    }

    getSignatureHelper() {
        if (typeof(this.signatureHelper) === 'undefined') {
            var params = {}
            params[RSH.kAWSAccessKeyId] = this.awsId
            params[RSH.kAWSSecretKey] = this.awsSecret
            params[RSH.kEndPoint] = this.endPoint
            this.signatureHelper = new RSH(params)
        }
        return this.signatureHelper
    }

    generateParams(operation, params) {
        params.Service = OperationHelper.service
        params.Version = OperationHelper.version
        params.Operation = operation
        params.AWSAccessKeyId = this.awsId
        params.AssociateTag = this.assocId
        return params
    }

    generateUri(operation, params) {
        params = this.generateParams(operation, params)
        var helper = this.getSignatureHelper()
        params = helper.sign(params)
        var queryString = helper.canonicalize(params)
        return this.baseUri + '?' + queryString
    }

    execute(operation, params, callback) {
        const throttledAction = () => this._execute(operation, params, callback)
        return this.throttler.execute(throttledAction)
    }

    _execute(operation, params, callback) {
        if (typeof(operation) === 'undefined') {
            throw new Error('Missing operation parameter')
        }
        if (typeof(params) === 'undefined') {
            params = {}
        }

        var uri = this.generateUri(operation, params)
        var host = this.endPoint
        var scheme = this.scheme
        var xml2jsOptions = this.xml2jsOptions

        var options = {
            hostname: host,
            path: uri,
            method: 'GET'
        }

        if (scheme) options['scheme'] = scheme;

        var responseBody = ''

        const promise = new Promise((resolve, reject) => {
            var request = http.request(options, function (response) {
                response.setEncoding('utf8')

                response.on('data', function (chunk) {
                    responseBody += chunk
                })

                response.on('end', function () {
                    xml2js.parseString(responseBody, xml2jsOptions, function (err, result) {
                        if (callback) callback(err, result, responseBody)
                        else if (err) reject(err)
                        else resolve({
                            result,
                            responseBody
                        })
                    })
                })

            })

            request.on('error', function (err) {
                if (callback) callback(err)
                else reject(err)
            })

            request.end()
        })

        if (!callback) return promise
    }
}

OperationHelper.version = '2013-08-01'
OperationHelper.service = 'AWSECommerceService'
OperationHelper.defaultBaseUri = '/onca/xml'

exports.OperationHelper = OperationHelper
<|endoftext|>


<commit_before>/* eslint-disable no-unused-vars, no-undefined, no-unused-expressions */
var mocha = require('mocha');
var chai = require('chai');
var expect = chai.expect;
var should = chai.should();
var Action = require('./../lib/action');
var Store = require('../lib/store');
var Mixin = require('../lib/ObservableStateMixin');
var Rx = require('rx');
var sinon = require('sinon');
var Q = require('q');
var chaiAsPromised = require('chai-as-promised');
var sinonChai = require('sinon-chai');

chai.use(chaiAsPromised);
chai.use(sinonChai);

describe(' Store', function() {

  describe(' Errors:', function() {

    it('should throw an error if argument passed is not an object', function() {
      Store.create.bind(this, 5).should.
        throw('expects an object as argument, given : 5');
    });

    it('should throw an error if getInitialValue is not defined', function() {
      Store.create.bind(this, {}).should.
        throw('getInitialValue should be a function given : undefined');
    });
  });

  describe(' Get Initial Value:', function() {

    describe(' Normal function', function() {
      it(
        'should publish null if getInitialValue returns neither null',
        function() {

          var store = Store.create({
            getInitialValue: function () {}
          });

          store.subscribe(function(val) {
            expect(val).to.be.undefined;
          });
        }
      );

      it(
        'should return a value on initial subscription if `getInitialValue` ' +
          'returns a value',
        function() {
          var store = Store.create({
            getInitialValue: function () {
              return 5;
            }
          });

          store.subscribe(function(val) {
            expect(val).to.be.a('number');
          });
        }
      );
    });

    describe(' Promises and Observables', function() {
      var store, value;

      it('should produce a new Rx.Observable', function() {
        store = Store.create({
          getInitialValue: function() { }
        });
        store.should.be.an.instanceOf(Rx.Observable);
      });

      it(
        'should resolve and publish a value if getInitialValue returns ' +
         ' a Promise',
        function () {
          value = 1;
          store = Store.create({
            getInitialValue: function () {
              return Q.resolve(value);
            }
          });

          store.subscribe(function(val) {
            val.should.equal(value);
          });
        }
      );

      it(
        'should publish the observable\'s resolve value if getInitialValue ' +
          'returns an observable',
        function() {
          value = 2;

          store = Store.create({
            getInitialValue: function () {
              return Rx.Observable.of(value);
            }
          });

          store.subscribe(function(val) {
            val.should.equal(value);
          });
        }
      );
    });
  });

  describe(' Operations', function() {

    describe(' getOperations', function() {

      it('should accept a single observable', function() {
        var fn = function() {
          Store.create({
            getInitialValue: function() {
              return {};
            },
            getOperations: function() {
              return Rx.Observable.of(2);
            }
          });
        };
        expect(fn).not.to.throw();
      });

      it('should accept an array of observables', function() {
        var fn = function() {
          Store.create({
            getInitialValue: function() {
              return {};
            },
            getOperations: function() {
              return [
                Rx.Observable.of(2),
                Rx.Observable.of(3),
                Rx.Observable.of(5)
              ];
            }
          });
        };

        expect(fn).not.to.throw();
      });

      it(
        'should throw an error if getInitialValue is not a function',
        function() {
          var fn = function() {
            Store.create({
              getInitialValue: 'Not the momma',

              getOperations: function () {
                return Rx.Observable.of(5);
              }
            });
          };
          expect(fn).to.throw();
        }
      );

      it(
        'should throw an error if getOperations is not a function',
        function() {
          var fn = function() {
            Store.create({
              getInitialValue: function () {
                return {};
              },
              getOperations: 'Not the momma'
            });
          };
          expect(fn).to.throw();
        }
      );

    });

    describe(' Basic transformation properties', function() {

      var value = { hello: 'world' };
      var newValue = { foo: 'bar' };
      var operations = new Rx.Subject();
      var spy = sinon.spy();
      var store;

      before(function() {
        store = Store.create({
          getInitialValue: function () {
            return value;
          },

          getOperations: function () {
            return operations;
          }
        });
        store.subscribe(spy);
      });

      it(
        'should passed to the value held by the store to the operations ' +
          'transform',
        function() {
          operations.onNext({
            transform: function(val) {
              val.should.equal(value);
              return newValue;
            }
          });
        }
      );

      it(
        'the value held by the store should be the one returned by ' +
          'the function passed to `applyOperation`',
        function() {
          spy.should.have.been.calledWith(value);
        }
      );

      it('observers should have been notified with the new value', function() {
        spy.should.have.been.calledWith(newValue);
      });

      it('store have been called twice', function() {
        spy.should.have.been.calledTwice;
      });

    });

    describe(' Basic set value operation', function() {

      var value = { hello: 'world' };
      var newValue = { foo: 'bar' };
      var operations = new Rx.Subject();
      var spy = sinon.spy();
      var store;

      before(function() {
        store = Store.create({
          getInitialValue: function () {
            return value;
          },

          getOperations: function () {
            return operations;
          }
        });
        store.subscribe(spy);
      });

      it(
        'the value held by the store should be the one returned by the ' +
          'value held by the object passed to `getOperations`',
        function() {
          spy.should.have.been.calledWith(value);
        }
      );

      it('should have notified observers with the new value', function() {
        operations.onNext({value: newValue});
        spy.should.have.been.calledWith(newValue);
      });

      it('should have called observers twice', function() {
        spy.should.have.been.calledTwice;
      });
    });

    describe(' Operations canceling', function () {

      describe(' Basic operations', function () {

        var value = {};
        var newValue = {};
        var operations = new Rx.Subject();
        var spy = sinon.spy();
        var defer = Q.defer();
        var store;

        before(function() {
          store = Store.create({
            getInitialValue: function() {
              return value;
            },
            getOperations: function() {
              return operations;
            }
          });
          store.subscribe(spy);
        });

        it(
          'should have notified observers with the initial value',
          function () {
            spy.should.have.been.calledOnce;
            spy.should.have.been.calledWith(value);
          }
        );

        it(
          'should have notified observers with the new value',
          function () {
            operations.onNext({
              value: newValue,
              confirm: defer.promise
            });
            spy.should.have.been.calledTwice;
            spy.should.have.been.calledWith(newValue);
          }
        );

        it(
          'observers should have been notified about the canceling',
          function(done) {
            defer.reject();
            defer.promise.then(
              function() { },
              function() {
                spy.should.have.been.calledThrice;
                spy.should.have.been.calledWith(value);
                done();
              });
          }
        );
      });

      describe('Nesting', function() {

        var operations = new Rx.Subject();
        var spy = sinon.spy();
        var deferred1 = Q.defer();
        var deferred2 = Q.defer();
        var store;

        before(function() {
          store = Store.create({
            getInitialValue: function() {
              return [];
            },
            getOperations: function() {
              return operations;
            }
          });
          store.subscribe(spy);

          operations.onNext({
            transform: function (arr) {
              return arr.concat('foo');
            },
            confirm: deferred1.promise
          });
        });

        it(
          'should have notified observers with the transformed value ' +
            'after the first operation has been applied',
          function() {
            spy.should.have.been.calledWith(['foo']);
          }
        );

        it(
          'should have notified observers with the transformed value ' +
            'after the second operation has been applied',
          function() {
            operations.onNext({
              transform: function (arr) {
                return arr.concat('bar');
              },
              confirm: deferred2.promise
            });
            spy.should.have.been.calledWith(['foo', 'bar']);
          }
        );

        it(
          'should have notified observers with result of applying the ' +
            'second operation on the old value after the first ' +
            'operation has failed',
          function(done) {
            deferred1.reject();
            deferred1.promise.catch(function() {
              spy.should.have.been.calledWith(['bar']);
              done();
            });
          }
        );

        it(
          'should have notified observers with the initial value after' +
            ' the second operation has failed',
            function(done) {
            deferred2.reject();
            deferred2.promise.catch(function() {
              spy.should.have.been.calledWith([]);
              done();
            });
          }
        );
      });
    });

    describe('Lifecycle', function() {

      var initialValue = new Rx.Subject();
      var initialValueSpy = sinon.spy(function () {
        return initialValue;
      });

      var operations = new Rx.Subject();
      var operationsSpy = sinon.spy(function () {
        return operations;
      });
      var store, disposable;

      describe('Before subscription events', function() {

        before(function() {
          store = Store.create({
            getInitialValue: initialValueSpy,
            getOperations: operationsSpy
          });
        });

        it(
          'getInitialValue and getOperatons should not have been called ' +
           'before a subsription',
          function() {
            initialValueSpy.should.not.have.been.called;
            operationsSpy.should.not.have.been.called;
          }
        );

      });

      describe('Subscription event setup', function() {

        before(function () {
          disposable = store.subscribe(function () {
          });
        });

        it(
          'on the first subscription the store should get the initialValue ' +
            'and subscribe to the returned observable',
          function () {
            initialValueSpy.should.have.been.calledOnce;
            initialValue.hasObservers();
          }
        );

        it(
          'until initialValue\'s observable pushes a new value getOperations ' +
            'should not have been called',
          function () {
            operationsSpy.should.not.have.been.called;
          }
        );

      });

      describe('Subscription event operations', function() {
        before(function() {
          initialValue.onNext({});
        });

        it(
          'when the initialValue resolves to a value operations spy should ' +
          'have been called',
          function() {
            operationsSpy.should.have.been.called;
            operations.hasObservers();
          }
        );
      });

      describe('', function() {

        var oldOperations = operations;
        operations = new Rx.Subject();

        before(function () {
          initialValue.onNext({});
        });

        it(
          'when the initialValue pushes a new value the store should ' +
            'dispose of the subscription to operations, re-invoke ' +
            'getOperations and subscribe to the returned observable',
          function() {
            operationsSpy.should.have.been.calledTwice;
            operations.hasObservers();
            oldOperations.hasObservers();
          }
        );
      });

      describe('', function() {

        before(function() {
          disposable.dispose();
        });

        it(
          'when all the subscription to the store has been disposed, it ' +
            'should dispose the subscription on operations and initialValue',
          function() {
            !initialValue.hasObservers();
            !operations.hasObservers();
          }
        );
      });

      describe('', function() {

        before(function() {
          store.subscribe(function() {});
        });

        it('on resubscribe it should restart the process', function() {
          store.subscribe(function() {});
          initialValueSpy.should.have.been.calledTwice;
          initialValue.hasObservers();
          operationsSpy.should.have.been.calledTwice;
          !operations.hasObservers();
        });

        it('on resubscribe it should restart the process', function() {
          initialValue.onNext({});
          operationsSpy.should.have.been.calledThrice;
          operations.hasObservers();
        });
      });

    });
  });
});
<commit_msg>clean up store test suite<commit_after>/* eslint-disable no-unused-vars, no-undefined, no-unused-expressions */
var mocha = require('mocha');
var chai = require('chai');
var expect = chai.expect;
var should = chai.should();
var Action = require('./../lib/action');
var Store = require('../lib/store');
var Mixin = require('../lib/ObservableStateMixin');
var Rx = require('rx');
var sinon = require('sinon');
var Q = require('q');
var chaiAsPromised = require('chai-as-promised');
var sinonChai = require('sinon-chai');

chai.use(chaiAsPromised);
chai.use(sinonChai);

describe('Store', function() {

  describe('create:', function() {

    it('should produce a new Rx.Observable', function() {
      var store = Store.create({
        getInitialValue: function() { }
      });
      store.should.be.an.instanceOf(Rx.Observable);
    });

    it('should throw an error if argument passed is not an object', function() {
      Store.create.bind(this, 5)
        .should
        .throw('expects an object as argument, given : 5');
    });

  });

  describe('getInitialValue:', function() {

    it('should publish null if getInitialValue returns null', function() {
      var store = Store.create({
        getInitialValue: function () {}
      });

      store.subscribe(function(val) {
        expect(val).to.be.undefined;
      });
    });

    it(
      'should return a value on initial subscription if `getInitialValue` ' +
      'returns a value',
      function() {
        var store = Store.create({
          getInitialValue: function () {
            return 5;
          }
        });

        store.subscribe(function(val) {
          expect(val).to.be.a('number');
        });
      }
    );

    it('should throw an error if getInitialValue is not defined', function() {
      Store.create.bind(this, {})
        .should
        .throw('getInitialValue should be a function given : undefined');
    });

    it(
      'should throw an error if getInitialValue is not a function',
      function() {
        var fn = function() {
          Store.create({
            getInitialValue: 'Not the momma',

            getOperations: function () {
              return Rx.Observable.of(5);
            }
          });
        };
        expect(fn).to.throw();
      }
    );

    describe('with promises and observables', function() {
      var store, value;

      it(
        'should resolve and publish a value if getInitialValue returns ' +
         ' a Promise',
        function () {
          value = 1;
          store = Store.create({
            getInitialValue: function () {
              return Q.resolve(value);
            }
          });

          store.subscribe(function(val) {
            val.should.equal(value);
          });
        }
      );

      it(
        'should notify observers if get initial value promise is rejected'
      );

      it(
        'should publish the observable\'s resolve value if getInitialValue ' +
          'returns an observable',
        function() {
          value = 2;

          store = Store.create({
            getInitialValue: function () {
              return Rx.Observable.of(value);
            }
          });

          store.subscribe(function(val) {
            val.should.equal(value);
          });
        }
      );

      it(
        'should notify observers if get initial value observable errors',
        function() {
          store = Store.create({
            getInitialValue: function () {
              return Rx.Observable.throw('snarf?');
            }
          });

          store.subscribe(function() { }, function(err) {
            err.should.be.instanceOf(Error);
          });
        }
      );
    });
  });

  describe('operations', function() {

    describe('getOperations', function() {

      it('should accept a single observable', function() {
        var fn = function() {
          Store.create({
            getInitialValue: function() {
              return {};
            },
            getOperations: function() {
              return Rx.Observable.of(2);
            }
          });
        };
        expect(fn).not.to.throw();
      });

      it('should accept an array of observables', function() {
        var fn = function() {
          Store.create({
            getInitialValue: function() {
              return {};
            },
            getOperations: function() {
              return [
                Rx.Observable.of(2),
                Rx.Observable.of(3),
                Rx.Observable.of(5)
              ];
            }
          });
        };

        expect(fn).not.to.throw();
      });

      it(
        'should throw an error if getOperations is not a function',
        function() {
          var fn = function() {
            Store.create({
              getInitialValue: function () {
                return {};
              },
              getOperations: 'Not the momma'
            });
          };
          expect(fn).to.throw();
        }
      );

      it('should throw if it does not return an observable');
      it('should throw if it return an array with a non-observable');
    });

    describe('set value', function() {

      var value = { hello: 'world' };
      var newValue = { foo: 'bar' };
      var operations = new Rx.Subject();
      var spy = sinon.spy();
      var store;

      before(function() {
        store = Store.create({
          getInitialValue: function () {
            return value;
          },

          getOperations: function () {
            return operations;
          }
        });
        store.subscribe(spy);
      });

      it(
        'the value held by the store should be the one returned by the ' +
          'value held by the object passed to `getOperations`',
        function() {
          spy.should.have.been.calledWith(value);
        }
      );

      it('should have notified observers with the new value', function() {
        operations.onNext({value: newValue});
        spy.should.have.been.calledWith(newValue);
      });

      it('should have called observers twice', function() {
        spy.should.have.been.calledTwice;
      });
    });

    describe('transforms', function() {

      var value = { hello: 'world' };
      var newValue = { foo: 'bar' };
      var operations = new Rx.Subject();
      var spy = sinon.spy();
      var store;

      before(function() {
        store = Store.create({
          getInitialValue: function () {
            return value;
          },

          getOperations: function () {
            return operations;
          }
        });
        store.subscribe(spy);
      });

      it(
        'should passed to the value held by the store to the operations ' +
          'transform',
        function() {
          operations.onNext({
            transform: function(val) {
              val.should.equal(value);
              return newValue;
            }
          });
        }
      );

      it(
        'the value held by the store should be the one returned by ' +
          'the function passed to `applyOperation`',
        function() {
          spy.should.have.been.calledWith(value);
        }
      );

      it('observers should have been notified with the new value', function() {
        spy.should.have.been.calledWith(newValue);
      });

      it('store have been called twice', function() {
        spy.should.have.been.calledTwice;
      });

    });

    describe('canceling', function () {

      var value = {};
      var newValue = {};
      var operations = new Rx.Subject();
      var spy = sinon.spy();
      var defer = Q.defer();
      var store;

      before(function() {
        store = Store.create({
          getInitialValue: function() {
            return value;
          },
          getOperations: function() {
            return operations;
          }
        });
        store.subscribe(spy);
      });

      it(
        'should have notified observers with the initial value',
        function () {
          spy.should.have.been.calledOnce;
          spy.should.have.been.calledWith(value);
        }
      );

      it(
        'should have notified observers with the new value',
        function () {
          operations.onNext({
            value: newValue,
            confirm: defer.promise
          });
          spy.should.have.been.calledTwice;
          spy.should.have.been.calledWith(newValue);
        }
      );

      it(
        'observers should have been notified about the canceling',
        function(done) {
          defer.reject();
          defer.promise.then(
            function() { },
            function() {
              spy.should.have.been.calledThrice;
              spy.should.have.been.calledWith(value);
              done();
            });
        }
      );

      describe('with nesting', function() {

        var operations = new Rx.Subject();
        var spy = sinon.spy();
        var deferred1 = Q.defer();
        var deferred2 = Q.defer();
        var store;

        before(function() {
          store = Store.create({
            getInitialValue: function() {
              return [];
            },
            getOperations: function() {
              return operations;
            }
          });
          store.subscribe(spy);

          operations.onNext({
            transform: function (arr) {
              return arr.concat('foo');
            },
            confirm: deferred1.promise
          });
        });

        it(
          'should have notified observers with the transformed value ' +
            'after the first operation has been applied',
          function() {
            spy.should.have.been.calledWith(['foo']);
          }
        );

        it(
          'should have notified observers with the transformed value ' +
            'after the second operation has been applied',
          function() {
            operations.onNext({
              transform: function (arr) {
                return arr.concat('bar');
              },
              confirm: deferred2.promise
            });
            spy.should.have.been.calledWith(['foo', 'bar']);
          }
        );

        it(
          'should have notified observers with result of applying the ' +
            'second operation on the old value after the first ' +
            'operation has failed',
          function(done) {
            deferred1.reject();
            deferred1.promise.catch(function() {
              spy.should.have.been.calledWith(['bar']);
              done();
            });
          }
        );

        it(
          'should have notified observers with the initial value after' +
            ' the second operation has failed',
            function(done) {
            deferred2.reject();
            deferred2.promise.catch(function() {
              spy.should.have.been.calledWith([]);
              done();
            });
          }
        );
      });
    });

  });

  describe('lifecycle', function() {

    var initialValue;
    var initialValueSpy;
    var operations;
    var operationsSpy;
    var store, disposable;

    beforeEach(function() {
      initialValue = new Rx.Subject();
      initialValueSpy = sinon.spy(function () {
        return initialValue;
      });

      operations = new Rx.Subject();
      operationsSpy = sinon.spy(function () {
        return operations;
      });

      store = Store.create({
        getInitialValue: initialValueSpy,
        getOperations: operationsSpy
      });
    });

    it(
      'should not have called getInitialValue and getOperatons' +
      'before a subscription',
      function() {
        initialValueSpy.should.not.have.been.called;
        operationsSpy.should.not.have.been.called;
      }
    );

    it(
      'should subscribe to operations observable on initial subscription',
      function () {
        store.subscribe(function () { });
        initialValue.hasObservers().should.be.true;
        initialValueSpy.should.have.been.calledOne;
      }
    );

    it(
      'should not subscribe to operations until initial value observable ' +
      'publishes first value',
      function () {
        store.subscribe(function(value) {
          value.should.equal('snarf');
        });
        operationsSpy.should.not.have.been.called;
        initialValue.onNext('snarf');
        operationsSpy.should.have.been.calledOnce;
        operations.hasObservers().should.be.true;
      }
    );

    it(
      'should dispose subscriptions to observable and initial value ' +
      'observable when store disposes last observer',
      function() {
        var disposable = store.subscribe(function() { });
        initialValue.hasObservers().should.be.true;
        initialValue.onNext(true);
        operations.hasObservers().should.be.true;
        disposable.dispose();
        initialValue.hasObservers().should.be.false;
        operations.hasObservers().should.be.false;
      }
    );

    it(
      'should resubscribe to observables when store is resubscribed',
      function() {
        var disposable = store.subscribe(function() { });
        initialValue.onNext(true);
        operations.hasObservers().should.be.true;
        disposable.dispose();
        initialValue.hasObservers().should.be.false;
        operations.hasObservers().should.be.false;
        store.subscribe(function() {});
        initialValue.onNext(true);
        initialValue.hasObservers().should.be.true;
        operations.hasObservers().should.be.true;
      }
    );
  });
});
<|endoftext|>