<commit_before>import {
  Button,
  Grid,
  Row,
  Col,
  FormGroup,
  FormControl,
  ControlLabel,
  Panel,
  Badge,
  ProgressBar
} from 'react-bootstrap';

import Utility from '../../../../shared/util/Utility';

import * as Lessons from '../../../../shared/reducers/Lessons';
import * as Security from '../../../../shared/reducers/Security';

import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import faPlay from '@fortawesome/fontawesome-free-solid/faPlay';
import faStar from '@fortawesome/fontawesome-free-solid/faStar';

import ToggleButton from 'react-toggle-button';

export const Reducers = [Lessons, Security];

export class selectScreen extends React.Component {
  constructor(props) {
    super(props);
    this.handleLanguageSelection = this.handleLanguageSelection.bind(this);
    this.handleStarredClick = this.handleStarredClick.bind(this);
    this.handleSpacedRepetition = this.handleSpacedRepetition.bind(this);
  }

  componentDidMount() {
    window.addEventListener('keydown', this.onKeys);

    this.props.fetchLessons(this.props.params.type).catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchUserStarredLessons().catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchFavoriteLesson(this.props.params.type).catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchaddressedQuestionsInLessons();

    if (this.props.params.type === 'kanji') {
      this.props.setQuestionLanguage('reading');
    }
  }

  // Triggers when we change between play types but remain in "selection" page
  componentWillReceiveProps(nextProps) {
    if (this.props.params.type !== nextProps.params.type) {
      this.props.fetchLessons(nextProps.params.type).catch(() => this.props.verifyUserLoggedIn());
    }
  }

  componentWillUnmount() {
    window.removeEventListener('keydown', this.onKeys);
  }

  getPageHeader() {
    switch (this.props.params.type) {
      case 'quiz':
        return 'Quiz';
      case 'guess':
        return 'Gissa ordet';
      case 'translate':
        return 'Översätt ordet';
      case 'flashcards':
        return 'Bildkort';
      case 'kanji':
        return 'Skriv Kanji';
      case 'grammar':
        return 'Böj verb';
      default:
        throw new Error('No play type specified');
    }
  }

  getPageDescription() {
    switch (this.props.params.type) {
      case 'quiz':
        return 'Sätt dina kunskaper om Japan på prov genom att välja en av 4 svarsalternativ';
      case 'guess':
        return 'Välj mellan 4 svarsalternativ för den korrekta översättningen.';
      case 'translate':
        return 'Översätt det visade ordet i fritext.';
      case 'flashcards':
        return 'Träna dig själv genom att använda kort, med frågan på ena sidan och rätta svaret på den andra.';
      case 'kanji':
        return 'Försök rita kanji-tecken med korrekta drag och i rätt ordning.';
      case 'grammar':
        return 'Böj det visade ordet i fritext på angiven verbform.';
      default:
        throw new Error('No play type specified');
    }
  }

  handleLanguageSelection(event) {
    switch (event.target.name) {
      case 'selectedLesson':
        this.props.setSelectedLesson(event.target.value);
        break;
      case 'questionType':
        this.props.setQuestionLanguage(event.target.value);
        break;
      case 'answerType':
        this.props.setAnswerLanguage(event.target.value);
        break;
      default:
        break;
    }
  }

  startLesson() {
    try {
      this.props.fetchLesson(this.props.params.type).then(() => {
        this.props.setPageByName(`/play/${this.props.params.type}`);
      });
    } catch (err) {
      this.props.verifyUserLoggedIn();
    }
  }

  handleSpacedRepetition() {
    this.props.toggleSpacedRepetition();
  }

  isSpacedRepetition() {
    return this.props.spacedRepetition && this.props.spacedRepetitionModes.includes(this.props.params.type);
  }

  handleStarredClick(lesson) {
    this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name)
      ? this.props.removeStarredLesson(lesson.name, this.props.params.type)
      : this.props.addStarredLesson(lesson.name, this.props.params.type);
  }

  getNumberOfRetentionQuestions(lesson) {
    if (
      this.props.addressedQuestionsInLessons &&
      this.props.addressedQuestionsInLessons[lesson.name] &&
      this.props.spacedRepetitionModes.includes(this.props.params.type)
    ) {
      return this.props.addressedQuestionsInLessons[lesson.name];
    }
    return { unanswered: 0, retention: 0, all: 0 };
  }

  getNumberOfFavoriteQuestions() {
    if (
      this.props.favoriteLesson &&
      this.props.favoriteLesson.nuggetData &&
      this.props.spacedRepetitionModes.includes(this.props.params.type)
    ) {
      return this.props.favoriteLesson.nuggetData;
    }
    return { unanswered: 0, retention: 0, all: 0 };
  }

  isLessonFinished(lesson) {
    if (!this.props.addressedQuestionsInLessons) {
      return false;
    }
    return (
      this.getNumberOfRetentionQuestions(lesson).unanswered === 0 &&
      this.getNumberOfRetentionQuestions(lesson).retention === 0
    );
  }

  isLessonStarted(lesson) {
    if (!this.props.addressedQuestionsInLessons) {
      return true;
    }

    return !(this.getNumberOfRetentionQuestions(lesson).unanswered === this.getNumberOfRetentionQuestions(lesson).all);
  }

  getLessons(lessons) {
    const mediumColumnSize = 6;
    const largeColumnSize = 4;
    const renderedLessons = lessons.map(lesson => (
      <Col
        key={lesson.name}
        xs={12}
        md={mediumColumnSize}
        lg={largeColumnSize}
      >
        <Panel>
          <Panel.Body>

                  {lesson.name}
                  {this.isSpacedRepetition() &&
                  !this.isLessonFinished(lesson) &&
                  this.getNumberOfRetentionQuestions(lesson).retention > 0 ? (
                      <Badge className="badge--type-todo">{this.getNumberOfRetentionQuestions(lesson).retention}</Badge>
                    ) : null}
                  {this.isSpacedRepetition() &&
                  !this.isLessonFinished(lesson) &&
                  this.getNumberOfRetentionQuestions(lesson).unanswered > 0 ? (
                      <Badge className="badge--type-new">{this.getNumberOfRetentionQuestions(lesson).unanswered}</Badge>
                    ) : null}

                {this.props.params.type === 'quiz' ? null : (

                    <Button
                      bsClass={
                        this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name)
                          ? 'favorite-icon-button favorite-icon-button--active icon-button'
                          : 'favorite-icon-button icon-button'
                      }
                      onClick={e => {
                        e.stopPropagation();
                        this.handleStarredClick(lesson);
                      }}
                    >
                      <FontAwesomeIcon
                        className={'fa-fw'}
                        icon={faStar}
                      />
                    </Button>

                )}

              {this.isSpacedRepetition() ? (

                  <ProgressBar
                    now={
                      100 -
                      (this.getNumberOfRetentionQuestions(lesson).retention +
                        this.getNumberOfRetentionQuestions(lesson).unanswered) /
                        this.getNumberOfRetentionQuestions(lesson).all *
                        100
                    }
                  />

              ) : null}

{lesson.description}

                <Button
                  onClick={e => {
                    e.stopPropagation();
                    this.props.setSelectedLesson(lesson);
                    this.startLesson();
                  }}
                  disabled={this.isSpacedRepetition() && this.isLessonFinished(lesson) && this.isLessonStarted(lesson)}
                  bsClass={'icon-button'}
                >
                  <FontAwesomeIcon
                    className={'fa-fw'}
                    icon={faPlay}
                  />
                </Button>

          </Panel.Body>
        </Panel>
      </Col>
    ));

    const adjustedLessons = [];
    for (let i = 0; i < renderedLessons.length; i++) {
      adjustedLessons.push(renderedLessons[i]);
      if ((i + 1) % (12 / mediumColumnSize) === 0) {
        const fixer = (<div
          key={`clearfix-md ${i}`}
          className="clearfix visible-md"
        />);
        adjustedLessons.push(fixer);
      }

      if ((i + 1) % (12 / largeColumnSize) === 0) {
        const fixer = (<div
          key={`clearfix-lg ${i}`}
          className="clearfix visible-lg"
        />);
        adjustedLessons.push(fixer);
      }
    }
    return adjustedLessons ? <Row>{adjustedLessons}</Row> : null;
  }

  getLanguageSelection() {
    const answerLanguages = [];
    let questionLanguages = [];
    answerLanguages.push(
      <option
        key={'reading'}
        value={'reading'}
      >
        Japanska
      </option>
    );
    answerLanguages.push(
      <option
        key={'swedish'}
        value={'swedish'}
      >
        Svenska
      </option>
    );
    /* devcode:start */
    answerLanguages.push(
      <option
        key={'english'}
        value={'english'}
      >
        Engelska
      </option>
    );
    /* devcode:end */

    if (this.props.params.type === 'kanji') {
      questionLanguages = answerLanguages.shift();
    } else {
      questionLanguages = answerLanguages;
    }

    if (this.props.params.type === 'quiz' || this.props.params.type === 'grammar') {
      return
;
    } else {
      return (
        <FormGroup>
          <Row>
            <Col
              xs={12}
              sm={4}
            >
              <FormGroup controlId="questionLanguageSelection">
                <ControlLabel>Frågespråk</ControlLabel>
                <FormControl
                  componentClass="select"
                  name="questionType"
                  onChange={this.handleLanguageSelection}
                  value={this.props.questionType}
                  disabled={this.props.params.type === 'kanji'}
                >
                  {questionLanguages}
                </FormControl>
              </FormGroup>
            </Col>
            <Col
              xs={12}
              sm={4}
            >
              <FormGroup controlId="answerLanguageSelection">
                <ControlLabel>Svarspråk</ControlLabel>
                <FormControl
                  componentClass="select"
                  name="answerType"
                  onChange={this.handleLanguageSelection}
                  value={this.props.answerType}
                >
                  {answerLanguages}
                </FormControl>
              </FormGroup>
            </Col>
            <Col
              xs={12}
              sm={4}
            >
              <FormGroup>
                <ControlLabel>Smart inlärningsläge</ControlLabel>
                <ToggleButton
                  inactiveLabel={'Av'}
                  activeLabel={'På'}
                  value={this.isSpacedRepetition()}
                  onToggle={this.handleSpacedRepetition}
                />
              </FormGroup>
            </Col>
          </Row>
        </FormGroup>
      );
    }
  }

  render() {
    let lessonsUnfinished, lessonsFinished, lessonsUnstarted, lessonsAll;
    if (this.isSpacedRepetition()) {
      lessonsUnfinished = this.getLessons(this.props.lessons.filter(lesson => !this.isLessonFinished(lesson)));
      lessonsFinished = this.getLessons(
        this.props.lessons.filter(lesson => this.isLessonFinished(lesson) && this.isLessonStarted(lesson))
      );
      lessonsUnstarted = this.getLessons(this.props.lessons.filter(lesson => !this.isLessonStarted(lesson)));
    } else {
      lessonsAll = this.getLessons(this.props.lessons);
    }

    const favoriteLesson = (
      <Row>
        <Col
          xs={12}
          md={12}
          lg={12}
        >
          <Panel>
            <Panel.Body>

                    {'Blandade frågor.'}
                    {this.isSpacedRepetition() && this.getNumberOfFavoriteQuestions().retention > 0 ? (
                      <Badge className="badge--type-todo">{this.getNumberOfFavoriteQuestions().retention}</Badge>
                    ) : null}
                    {this.isSpacedRepetition() && this.getNumberOfFavoriteQuestions().unanswered > 0 ? (
                      <Badge className="badge--type-new">{this.getNumberOfFavoriteQuestions().unanswered}</Badge>
                    ) : null}

                {this.isSpacedRepetition() ? (

                    <ProgressBar
                      now={
                        this.getNumberOfFavoriteQuestions().all > 0
                          ? 100 -
                            (this.getNumberOfFavoriteQuestions().retention +
                              this.getNumberOfFavoriteQuestions().unanswered) /
                              this.getNumberOfFavoriteQuestions().all *
                              100
                          : 0
                      }
                    />

                ) : null}

                  {'Blandade frågor från alla dina favoritmarkerade lektioner.'}

                  <Button
                    onClick={e => {
                      e.stopPropagation();
                      this.props.setSelectedLesson(this.props.favoriteLesson);
                      this.startLesson();
                    }}
                    disabled={this.props.starredLessons.length === 0}
                    bsClass={'icon-button'}
                  >
                    <FontAwesomeIcon
                      className={'fa-fw'}
                      icon={faPlay}
                    />
                  </Button>

            </Panel.Body>
          </Panel>
        </Col>
      </Row>
    );

    return (
      <Grid>
        <Col>

{this.getPageHeader()}

{this.getPageDescription()}

Lektioner

          {this.props.params.type !== 'quiz' && this.props.params.type !== 'grammar' ? favoriteLesson : null}
          {this.isSpacedRepetition() ? (

              {lessonsUnfinished ?
Pågående lektioner
 : null}
              {lessonsUnfinished}

              {lessonsUnstarted ?
Ej påbörjade lektioner
 : null}
              {lessonsUnstarted}

              {lessonsFinished ?
Färdiga lektioner
 : null}
              {lessonsFinished}

          ) : (
            lessonsAll
          )}
          {this.getLanguageSelection()}
        </Col>
      </Grid>
    );
  }
}

selectScreen.defaultProps = Utility.reduxEnabledDefaultProps({}, Reducers);

selectScreen.propTypes = Utility.reduxEnabledPropTypes({}, Reducers);

export default Utility.superConnect(this, Reducers)(selectScreen);
<commit_msg>visuals for language select<commit_after>import {
  Button,
  Grid,
  Row,
  Col,
  FormGroup,
  FormControl,
  Form,
  ControlLabel,
  Panel,
  Badge,
  ProgressBar,
  Radio
} from 'react-bootstrap';

import Utility from '../../../../shared/util/Utility';

import * as Lessons from '../../../../shared/reducers/Lessons';
import * as Security from '../../../../shared/reducers/Security';

import FontAwesomeIcon from '@fortawesome/react-fontawesome';
import faPlay from '@fortawesome/fontawesome-free-solid/faPlay';
import faStar from '@fortawesome/fontawesome-free-solid/faStar';

import ToggleButton from 'react-toggle-button';

export const Reducers = [Lessons, Security];

export class selectScreen extends React.Component {
  constructor(props) {
    super(props);
    this.handleLanguageSelection = this.handleLanguageSelection.bind(this);
    this.handleStarredClick = this.handleStarredClick.bind(this);
    this.handleSpacedRepetition = this.handleSpacedRepetition.bind(this);
  }

  componentDidMount() {
    window.addEventListener('keydown', this.onKeys);

    this.props.fetchLessons(this.props.params.type).catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchUserStarredLessons().catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchFavoriteLesson(this.props.params.type).catch(() => this.props.verifyUserLoggedIn());

    this.props.fetchaddressedQuestionsInLessons();

    if (this.props.params.type === 'kanji') {
      this.props.setQuestionLanguage('reading');
    }
  }

  // Triggers when we change between play types but remain in "selection" page
  componentWillReceiveProps(nextProps) {
    if (this.props.params.type !== nextProps.params.type) {
      this.props.fetchLessons(nextProps.params.type).catch(() => this.props.verifyUserLoggedIn());
    }
  }

  componentWillUnmount() {
    window.removeEventListener('keydown', this.onKeys);
  }

  getPageHeader() {
    switch (this.props.params.type) {
      case 'quiz':
        return 'Quiz';
      case 'guess':
        return 'Gissa ordet';
      case 'translate':
        return 'Översätt ordet';
      case 'flashcards':
        return 'Bildkort';
      case 'kanji':
        return 'Skriv Kanji';
      case 'grammar':
        return 'Böj verb';
      default:
        throw new Error('No play type specified');
    }
  }

  getPageDescription() {
    switch (this.props.params.type) {
      case 'quiz':
        return 'Sätt dina kunskaper om Japan på prov genom att välja en av 4 svarsalternativ';
      case 'guess':
        return 'Välj mellan 4 svarsalternativ för den korrekta översättningen.';
      case 'translate':
        return 'Översätt det visade ordet i fritext.';
      case 'flashcards':
        return 'Träna dig själv genom att använda kort, med frågan på ena sidan och rätta svaret på den andra.';
      case 'kanji':
        return 'Försök rita kanji-tecken med korrekta drag och i rätt ordning.';
      case 'grammar':
        return 'Böj det visade ordet i fritext på angiven verbform.';
      default:
        throw new Error('No play type specified');
    }
  }

  handleLanguageSelection(event) {
    switch (event.target.name) {
      case 'selectedLesson':
        this.props.setSelectedLesson(event.target.value);
        break;
      case 'questionType':
        this.props.setQuestionLanguage(event.target.value);
        break;
      case 'answerType':
        this.props.setAnswerLanguage(event.target.value);
        break;
      default:
        break;
    }
  }

  startLesson() {
    try {
      this.props.fetchLesson(this.props.params.type).then(() => {
        this.props.setPageByName(`/play/${this.props.params.type}`);
      });
    } catch (err) {
      this.props.verifyUserLoggedIn();
    }
  }

  handleSpacedRepetition() {
    this.props.toggleSpacedRepetition();
  }

  isSpacedRepetition() {
    return this.props.spacedRepetition && this.props.spacedRepetitionModes.includes(this.props.params.type);
  }

  handleStarredClick(lesson) {
    this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name)
      ? this.props.removeStarredLesson(lesson.name, this.props.params.type)
      : this.props.addStarredLesson(lesson.name, this.props.params.type);
  }

  getNumberOfRetentionQuestions(lesson) {
    if (
      this.props.addressedQuestionsInLessons &&
      this.props.addressedQuestionsInLessons[lesson.name] &&
      this.props.spacedRepetitionModes.includes(this.props.params.type)
    ) {
      return this.props.addressedQuestionsInLessons[lesson.name];
    }
    return { unanswered: 0, retention: 0, all: 0 };
  }

  getNumberOfFavoriteQuestions() {
    if (
      this.props.favoriteLesson &&
      this.props.favoriteLesson.nuggetData &&
      this.props.spacedRepetitionModes.includes(this.props.params.type)
    ) {
      return this.props.favoriteLesson.nuggetData;
    }
    return { unanswered: 0, retention: 0, all: 0 };
  }

  isLessonFinished(lesson) {
    if (!this.props.addressedQuestionsInLessons) {
      return false;
    }
    return (
      this.getNumberOfRetentionQuestions(lesson).unanswered === 0 &&
      this.getNumberOfRetentionQuestions(lesson).retention === 0
    );
  }

  isLessonStarted(lesson) {
    if (!this.props.addressedQuestionsInLessons) {
      return true;
    }

    return !(this.getNumberOfRetentionQuestions(lesson).unanswered === this.getNumberOfRetentionQuestions(lesson).all);
  }

  getLessons(lessons) {
    const mediumColumnSize = 6;
    const largeColumnSize = 4;
    const renderedLessons = lessons.map(lesson => (
      <Col
        key={lesson.name}
        xs={12}
        md={mediumColumnSize}
        lg={largeColumnSize}
      >
        <Panel>
          <Panel.Body>

                  {lesson.name}
                  {this.isSpacedRepetition() &&
                  !this.isLessonFinished(lesson) &&
                  this.getNumberOfRetentionQuestions(lesson).retention > 0 ? (
                      <Badge className="badge--type-todo">{this.getNumberOfRetentionQuestions(lesson).retention}</Badge>
                    ) : null}
                  {this.isSpacedRepetition() &&
                  !this.isLessonFinished(lesson) &&
                  this.getNumberOfRetentionQuestions(lesson).unanswered > 0 ? (
                      <Badge className="badge--type-new">{this.getNumberOfRetentionQuestions(lesson).unanswered}</Badge>
                    ) : null}

                {this.props.params.type === 'quiz' ? null : (

                    <Button
                      bsClass={
                        this.props.starredLessons.map(userLesson => userLesson.lesson.name).includes(lesson.name)
                          ? 'favorite-icon-button favorite-icon-button--active icon-button'
                          : 'favorite-icon-button icon-button'
                      }
                      onClick={e => {
                        e.stopPropagation();
                        this.handleStarredClick(lesson);
                      }}
                    >
                      <FontAwesomeIcon
                        className={'fa-fw'}
                        icon={faStar}
                      />
                    </Button>

                )}

              {this.isSpacedRepetition() ? (

                  <ProgressBar
                    now={
                      100 -
                      (this.getNumberOfRetentionQuestions(lesson).retention +
                        this.getNumberOfRetentionQuestions(lesson).unanswered) /
                        this.getNumberOfRetentionQuestions(lesson).all *
                        100
                    }
                  />

              ) : null}

{lesson.description}

                <Button
                  onClick={e => {
                    e.stopPropagation();
                    this.props.setSelectedLesson(lesson);
                    this.startLesson();
                  }}
                  disabled={this.isSpacedRepetition() && this.isLessonFinished(lesson) && this.isLessonStarted(lesson)}
                  bsClass={'icon-button'}
                >
                  <FontAwesomeIcon
                    className={'fa-fw'}
                    icon={faPlay}
                  />
                </Button>

          </Panel.Body>
        </Panel>
      </Col>
    ));

    const adjustedLessons = [];
    for (let i = 0; i < renderedLessons.length; i++) {
      adjustedLessons.push(renderedLessons[i]);
      if ((i + 1) % (12 / mediumColumnSize) === 0) {
        const fixer = (<div
          key={`clearfix-md ${i}`}
          className="clearfix visible-md"
        />);
        adjustedLessons.push(fixer);
      }

      if ((i + 1) % (12 / largeColumnSize) === 0) {
        const fixer = (<div
          key={`clearfix-lg ${i}`}
          className="clearfix visible-lg"
        />);
        adjustedLessons.push(fixer);
      }
    }
    return adjustedLessons ? <Row>{adjustedLessons}</Row> : null;
  }

  getLanguageSelection() {
    const languages = [];
    languages.push(
      <Radio
        key={'reading'}
        value={'reading'}
        name={'languageSelect'}
        onChange={this.handleLanguageSelection}
      >
        Japanska till Svenska
      </Radio>
    );
    languages.push(
      <Radio
        key={'swedish'}
        value={'swedish'}
        name={'languageSelect'}
        onChange={this.handleLanguageSelection}
      >
        Svenska till Japanska
      </Radio>
    );
    if (this.props.params.type === 'quiz' || this.props.params.type === 'grammar') {
      return
;
    } else {
      return (
        <FormGroup>
          <FormGroup controlId="questionLanguageSelection">
            <ControlLabel>Frågespråk</ControlLabel>
            {languages}
          </FormGroup>
          <FormGroup>
            <ControlLabel>Smart inlärningsläge</ControlLabel>
            <ToggleButton
              inactiveLabel={'Av'}
              activeLabel={'På'}
              value={this.isSpacedRepetition()}
              onToggle={this.handleSpacedRepetition}
            />
          </FormGroup>
        </FormGroup>
      );
    }
  }

  render() {
    let lessonsUnfinished, lessonsFinished, lessonsUnstarted, lessonsAll;
    if (this.isSpacedRepetition()) {
      lessonsUnfinished = this.getLessons(this.props.lessons.filter(lesson => !this.isLessonFinished(lesson)));
      lessonsFinished = this.getLessons(
        this.props.lessons.filter(lesson => this.isLessonFinished(lesson) && this.isLessonStarted(lesson))
      );
      lessonsUnstarted = this.getLessons(this.props.lessons.filter(lesson => !this.isLessonStarted(lesson)));
    } else {
      lessonsAll = this.getLessons(this.props.lessons);
    }

    const favoriteLesson = (
      <Row>
        <Col
          xs={12}
          md={12}
          lg={12}
        >
          <Panel>
            <Panel.Body>

                    {'Blandade frågor.'}
                    {this.isSpacedRepetition() && this.getNumberOfFavoriteQuestions().retention > 0 ? (
                      <Badge className="badge--type-todo">{this.getNumberOfFavoriteQuestions().retention}</Badge>
                    ) : null}
                    {this.isSpacedRepetition() && this.getNumberOfFavoriteQuestions().unanswered > 0 ? (
                      <Badge className="badge--type-new">{this.getNumberOfFavoriteQuestions().unanswered}</Badge>
                    ) : null}

                {this.isSpacedRepetition() ? (

                    <ProgressBar
                      now={
                        this.getNumberOfFavoriteQuestions().all > 0
                          ? 100 -
                            (this.getNumberOfFavoriteQuestions().retention +
                              this.getNumberOfFavoriteQuestions().unanswered) /
                              this.getNumberOfFavoriteQuestions().all *
                              100
                          : 0
                      }
                    />

                ) : null}

                  {'Blandade frågor från alla dina favoritmarkerade lektioner.'}

                  <Button
                    onClick={e => {
                      e.stopPropagation();
                      this.props.setSelectedLesson(this.props.favoriteLesson);
                      this.startLesson();
                    }}
                    disabled={this.props.starredLessons.length === 0}
                    bsClass={'icon-button'}
                  >
                    <FontAwesomeIcon
                      className={'fa-fw'}
                      icon={faPlay}
                    />
                  </Button>

            </Panel.Body>
          </Panel>
        </Col>
      </Row>
    );

    return (
      <Grid>
        <Col>

{this.getPageHeader()}

{this.getPageDescription()}

          {this.getLanguageSelection()}

Lektioner

          {this.props.params.type !== 'quiz' && this.props.params.type !== 'grammar' ? favoriteLesson : null}
          {this.isSpacedRepetition() ? (

              {lessonsUnfinished ?
Pågående lektioner
 : null}
              {lessonsUnfinished}

              {lessonsUnstarted ?
Ej påbörjade lektioner
 : null}
              {lessonsUnstarted}

              {lessonsFinished ?
Färdiga lektioner
 : null}
              {lessonsFinished}

          ) : (
            lessonsAll
          )}
        </Col>
      </Grid>
    );
  }
}

selectScreen.defaultProps = Utility.reduxEnabledDefaultProps({}, Reducers);

selectScreen.propTypes = Utility.reduxEnabledPropTypes({}, Reducers);

export default Utility.superConnect(this, Reducers)(selectScreen);
<|endoftext|>


<commit_before>import Plane from './Plane';

const DEFAULT_BORDER_SIZE = 10;

/**
 * The NineSlicePlane allows you to stretch a texture using 9-slice scaling. The corners will remain unscaled (useful
 * for buttons with rounded corners for example) and the other areas will be scaled horizontally and or vertically
 *
 *```js
 * let Plane9 = new PIXI.NineSlicePlane(PIXI.Texture.fromImage('BoxWithRoundedCorners.png'), 15, 15, 15, 15);
 *  ```
 *

 *      A                          B
 *    +---+----------------------+---+
 *  C | 1 |          2           | 3 |
 *    +---+----------------------+---+
 *    |   |                      |   |
 *    | 4 |          5           | 6 |
 *    |   |                      |   |
 *    +---+----------------------+---+
 *  D | 7 |          8           | 9 |
 *    +---+----------------------+---+

 *  When changing this objects width and/or height:
 *     areas 1 3 7 and 9 will remain unscaled.
 *     areas 2 and 8 will be stretched horizontally
 *     areas 4 and 6 will be stretched vertically
 *     area 5 will be stretched both horizontally and vertically
 *

 *
 * @class
 * @extends PIXI.mesh.Plane
 * @memberof PIXI.mesh
 *
 */
export default class NineSlicePlane extends Plane
{
    /**
     * @param {PIXI.Texture} texture - The texture to use on the NineSlicePlane.
     * @param {int} [leftWidth=10] size of the left vertical bar (A)
     * @param {int} [topHeight=10] size of the top horizontal bar (C)
     * @param {int} [rightWidth=10] size of the right vertical bar (B)
     * @param {int} [bottomHeight=10] size of the bottom horizontal bar (D)
     */
    constructor(texture, leftWidth, topHeight, rightWidth, bottomHeight)
    {
        super(texture, 4, 4);

        this._lastWidth = null;
        this._lastHeight = null;

        this._width = texture.orig.width;
        this._height = texture.orig.height;

        /**
         * The width of the left column (a)
         *
         * @member {number}
         * @memberof PIXI.NineSlicePlane#
         * @override
         */
        this._leftWidth = typeof leftWidth !== 'undefined' ? leftWidth : DEFAULT_BORDER_SIZE;

        /**
         * The width of the right column (b)
         *
         * @member {number}
         * @memberof PIXI.NineSlicePlane#
         * @override
         */
        this._rightWidth = typeof rightWidth !== 'undefined' ? rightWidth : DEFAULT_BORDER_SIZE;

        /**
         * The height of the top row (c)
         *
         * @member {number}
         * @memberof PIXI.NineSlicePlane#
         * @override
         */
        this._topHeight = typeof topHeight !== 'undefined' ? topHeight : DEFAULT_BORDER_SIZE;

        /**
         * The height of the bottom row (d)
         *
         * @member {number}
         * @memberof PIXI.NineSlicePlane#
         * @override
         */
        this._bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;

        this.refresh(true);
    }

    /**
     * The width of the left column
     *
     * @member {number}
     */
    get leftWidth()
    {
        return this._leftWidth;
    }

    set leftWidth(value) // eslint-disable-line require-jsdoc
    {
        if (this._leftWidth === value)
        {
            return;
        }
        this._leftWidth = value;
        this._verticesID++;
    }

    /**
     * The width of the right column
     *
     * @member {number}
     */
    get rightWidth()
    {
        return this._rightWidth;
    }

    set rightWidth(value) // eslint-disable-line require-jsdoc
    {
        if (this._rightWidth === value)
        {
            return;
        }
        this._rightWidth = value;
        this._verticesID++;
    }

    /**
     * The height of the top row
     *
     * @member {number}
     */
    get topHeight()
    {
        return this._topHeight;
    }

    set topHeight(value) // eslint-disable-line require-jsdoc
    {
        if (this._topHeight === value)
        {
            return;
        }
        this._topHeight = value;
        this._verticesID++;
    }

    /**
     * The height of the bottom row
     *
     * @member {number}
     */
    get bottomHeight()
    {
        return this._bottomHeight;
    }

    set bottomHeight(value) // eslint-disable-line require-jsdoc
    {
        if (this._bottomHeight === value)
        {
            return;
        }
        this._bottomHeight = value;
        this._verticesID++;
    }

    /**
     * refreshes both vertices and uvs
     *
     * @private
     */
    _refreshVertices()
    {
        this.updateHorizontalVertices();
        this.updateVerticalVertices();

        const vertices = this.vertices;
        const offsetX = this._anchor._x * this.width;
        const offsetY = this._anchor._y * this.height;

        for (let i = 0; i < 32; i += 2)
        {
            vertices[i] += offsetX;
            vertices[i + 1] += offsetY;
        }
        this.dirty++;
    }

    /**
     * does nothing
     *
     * @private
     */
    _refreshUvs()
    {
        this._uvsID = this._lastUvsID;

        const uvs = this.uvs;
        const texture = this._texture;
        const width = texture.orig.width;
        const height = texture.orig.height;

        uvs[0] = uvs[8] = uvs[16] = uvs[24] = 0;
        uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._leftWidth / width;
        uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - this._rightWidth / width;
        uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;

        uvs[1] = uvs[3] = uvs[5] = uvs[7] = 0;
        uvs[9] = uvs[11] = uvs[13] = uvs[15] = this._topHeight / height;
        uvs[17] = uvs[19] = uvs[21] = uvs[23] = 1 - this._bottomHeight / height;
        uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;

        this.dirty++;

        this.multiplyUvs();
    }

    /**
     * Updates the horizontal vertices.
     */
    updateHorizontalVertices()
    {
        const vertices = this.vertices;

        vertices[1] = vertices[3] = vertices[5] = vertices[7] = 0;
        vertices[9] = vertices[11] = vertices[13] = vertices[15] = this._topHeight;
        vertices[17] = vertices[19] = vertices[21] = vertices[23] = this._height - this._bottomHeight;
        vertices[25] = vertices[27] = vertices[29] = vertices[31] = this._height;
    }

    /**
     * Updates the vertical vertices.
     *
     */
    updateVerticalVertices()
    {
        const vertices = this.vertices;

        vertices[0] = vertices[8] = vertices[16] = vertices[24] = 0;
        vertices[2] = vertices[10] = vertices[18] = vertices[26] = this._leftWidth;
        vertices[4] = vertices[12] = vertices[20] = vertices[28] = this._width - this._rightWidth;
        vertices[6] = vertices[14] = vertices[22] = vertices[30] = this._width;
    }

    /**
     * Renders the object using the WebGL renderer
     *
     * @private
     * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer
     */
    _renderWebGL(renderer)
    {
        // no texture - no drawImage
        if (!this._texture.valid)
        {
            return;
        }

        this.refresh();

        renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
        renderer.plugins[this.pluginName].render(this);
    }

    /**
     * Renders the object using the Canvas renderer
     *
     * @private
     * @param {PIXI.CanvasRenderer} renderer - The canvas renderer to render with.
     */
    _renderCanvas(renderer)
    {
        // no texture - no drawImage
        if (!this._texture.valid)
        {
            return;
        }

        this.refresh();

        // advanced rendering - allow texture rotates
        if (this._texture.rotate)
        {
            super._renderCanvas(renderer);

            return;
        }

        const context = renderer.context;

        context.globalAlpha = this.worldAlpha;

        const transform = this.worldTransform;
        const res = renderer.resolution;

        if (renderer.roundPixels)
        {
            context.setTransform(
                transform.a * res,
                transform.b * res,
                transform.c * res,
                transform.d * res,
                (transform.tx * res) | 0,
                (transform.ty * res) | 0
            );
        }
        else
        {
            context.setTransform(
                transform.a * res,
                transform.b * res,
                transform.c * res,
                transform.d * res,
                transform.tx * res,
                transform.ty * res
            );
        }

        const base = this._texture.baseTexture;
        const textureSource = base.source;
        const w = base.width;
        const h = base.height;

        this.drawSegment(context, textureSource, w, h, 0, 1, 10, 11);
        this.drawSegment(context, textureSource, w, h, 2, 3, 12, 13);
        this.drawSegment(context, textureSource, w, h, 4, 5, 14, 15);
        this.drawSegment(context, textureSource, w, h, 8, 9, 18, 19);
        this.drawSegment(context, textureSource, w, h, 10, 11, 20, 21);
        this.drawSegment(context, textureSource, w, h, 12, 13, 22, 23);
        this.drawSegment(context, textureSource, w, h, 16, 17, 26, 27);
        this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29);
        this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31);
    }

    /**
     * Renders one segment of the plane.
     * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure
     * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used.
     *
     * @private
     * @param {CanvasRenderingContext2D} context - The context to draw with.
     * @param {CanvasImageSource} textureSource - The source to draw.
     * @param {number} w - width of the texture
     * @param {number} h - height of the texture
     * @param {number} x1 - x index 1
     * @param {number} y1 - y index 1
     * @param {number} x2 - x index 2
     * @param {number} y2 - y index 2
     */
    drawSegment(context, textureSource, w, h, x1, y1, x2, y2)
    {
        // otherwise you get weird results when using slices of that are 0 wide or high.
        const uvs = this.uvs;
        const vertices = this.vertices;

        let sw = (uvs[x2] - uvs[x1]) * w;
        let sh = (uvs[y2] - uvs[y1]) * h;
        let dw = vertices[x2] - vertices[x1];
        let dh = vertices[y2] - vertices[y1];

        // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn.
        if (sw < 1)
        {
            sw = 1;
        }

        if (sh < 1)
        {
            sh = 1;
        }

        // make sure destination is at least 1 pixel wide and high, otherwise you get
        // lines when rendering close to original size.
        if (dw < 1)
        {
            dw = 1;
        }

        if (dh < 1)
        {
            dh = 1;
        }

        context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh);
    }
}
<commit_msg>Fix NineSlicePlane<commit_after>import Plane from './Plane';

const DEFAULT_BORDER_SIZE = 10;

/**
 * The NineSlicePlane allows you to stretch a texture using 9-slice scaling. The corners will remain unscaled (useful
 * for buttons with rounded corners for example) and the other areas will be scaled horizontally and or vertically
 *
 *```js
 * let Plane9 = new PIXI.NineSlicePlane(PIXI.Texture.fromImage('BoxWithRoundedCorners.png'), 15, 15, 15, 15);
 *  ```
 *

 *      A                          B
 *    +---+----------------------+---+
 *  C | 1 |          2           | 3 |
 *    +---+----------------------+---+
 *    |   |                      |   |
 *    | 4 |          5           | 6 |
 *    |   |                      |   |
 *    +---+----------------------+---+
 *  D | 7 |          8           | 9 |
 *    +---+----------------------+---+

 *  When changing this objects width and/or height:
 *     areas 1 3 7 and 9 will remain unscaled.
 *     areas 2 and 8 will be stretched horizontally
 *     areas 4 and 6 will be stretched vertically
 *     area 5 will be stretched both horizontally and vertically
 *

 *
 * @class
 * @extends PIXI.mesh.Plane
 * @memberof PIXI.mesh
 *
 */
export default class NineSlicePlane extends Plane
{
    /**
     * @param {PIXI.Texture} texture - The texture to use on the NineSlicePlane.
     * @param {int} [leftWidth=10] size of the left vertical bar (A)
     * @param {int} [topHeight=10] size of the top horizontal bar (C)
     * @param {int} [rightWidth=10] size of the right vertical bar (B)
     * @param {int} [bottomHeight=10] size of the bottom horizontal bar (D)
     */
    constructor(texture, leftWidth, topHeight, rightWidth, bottomHeight)
    {
        super(texture, 4, 4);

        this._lastWidth = null;
        this._lastHeight = null;

        this._width = texture.orig.width;
        this._height = texture.orig.height;

        /**
         * The width of the left column (a)
         *
         * @member {number}
         * @memberof PIXI.NineSlicePlane#
         * @override
         */
        this._leftWidth = typeof leftWidth !== 'undefined' ? leftWidth : DEFAULT_BORDER_SIZE;

        /**
         * The width of the right column (b)
         *
         * @member {number}
         * @memberof PIXI.NineSlicePlane#
         * @override
         */
        this._rightWidth = typeof rightWidth !== 'undefined' ? rightWidth : DEFAULT_BORDER_SIZE;

        /**
         * The height of the top row (c)
         *
         * @member {number}
         * @memberof PIXI.NineSlicePlane#
         * @override
         */
        this._topHeight = typeof topHeight !== 'undefined' ? topHeight : DEFAULT_BORDER_SIZE;

        /**
         * The height of the bottom row (d)
         *
         * @member {number}
         * @memberof PIXI.NineSlicePlane#
         * @override
         */
        this._bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;

        this.refresh(true);
    }

    /**
     * The width of the left column
     *
     * @member {number}
     */
    get leftWidth()
    {
        return this._leftWidth;
    }

    set leftWidth(value) // eslint-disable-line require-jsdoc
    {
        if (this._leftWidth === value)
        {
            return;
        }
        this._leftWidth = value;
        this._verticesID++;
    }

    /**
     * The width of the right column
     *
     * @member {number}
     */
    get rightWidth()
    {
        return this._rightWidth;
    }

    set rightWidth(value) // eslint-disable-line require-jsdoc
    {
        if (this._rightWidth === value)
        {
            return;
        }
        this._rightWidth = value;
        this._verticesID++;
    }

    /**
     * The height of the top row
     *
     * @member {number}
     */
    get topHeight()
    {
        return this._topHeight;
    }

    set topHeight(value) // eslint-disable-line require-jsdoc
    {
        if (this._topHeight === value)
        {
            return;
        }
        this._topHeight = value;
        this._verticesID++;
    }

    /**
     * The height of the bottom row
     *
     * @member {number}
     */
    get bottomHeight()
    {
        return this._bottomHeight;
    }

    set bottomHeight(value) // eslint-disable-line require-jsdoc
    {
        if (this._bottomHeight === value)
        {
            return;
        }
        this._bottomHeight = value;
        this._verticesID++;
    }

    /**
     * refreshes both vertices and uvs
     *
     * @private
     */
    _refreshVertices()
    {
        this.updateHorizontalVertices();
        this.updateVerticalVertices();

        const vertices = this.vertices;
        const offsetX = this._anchor._x * this.width;
        const offsetY = this._anchor._y * this.height;

        for (let i = 0; i < 32; i += 2)
        {
            vertices[i] += offsetX;
            vertices[i + 1] += offsetY;
        }
        this.dirty++;
    }

    /**
     * does nothing
     *
     * @private
     */
    _refreshUvs()
    {
        this._uvsID = this._lastUvsID;

        const uvs = this.uvs;
        const texture = this._texture;
        const width = texture.orig.width;
        const height = texture.orig.height;

        uvs[0] = uvs[8] = uvs[16] = uvs[24] = 0;
        uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._leftWidth / width;
        uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._rightWidth / width);
        uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;

        uvs[1] = uvs[3] = uvs[5] = uvs[7] = 0;
        uvs[9] = uvs[11] = uvs[13] = uvs[15] = this._topHeight / height;
        uvs[17] = uvs[19] = uvs[21] = uvs[23] = 1 - (this._bottomHeight / height);
        uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;

        this.dirty++;

        this.multiplyUvs();
    }

    /**
     * Updates the horizontal vertices.
     */
    updateHorizontalVertices()
    {
        const vertices = this.vertices;

        vertices[1] = vertices[3] = vertices[5] = vertices[7] = 0;
        vertices[9] = vertices[11] = vertices[13] = vertices[15] = this._topHeight;
        vertices[17] = vertices[19] = vertices[21] = vertices[23] = this._height - this._bottomHeight;
        vertices[25] = vertices[27] = vertices[29] = vertices[31] = this._height;
    }

    /**
     * Updates the vertical vertices.
     *
     */
    updateVerticalVertices()
    {
        const vertices = this.vertices;

        vertices[0] = vertices[8] = vertices[16] = vertices[24] = 0;
        vertices[2] = vertices[10] = vertices[18] = vertices[26] = this._leftWidth;
        vertices[4] = vertices[12] = vertices[20] = vertices[28] = this._width - this._rightWidth;
        vertices[6] = vertices[14] = vertices[22] = vertices[30] = this._width;
    }

    /**
     * Renders the object using the WebGL renderer
     *
     * @private
     * @param {PIXI.WebGLRenderer} renderer - a reference to the WebGL renderer
     */
    _renderWebGL(renderer)
    {
        // no texture - no drawImage
        if (!this._texture.valid)
        {
            return;
        }

        this.refresh();

        renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
        renderer.plugins[this.pluginName].render(this);
    }

    /**
     * Renders the object using the Canvas renderer
     *
     * @private
     * @param {PIXI.CanvasRenderer} renderer - The canvas renderer to render with.
     */
    _renderCanvas(renderer)
    {
        // no texture - no drawImage
        if (!this._texture.valid)
        {
            return;
        }

        this.refresh();

        // advanced rendering - allow texture rotates
        if (this._texture.rotate)
        {
            super._renderCanvas(renderer);

            return;
        }

        const context = renderer.context;

        context.globalAlpha = this.worldAlpha;

        const transform = this.worldTransform;
        const res = renderer.resolution;

        if (renderer.roundPixels)
        {
            context.setTransform(
                transform.a * res,
                transform.b * res,
                transform.c * res,
                transform.d * res,
                (transform.tx * res) | 0,
                (transform.ty * res) | 0
            );
        }
        else
        {
            context.setTransform(
                transform.a * res,
                transform.b * res,
                transform.c * res,
                transform.d * res,
                transform.tx * res,
                transform.ty * res
            );
        }

        const base = this._texture.baseTexture;
        const textureSource = base.source;
        const w = base.width;
        const h = base.height;

        this.drawSegment(context, textureSource, w, h, 0, 1, 10, 11);
        this.drawSegment(context, textureSource, w, h, 2, 3, 12, 13);
        this.drawSegment(context, textureSource, w, h, 4, 5, 14, 15);
        this.drawSegment(context, textureSource, w, h, 8, 9, 18, 19);
        this.drawSegment(context, textureSource, w, h, 10, 11, 20, 21);
        this.drawSegment(context, textureSource, w, h, 12, 13, 22, 23);
        this.drawSegment(context, textureSource, w, h, 16, 17, 26, 27);
        this.drawSegment(context, textureSource, w, h, 18, 19, 28, 29);
        this.drawSegment(context, textureSource, w, h, 20, 21, 30, 31);
    }

    /**
     * Renders one segment of the plane.
     * to mimic the exact drawing behavior of stretching the image like WebGL does, we need to make sure
     * that the source area is at least 1 pixel in size, otherwise nothing gets drawn when a slice size of 0 is used.
     *
     * @private
     * @param {CanvasRenderingContext2D} context - The context to draw with.
     * @param {CanvasImageSource} textureSource - The source to draw.
     * @param {number} w - width of the texture
     * @param {number} h - height of the texture
     * @param {number} x1 - x index 1
     * @param {number} y1 - y index 1
     * @param {number} x2 - x index 2
     * @param {number} y2 - y index 2
     */
    drawSegment(context, textureSource, w, h, x1, y1, x2, y2)
    {
        // otherwise you get weird results when using slices of that are 0 wide or high.
        const uvs = this.uvs;
        const vertices = this.vertices;

        let sw = (uvs[x2] - uvs[x1]) * w;
        let sh = (uvs[y2] - uvs[y1]) * h;
        let dw = vertices[x2] - vertices[x1];
        let dh = vertices[y2] - vertices[y1];

        // make sure the source is at least 1 pixel wide and high, otherwise nothing will be drawn.
        if (sw < 1)
        {
            sw = 1;
        }

        if (sh < 1)
        {
            sh = 1;
        }

        // make sure destination is at least 1 pixel wide and high, otherwise you get
        // lines when rendering close to original size.
        if (dw < 1)
        {
            dw = 1;
        }

        if (dh < 1)
        {
            dh = 1;
        }

        context.drawImage(textureSource, uvs[x1] * w, uvs[y1] * h, sw, sh, vertices[x1], vertices[y1], dw, dh);
    }
}
<|endoftext|>


<commit_before>var bitcoin = Npm.require("bitcoin");
var ERR_CODE_WALLET_LOCKED = -13;

// A wrapper for commands that require an unlocked wallet.
var BitcoinClient = bitcoin.Client;
BitcoinClient.prototype.withUnlockedWallet = function (command, callback) {
    var that = this;
    // Try once, and if we get a failure due to an encrypted wallet,
    // we'll try again after unlocking the wallet.
    command(function () {
        if (arguments[0] && arguments[0].code == ERR_CODE_WALLET_LOCKED) {
            // Unlock the wallet and try again.
            that.walletPassphrase(
                Bitcoin.Settings.walletPassphrase,
                Bitcoin.Settings.walletLockInterval,
                (function () { command(callback); }));
        } else if (callback) {
            // If the call succeeded, pass on the results to its callback.
            callback.apply(callback, arguments);
        }
    });
};

// Override the default sendToAddress function
BitcoinClient.prototype._sendToAddress  = BitcoinClient.prototype.sendToAddress;
BitcoinClient.prototype.sendToAddress = function(address, amount, callback) {
    var that = this;
    this.withUnlockedWallet(function (_callback) {
       that._sendToAddress(address, amount, _callback);
    }, callback);
}

Bitcoin.Client = new BitcoinClient(Bitcoin.Settings.client);
<commit_msg>Cleaned up the encrypted wallet handling code a little by creating a generic decorator that can be applied to any of the bitcoin.Client functions which tries to run the overridden function, and then re-runs it after unlocking the wallet if the first attempt fails due to a 'locked wallet' error.<commit_after>var bitcoin = Npm.require("bitcoin");
var ERR_CODE_WALLET_LOCKED = -13;

// A decorator for bitcoin.Client commands that require an unlocked wallet.
// Calling a bitcoin.Client command that has been wrapped in this decorator
// results in the command being called once and then re-called after unlocking
// the wallet if the command fails due to the wallet being locked.
Bitcoin.withUnlockedWallet = function (command) {
    return function () {
        var that = this;
        var args = arguments;
        var tail = args[args.length-1];
        var callback = (_.isFunction(tail) ? tail : undefined);

        // Takes a function that takes a modified callback and passes it
        // on to a Bitcoin client command. The modified callback will
        // retry the original command with the original callback after
        // unlocking the wallet with the wallet passphrase defined in the
        // settings file.
        // I'm defining this function here because it doesn't make any
        // sense to expose it. Would be awesome if we could refactor all
        // this to make it simpler sometime, but this works for now.
        var retryIfLocked = function (command) {
            // Try once, and if we get a failure due to an encrypted wallet,
            // we'll try again after unlocking the wallet.
            command(function () {
                if (arguments[0] && arguments[0].code == ERR_CODE_WALLET_LOCKED) {
                    // Unlock the wallet and try again.
                    that.walletPassphrase(
                        Bitcoin.Settings.walletPassphrase,
                        Bitcoin.Settings.walletLockInterval,
                        (function () { command(callback); }));
                } else if (callback) {
                    // If the call succeeded, pass on the results to its callback.
                    callback.apply(callback, arguments);
                }
            });
        };

        // Here we are just creating a function that overrides whatever the
        // original callback was (if there was one) with the modified callback
        // from retryIfLocked. If there was no callback originally defined,
        // it just adds the modified callback onto the end of the arguments
        // to the original command.
        retryIfLocked(function (_callback) {
            if (_.isFunction(args[args.length-1])) {
                args[args.length-1] = _callback;
            } else {
                // args is an object masquerading as an array,
                // so we have to increment its length property
                // if we're adding a callback.
                args[args.length] = _callback;
                args.length++;
            }
            command.apply(that, args);
        });
    };
}

// Creating a copy of the bitcoin.Client class so we can
// override functions without altering the original class.
var BitcoinClient = bitcoin.Client;

// Decorate all functions that require an unlocked wallet,
// save for dumpprivkeys, importprivkey, keypoolrefill,
// signmessage, and signrawtransaction (since we should
// NEVER need to run those from within the web app).
BitcoinClient.prototype.sendToAddress = Bitcoin.withUnlockedWallet(BitcoinClient.prototype.sendToAddress);
BitcoinClient.prototype.sendFrom = Bitcoin.withUnlockedWallet(BitcoinClient.prototype.sendFrom);
BitcoinClient.prototype.sendMany = Bitcoin.withUnlockedWallet(BitcoinClient.prototype.sendMany);

// And finally create an instance of our modified class!
Bitcoin.Client = new BitcoinClient(Bitcoin.Settings.client);
<|endoftext|>


<commit_before>function createWorkspace(path, collectionId, menu, stopEventListener) {
  var safePath = '';
  $("#working-on").on('click', function () {}); // add event listener to mainNav

  if(stopEventListener) {
    document.getElementById('iframe').onload = function () {
      var browserLocation = document.getElementById('iframe').contentWindow.location.href;
      $('.browser-location').val(browserLocation);
      var iframeEvent = document.getElementById('iframe').contentWindow;
          iframeEvent.removeEventListener('click', Florence.Handler, true);
    };
    return false;
  } else {
    var currentPath = '';
    if (path) {
      currentPath = path;
      safePath = checkPathSlashes(currentPath);
    }

    Florence.globalVars.pagePath = safePath;

    Florence.refreshAdminMenu();

    var workSpace = templates.workSpace(Florence.tredegarBaseUrl + safePath);
     $('.section').html(workSpace);

    document.getElementById('iframe').onload = function () {
      $('.browser-location').val(Florence.tredegarBaseUrl + Florence.globalVars.pagePath);
      var iframeEvent = document.getElementById('iframe').contentWindow;
      iframeEvent.addEventListener('click', Florence.Handler, true);

      if (Florence.globalVars.welsh !== true) {
        $('#nav--workspace__welsh').empty().append('Language: English');
      } else {
        $('#nav--workspace__welsh').empty().append('Language: Welsh');
      }

      //click handlers
      $('.nav--workspace > li').click(function () {
        menu = '';
        if (Florence.Editor.isDirty) {
          var result = confirm("You have unsaved changes. Are you sure you want to continue");
          if (result === true) {
            Florence.Editor.isDirty = false;
            processMenuClick(this);
          } else {
            return false;
          }
        } else {
          processMenuClick(this);
        }
      });

      function processMenuClick(clicked) {
        var menuItem = $(clicked);

        $('.nav--workspace li').removeClass('selected');
        menuItem.addClass('selected');

        if (menuItem.is('#browse')) {
          loadBrowseScreen(collectionId, 'click');
        } else if (menuItem.is('#create')) {
          loadCreateScreen(collectionId);
        } else if (menuItem.is('#edit')) {
          loadPageDataIntoEditor(Florence.globalVars.pagePath, collectionId);
        } else {
          loadBrowseScreen(collectionId);
        }
      }

      $('#nav--workspace__welsh').on('click', function () {
        Florence.globalVars.welsh = Florence.globalVars.welsh === false ? true : false;
        console.log(Florence.globalVars.welsh);
        createWorkspace(Florence.globalVars.pagePath, collectionId, 'browse');
      });

      $('.workspace-menu').on('click', '.btn-browse-create', function () {
        var dest = $('.tree-nav-holder ul').find('.selected').attr('data-url');
        viewWorkspace(dest, Florence.collection.id, 'create');
      });

      $('.workspace-menu').on('click', '.btn-browse-edit', function () {
        var dest = $('.tree-nav-holder ul').find('.selected').attr('data-url');
        viewWorkspace(dest, Florence.collection.id, 'edit');
      });

      viewWorkspace(Florence.globalVars.pagePath, collectionId, menu);
    }
  }
}

<commit_msg>Add welsh functionality<commit_after>function createWorkspace(path, collectionId, menu, stopEventListener) {
  var safePath = '';
  $("#working-on").on('click', function () {}); // add event listener to mainNav

  if(stopEventListener) {
    document.getElementById('iframe').onload = function () {
      var browserLocation = document.getElementById('iframe').contentWindow.location.href;
      $('.browser-location').val(browserLocation);
      var iframeEvent = document.getElementById('iframe').contentWindow;
          iframeEvent.removeEventListener('click', Florence.Handler, true);
    };
    return false;
  } else {
    var currentPath = '';
    if (path) {
      currentPath = path;
      safePath = checkPathSlashes(currentPath);
    }

    Florence.globalVars.pagePath = safePath;
    if (Florence.globalVars.welsh !== true) {
      Florence.tredegarBaseUrl = window.location.origin;
    } else {
      Florence.tredegarBaseUrl = window.location.origin + '/cy';
    }
    Florence.refreshAdminMenu();

    var workSpace = templates.workSpace(Florence.tredegarBaseUrl + safePath);
     $('.section').html(workSpace);

    document.getElementById('iframe').onload = function () {
      $('.browser-location').val(Florence.tredegarBaseUrl + Florence.globalVars.pagePath);
      var iframeEvent = document.getElementById('iframe').contentWindow;
      iframeEvent.addEventListener('click', Florence.Handler, true);

      if (Florence.globalVars.welsh !== true) {
        $('#nav--workspace__welsh').empty().append('Language: English');
      } else {
        $('#nav--workspace__welsh').empty().append('Language: Welsh');
      }

      //click handlers
      $('.nav--workspace > li').click(function () {
        menu = '';
        if (Florence.Editor.isDirty) {
          var result = confirm("You have unsaved changes. Are you sure you want to continue");
          if (result === true) {
            Florence.Editor.isDirty = false;
            processMenuClick(this);
          } else {
            return false;
          }
        } else {
          processMenuClick(this);
        }
      });

      function processMenuClick(clicked) {
        var menuItem = $(clicked);

        $('.nav--workspace li').removeClass('selected');
        menuItem.addClass('selected');

        if (menuItem.is('#browse')) {
          loadBrowseScreen(collectionId, 'click');
        } else if (menuItem.is('#create')) {
          loadCreateScreen(collectionId);
        } else if (menuItem.is('#edit')) {
          loadPageDataIntoEditor(Florence.globalVars.pagePath, collectionId);
        } else {
          loadBrowseScreen(collectionId);
        }
      }

      $('#nav--workspace__welsh').on('click', function () {
        Florence.globalVars.welsh = Florence.globalVars.welsh === false ? true : false;
        console.log(Florence.globalVars.welsh);
        createWorkspace(Florence.globalVars.pagePath, collectionId, 'browse');
      });

      $('.workspace-menu').on('click', '.btn-browse-create', function () {
        var dest = $('.tree-nav-holder ul').find('.selected').attr('data-url');
        viewWorkspace(dest, Florence.collection.id, 'create');
      });

      $('.workspace-menu').on('click', '.btn-browse-edit', function () {
        var dest = $('.tree-nav-holder ul').find('.selected').attr('data-url');
        viewWorkspace(dest, Florence.collection.id, 'edit');
      });

      viewWorkspace(Florence.globalVars.pagePath, collectionId, menu);
    }
  }
}

<|endoftext|>


<commit_before>/**
 * Workflow controller for showing the available workflow templates and creating workflow instances from them
 * @author jjwyse
 */
var WorkflowController = BaseController.extend({
    _notifications: null,
    _elementsService: null,
    _cloudElementsUtils: null,
    _application: null,
    _workflow: null,
    _workflowInstance: null,
    _maskLoader: null,
    _picker: null,

    init: function($scope, CloudElementsUtils, Application, Workflow, WorkflowInstance, Notifications, ElementsService, MaskLoader, Picker, $window, $location, $interval, $filter, $route, $mdDialog) {
        var me = this;

        me._notifications = Notifications;
        me._elementsService = ElementsService;
        me._maskLoader = MaskLoader;
        me._cloudElementsUtils = CloudElementsUtils;
        me._application = Application;
        me._workflow = Workflow;
        me._workflowInstance = WorkflowInstance;
        me._picker = Picker;
        me.$window = $window;
        me.$location = $location;
        me.$interval = $interval;
        me.$mdDialog = $mdDialog;
        me._super($scope);
    },

    defineScope: function() {
        var me = this;

        // make sure the user is authenticated
        if(me._application.isSecretsPresent() == false) {
            me.$location.path('/');
            return;
        }

        me.$scope.processtep = 'workflow';
        me.$scope.appName = me._application.getApplicationName();
        me.$scope.onSelect = me.onSelect.bind(this);
        me.$scope.cancel = me.cancel.bind(this);
        me.$scope.workflows = [];

        // load the workflow templates
        me._maskLoader.show(me.$scope, 'Loading workflow templates...');
        me._loadWorkflowData();
    },

    defineListeners: function() {
        var me = this;
        me._notifications.addEventListener(bulkloader.events.ERROR, me._handleError.bind(me), me.$scope.$id);
    },

    destroy: function() {
        var me = this;
        me._notifications.removeEventListener(bulkloader.events.ERROR, me._handleError.bind(me), me.$scope.$id);
    },

    cancel: function() {
        var me = this;
        me.$location.path('/mapper');
    },

    onSelect: function(workflowTemplate) {
        var me = this;
        // TODO - JJW check to see if the workflow instance already exists
        me._createWorkflowInstance(workflowTemplate);
    },

    _handleError: function(event, error) {
        var me = this;
        console.log('In error ' + me.$scope.$id);
        me._maskLoader.hide();

        var confirm = me.$mdDialog.alert()
            .title('Error')
            .content(error)
            .ok('OK');

        me.$mdDialog.show(confirm);
    },

    _createWorkflowInstance: function(workflowTemplate) {
        var me = this;
        me._workflowInstance.openCreateWorkflowInstance(workflowTemplate);
    },

    _loadWorkflowData: function() {
        var me = this;
        me._workflow.loadWorkflowTemplates().then(me._handleWorkflowTemplatesLoaded.bind(me));
    },

    _handleWorkflowTemplatesLoaded: function(workflowTemplates) {
        var me = this;
        me._maskLoader.hide();
        console.log("Loaded " + workflowTemplates.length + " workflow templates");

        if(me._application.configuration.workflows && me._application.configuration.workflows.length > 0) {
            // if there is a workflows section in the app configuration, then filter out any that are not specified there
            var filteredWorkflowTemplates = [];
            for(var i = 0; i < me._application.configuration.workflows.length; i++) {
                var workflowAppConfig = me._application.configuration.workflows[i];

                // look through each workflow template we loaded and we have a workflow template with the name in the workflow app config, then include it
                for(var j = 0; j < workflowTemplates.length; j++) {
                    var workflowTemplate = workflowTemplates[j];
                    if(workflowAppConfig.name === workflowTemplate.name) {

                        // go through each config on the workflow template, and set default values with our target and source element instances, if possible
                        if(workflowTemplate.configuration) {
                            for(var k = 0; k < workflowTemplate.configuration.length; k++) {
                                var workflowTemplateConfig = workflowTemplate.configuration[k];
                                if(workflowTemplateConfig.type === 'elementInstance') {
                                    var configKey = workflowTemplateConfig.key;
                                    var elementKey = configKey.substr(0, configKey.indexOf('.'));
                                    console.log("Looking for source or target instance with key: " + elementKey);

                                    workflowTemplateConfig.defaultValues = {};
                                    if(me._picker.selectedElementInstance.element.key === elementKey) {
                                        workflowTemplateConfig.defaultValue = me._picker.selectedElementInstance.id;
                                    } else if(me._picker.targetElementInstance.element.key === elementKey) {
                                        workflowTemplateConfig.defaultValue = me._picker.targetElementInstance.id;
                                    }
                                }
                            }
                        }
                        filteredWorkflowTemplates.push(workflowTemplate);
                    }
                }
            }
            me.$scope.workflows = filteredWorkflowTemplates;
        } else {
            // if we do NOT have any workflows defined in our app config, then just show all of the workflow templates
            me.$scope.workflows = workflowTemplates;
        }
    }
});

WorkflowController.$inject = ['$scope', 'CloudElementsUtils', 'Application', 'Workflow', 'WorkflowInstance', 'Notifications', 'ElementsService', 'MaskLoader', 'Picker', '$window', '$location', '$interval', '$filter', '$route', '$mdDialog'];

angular.module('bulkloaderApp')
    .controller('WorkflowController', WorkflowController);
<commit_msg>some minor re-factoring CE-1942<commit_after>/**
 * Workflow controller for showing the available workflow templates and creating workflow instances from them
 * @author jjwyse
 */
var WorkflowController = BaseController.extend({
    _notifications: null,
    _elementsService: null,
    _cloudElementsUtils: null,
    _application: null,
    _workflow: null,
    _workflowInstance: null,
    _maskLoader: null,
    _picker: null,

    init: function($scope, CloudElementsUtils, Application, Workflow, WorkflowInstance, Notifications, ElementsService, MaskLoader, Picker, $window, $location, $interval, $filter, $route, $mdDialog) {
        var me = this;

        me._notifications = Notifications;
        me._elementsService = ElementsService;
        me._maskLoader = MaskLoader;
        me._cloudElementsUtils = CloudElementsUtils;
        me._application = Application;
        me._workflow = Workflow;
        me._workflowInstance = WorkflowInstance;
        me._picker = Picker;
        me.$window = $window;
        me.$location = $location;
        me.$interval = $interval;
        me.$mdDialog = $mdDialog;
        me._super($scope);
    },

    defineScope: function() {
        var me = this;

        // make sure the user is authenticated
        if(me._application.isSecretsPresent() == false) {
            me.$location.path('/');
            return;
        }

        me.$scope.processtep = 'workflow';
        me.$scope.appName = me._application.getApplicationName();
        me.$scope.onSelect = me.onSelect.bind(this);
        me.$scope.cancel = me.cancel.bind(this);
        me.$scope.workflows = [];

        // load the workflow templates
        me._maskLoader.show(me.$scope, 'Loading workflow templates...');
        me._loadWorkflowData();
    },

    defineListeners: function() {
        var me = this;
        me._notifications.addEventListener(bulkloader.events.ERROR, me._handleError.bind(me), me.$scope.$id);
    },

    destroy: function() {
        var me = this;
        me._notifications.removeEventListener(bulkloader.events.ERROR, me._handleError.bind(me), me.$scope.$id);
    },

    cancel: function() {
        var me = this;
        me.$location.path('/mapper');
    },

    onSelect: function(workflowTemplate) {
        var me = this;
        // TODO - JJW check to see if the workflow instance already exists
        me._createWorkflowInstance(workflowTemplate);
    },

    _handleError: function(event, error) {
        var me = this;
        console.log('In error ' + me.$scope.$id);
        me._maskLoader.hide();

        var confirm = me.$mdDialog.alert()
            .title('Error')
            .content(error)
            .ok('OK');

        me.$mdDialog.show(confirm);
    },

    _createWorkflowInstance: function(workflowTemplate) {
        var me = this;
        me._workflowInstance.openCreateWorkflowInstance(workflowTemplate);
    },

    _loadWorkflowData: function() {
        var me = this;
        me._workflow.loadWorkflowTemplates().then(me._handleWorkflowTemplatesLoaded.bind(me));
    },

    _handleWorkflowTemplatesLoaded: function(workflowTemplates) {
        var me = this;
        me._maskLoader.hide();
        console.log("Loaded " + workflowTemplates.length + " workflow templates");

        if(me._application.configuration.workflows && me._application.configuration.workflows.length > 0) {
            // if there is a workflows section in the app configuration, then filter out any that are not specified there
            me.$scope.workflows = me._filterWorkflowTemplates(workflowTemplates);
        } else {
            // if we do NOT have any workflows defined in our app config, then just show all of the workflow templates
            me.$scope.workflows = workflowTemplates;
        }
    },

    _filterWorkflowTemplates: function(workflowTemplates) {
        var me = this;

        var filteredWorkflowTemplates = [];
        for(var i = 0; i < me._application.configuration.workflows.length; i++) {
            var workflowAppConfig = me._application.configuration.workflows[i];

            // look through each workflow template we loaded and we have a workflow template with the name in the workflow app config, then include it
            for(var j = 0; j < workflowTemplates.length; j++) {
                var workflowTemplate = workflowTemplates[j];
                if(workflowAppConfig.name === workflowTemplate.name) {

                    // go through each config on the workflow template, and set default values with our target and source element instances, if possible
                    if(workflowTemplate.configuration) {
                        for(var k = 0; k < workflowTemplate.configuration.length; k++) {
                            var workflowTemplateConfig = workflowTemplate.configuration[k];
                            if(workflowTemplateConfig.type === 'elementInstance') {
                                var configKey = workflowTemplateConfig.key;
                                var elementKey = configKey.substr(0, configKey.indexOf('.'));
                                console.log("Looking for source or target instance with key: " + elementKey);

                                workflowTemplateConfig.defaultValues = {};
                                if(me._picker.selectedElementInstance.element.key === elementKey) {
                                    workflowTemplateConfig.defaultValue = me._picker.selectedElementInstance.id;
                                } else if(me._picker.targetElementInstance.element.key === elementKey) {
                                    workflowTemplateConfig.defaultValue = me._picker.targetElementInstance.id;
                                }
                            }
                        }
                    }
                    filteredWorkflowTemplates.push(workflowTemplate);
                }
            }
        }
        return filteredWorkflowTemplates;
    }
});

WorkflowController.$inject = ['$scope', 'CloudElementsUtils', 'Application', 'Workflow', 'WorkflowInstance', 'Notifications', 'ElementsService', 'MaskLoader', 'Picker', '$window', '$location', '$interval', '$filter', '$route', '$mdDialog'];

angular.module('bulkloaderApp')
    .controller('WorkflowController', WorkflowController);
<|endoftext|>


<commit_before>/** @jsx React.DOM */

var Mousetrap = require('mousetrap'),
    getUrl = require('../lib/get-url.js'),
    $ = require('jquery');

// TODO @palcu: take the response and pass it somehow to level component
// TODO @palcu: DRY this fucking code

module.exports = {
  componentWillMount: function() {
    Mousetrap.bind('up', function(e) {
      e.preventDefault();
      $.get(getUrl('move', this.state.currentChar, 'left'), this._parseResponse);
    }.bind(this));
    Mousetrap.bind('down', function(e) {
      e.preventDefault();
      $.get(getUrl('move', this.state.currentChar, 'right'), this._parseResponse);
    }.bind(this));
    Mousetrap.bind('left', function(e) {
      e.preventDefault();
      $.get(getUrl('move', this.state.currentChar, 'up'), this._parseResponse);
    }.bind(this));
    Mousetrap.bind('right', function(e) {
      e.preventDefault();
      $.get(getUrl('move', this.state.currentChar, 'down'), this._parseResponse);
    }.bind(this));
  },

  _parseResponse: function(response) {
    if ($.type(response.success) === 'object' && 'movedto' in response.success) {
      var coords = response.success.movedto;
      console.log('moved to (' + coords.x + ', ' + coords.y + ')');
    } else {
      console.log(JSON.stringify(response.success));
    }
  }
};
<commit_msg>Rename method<commit_after>/** @jsx React.DOM */

var Mousetrap = require('mousetrap'),
    getUrl = require('../lib/get-url.js'),
    $ = require('jquery');

// TODO @palcu: take the response and pass it somehow to level component
// TODO @palcu: DRY this fucking code

module.exports = {
  componentWillMount: function() {
    Mousetrap.bind('up', function(e) {
      e.preventDefault();
      $.get(getUrl('move', this.state.currentChar, 'left'), this._parseMoveResponse);
    }.bind(this));
    Mousetrap.bind('down', function(e) {
      e.preventDefault();
      $.get(getUrl('move', this.state.currentChar, 'right'), this._parseMoveResponse);
    }.bind(this));
    Mousetrap.bind('left', function(e) {
      e.preventDefault();
      $.get(getUrl('move', this.state.currentChar, 'up'), this._parseMoveResponse);
    }.bind(this));
    Mousetrap.bind('right', function(e) {
      e.preventDefault();
      $.get(getUrl('move', this.state.currentChar, 'down'), this._parseMoveResponse);
    }.bind(this));
  },

  _parseMoveResponse: function(response) {
    if ($.type(response.success) === 'object' && 'movedto' in response.success) {
      var coords = response.success.movedto;
      console.log('moved to (' + coords.x + ', ' + coords.y + ')');
    } else {
      console.log(JSON.stringify(response.success));
    }
  }
};
<|endoftext|>