<commit_before>'use strict';

const fs = require('fs');
const path = require('path');

const moment = require('moment');

const stateUtils = require('../../../../../../lib/cluster/services/cluster/backends/stateUtils');
const { dateFormat } = require('../../../../../../lib/utils/date_utils');

const clusterStateFile = path.join(__dirname, 'files', 'sample-state.json');

describe('stateUtils', () => {
    let clusterState;

    beforeEach(() => {
        clusterState = JSON.parse(fs.readFileSync(clusterStateFile, 'utf-8'));
    });

    describe('->_iterateState', () => {
        it('can filter worker list and return cluster master node', () => {
            const workerList = stateUtils._iterateState(
                clusterState,
                worker => worker.assignment === 'cluster_master'
            );
            expect(workerList[0].node_id).toEqual('node1.lan');
        });
    });

    describe('->findAllSlicers', () => {
        it('returns the right number of slicers', () => {
            const workerList = stateUtils.findAllSlicers(clusterState);
            expect(workerList[0].assignment).toEqual('execution_controller');
        });
    });

    describe('->findWorkersByExecutionID', () => {
        it('returns the right nummber of workers', () => {
            const workerList = stateUtils.findWorkersByExecutionID(
                clusterState,
                '8d7669e1-d351-4a0d-b36e-b11da5ade26a'
            );
            expect(workerList.length).toEqual(8);
        });
    });

    describe('->findSlicersByExecutionID', () => {
        it('returns foo', () => {
            const workerList = stateUtils.findSlicersByExecutionID(
                clusterState,
                { '8d7669e1-d351-4a0d-b36e-b11da5ade26a': '8d7669e1-d351-4a0d-b36e-b11da5ade26a' }
            );
            expect(workerList[0].assignment).toEqual('execution_controller');
        });
    });

    describe('->newGetSlicerStats', () => {
        it('runs without exploding', async () => {
            let slicerStats;
            const context = { sysconfig: { teraslice: { action_timeout: 5 } } };
            const msgResponse = {
                node_id: 'node2.lan',
                job_id: 'a1af76a0-a2bd-457d-b14a-f11e5f04b9a1',
                ex_id: '8d7669e1-d351-4a0d-b36e-b11da5ade26a',
                payload: {
                    name: 'steve',
                    stats: {
                        workers_available: 0,
                        workers_active: 0,
                        workers_joined: 0,
                        workers_reconnected: 0,
                        workers_disconnected: 0,
                        failed: 0,
                        subslices: 0,
                        queued: 0,
                        slice_range_expansion: 0,
                        processed: 0,
                        slicers: 0,
                        subslice_by_key: 0,
                        started: moment().format(dateFormat)
                    }
                }
            };

            const messaging = {
                send: () => msgResponse
            };

            const getSlicerStats = stateUtils.newGetSlicerStats(clusterState, context, messaging);

            try {
                slicerStats = await getSlicerStats(
                    ['8d7669e1-d351-4a0d-b36e-b11da5ade26a'],
                    '8d7669e1-d351-4a0d-b36e-b11da5ade26a'
                );
            } catch (e) {
                console.log(e);
            }
            expect(slicerStats[0].ex_id).toEqual('8d7669e1-d351-4a0d-b36e-b11da5ade26a');
        });
    });
});
<commit_msg>Fix typos and lazy labels<commit_after>'use strict';

const fs = require('fs');
const path = require('path');

const moment = require('moment');

const stateUtils = require('../../../../../../lib/cluster/services/cluster/backends/stateUtils');
const { dateFormat } = require('../../../../../../lib/utils/date_utils');

const clusterStateFile = path.join(__dirname, 'files', 'sample-state.json');

describe('stateUtils', () => {
    let clusterState;

    beforeEach(() => {
        clusterState = JSON.parse(fs.readFileSync(clusterStateFile, 'utf-8'));
    });

    describe('->_iterateState', () => {
        it('can filter worker list and return cluster master node', () => {
            const workerList = stateUtils._iterateState(
                clusterState,
                worker => worker.assignment === 'cluster_master'
            );
            expect(workerList[0].node_id).toEqual('node1.lan');
        });
    });

    describe('->findAllSlicers', () => {
        it('returns the right number of slicers', () => {
            const workerList = stateUtils.findAllSlicers(clusterState);
            expect(workerList[0].assignment).toEqual('execution_controller');
        });
    });

    describe('->findWorkersByExecutionID', () => {
        it('returns the right number of workers', () => {
            const workerList = stateUtils.findWorkersByExecutionID(
                clusterState,
                '8d7669e1-d351-4a0d-b36e-b11da5ade26a'
            );
            expect(workerList.length).toEqual(8);
        });
    });

    describe('->findSlicersByExecutionID', () => {
        it('returns execution_controller worker', () => {
            const workerList = stateUtils.findSlicersByExecutionID(
                clusterState,
                { '8d7669e1-d351-4a0d-b36e-b11da5ade26a': '8d7669e1-d351-4a0d-b36e-b11da5ade26a' }
            );
            expect(workerList[0].assignment).toEqual('execution_controller');
        });
    });

    describe('->newGetSlicerStats', () => {
        it('returns valid slicerStats', async () => {
            let slicerStats;
            const context = { sysconfig: { teraslice: { action_timeout: 5 } } };
            const msgResponse = {
                node_id: 'node2.lan',
                job_id: 'a1af76a0-a2bd-457d-b14a-f11e5f04b9a1',
                ex_id: '8d7669e1-d351-4a0d-b36e-b11da5ade26a',
                payload: {
                    name: 'steve',
                    stats: {
                        workers_available: 0,
                        workers_active: 0,
                        workers_joined: 0,
                        workers_reconnected: 0,
                        workers_disconnected: 0,
                        failed: 0,
                        subslices: 0,
                        queued: 0,
                        slice_range_expansion: 0,
                        processed: 0,
                        slicers: 0,
                        subslice_by_key: 0,
                        started: moment().format(dateFormat)
                    }
                }
            };

            const messaging = {
                send: () => msgResponse
            };

            const getSlicerStats = stateUtils.newGetSlicerStats(clusterState, context, messaging);

            try {
                slicerStats = await getSlicerStats(
                    ['8d7669e1-d351-4a0d-b36e-b11da5ade26a'],
                    '8d7669e1-d351-4a0d-b36e-b11da5ade26a'
                );
            } catch (e) {
                console.log(e);
            }
            expect(slicerStats[0].ex_id).toEqual('8d7669e1-d351-4a0d-b36e-b11da5ade26a');
        });
    });
});
<|endoftext|>


<commit_before>/*jshint esversion: 6 */

import React, {PropTypes} from 'react';
import {
  Grid,
  Row,
  Col,
  Jumbotron,
  Button,
  Table,
  ListGroup,
  Popover,
  OverlayTrigger,
  Glyphicon
} from 'react-bootstrap';
import Actions from './deduplicator-actions';
import _ from "underscore";
var AssignExistingCapabilityDialog = require('./assign-existing-capability');
import {getStyleForType} from './../editor/component-styles';
import MapLink from './maplink.js';
var UsageInfo = require('./usage-info');

var acceptorStyle = {
  width: "100%",
  height: "35px",
  lineHeight: "35px",
  textAlign: "center",
  verticalAlign: "middle",
  margin: "1px",
  color: "white"
};

var capabilityStyle = {
  textAlign: "center"
};

var greyLaneStyle = {
  position: 'absolute',
  left: 1,
  right: 1,
  top: 5,
  height: 20,
  margin: "1px",
  backgroundColor: '#f8f8f8'
};
export default class CapabilitiesView extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    this.state.assignExistingCapabilityDialog =  {
        open: false
    };
  }

  handleDragOver(e) {
    if (e.preventDefault) {
      e.preventDefault();
    }
    e.dataTransfer.dropEffect = 'copy';
  }

  handleDropExistingCapability(capability,  e) {
    var item = JSON.parse(e.dataTransfer.getData('json'));
    var newState = {
      assignExistingCapabilityDialog: {
        open: true,
        nodeBeingAssigned: item,
        capability: capability,
      }
    };
    this.setState(newState);
  }

  handleDropNewCapability(cat, e) {
    var item = JSON.parse(e.dataTransfer.getData('json'));
    Actions.createNewCapability(this.props.workspace._id, cat, item._id);
  }

  cancelDialog() {
    var newState = {
      assignExistingCapabilityDialog : {
        open:false
      }
    };
    this.setState(newState);
  }

  submitAssignDialog(nodeBeingAssignedID) {
    var capability = this.state.assignExistingCapabilityDialog.capability;
    Actions.assignNodeToCapability(this.props.workspace._id, capability._id, nodeBeingAssignedID);
    this.setState({
        assignExistingCapabilityDialog: {
          open: false
        }
      });
  }

  submitAssignAlias(nodeBeingAssignedID, aliasID) {
    Actions.assignNodeToAlias(this.props.workspace._id, aliasID, nodeBeingAssignedID);
    this.setState({
        assignExistingCapabilityDialog: {
          open: false
        }
      });
  }

  deleteCapability(capability, e) {
    if (e.preventDefault) {
      e.preventDefault();
    }
    e.stopPropagation();
    Actions.deleteCapability(this.props.workspace._id, capability._id);
  }

  renderSingleNode(node){
    if(!node){
      console.error('this node should not be null');
      return null;
    }
    var style = getStyleForType(node.type);
    style.left = node.x * 100 + '%';
    style.position = 'absolute';
    style.top = "10px";
    var workspaceID = this.props.workspace._id;
    var _popover = <Popover id={node._id} title="Component details">
            <UsageInfo node={node} workspaceID={workspaceID}/>
        </Popover>;
    return (
      <OverlayTrigger trigger="click" placement="bottom" overlay={_popover}>

      </OverlayTrigger>);
  }

  render() {
    var _acceptorStyleToSet = _.clone(acceptorStyle);
    var _capabilityStyleToSet = _.clone(capabilityStyle);
    var _greyLaneStyleToSet = _.clone(greyLaneStyle);
    var greyLaneText = null;
    var _this = this;
    if (this.props.dragStarted) {
      _acceptorStyleToSet = _.extend(_acceptorStyleToSet, {
        borderColor: "#00789b",
        boxShadow: "0 0 10px #00789b",
        border: '1px solid #00789b',
        color: 'black'
      });
      _greyLaneStyleToSet = _.extend(_greyLaneStyleToSet, {
        borderColor: "#00789b",
        boxShadow: "0 0 5px #00789b",
        border: '1px solid #00789b',
        zIndex:20
      });
      greyLaneText = "Drop here if the component does the same what this component";
    }

    var categories = [];
    if(!this.props.categories || !this.props.categories.capabilityCategories){
      return
 wait...

    }
    this.props.categories.capabilityCategories.forEach(function(category){

      var dragOver = _this.handleDragOver.bind(_this);
      var onDrop = _this.handleDropNewCapability.bind(_this, category._id);

      // first the title
      categories.push(
        <Row className="show-grid" key={category._id}>
          <Col xs={3}>

{category.name}

          </Col>
          <Col xs={9}>

Drop here if nothing in this category does the same job

          </Col>
        </Row>
      );

      category.capabilities.forEach(function(capability){
        console.log('rendering',capability );
        var _itemsToDisplay = [];
        capability.aliases.forEach(function(alias){
          console.log('rendering',alias );
            _itemsToDisplay.push(
              _this.renderSingleNode(alias.nodes[0])
            );
        });
        var name = capability.aliases[0].nodes[0].name;
        categories.push(
          <Row className="show-grid" key={capability._id}>
                      <Col xs={3}>

{name}

                      </Col>
                      <Col xs={8}>

{greyLaneText}

                          {_itemsToDisplay}

                      </Col>
                      <Col xs={1}>
                        <Button bsSize="xsmall" onClick={_this.deleteCapability.bind(_this, capability)}><Glyphicon glyph="remove"></Glyphicon></Button>
                      </Col>
                    </Row>
        );
      });

    });
    if(categories.length === 0){
      categories.push(<Row className="show-grid">
                  <Col xs={12}>
                    Nothing found
                  </Col>
                </Row>);
    }

    var assignDialogOpen = this.state.assignExistingCapabilityDialog.open;
    var capability = this.state.assignExistingCapabilityDialog.capability;
    var nodeBeingAssigned = this.state.assignExistingCapabilityDialog.nodeBeingAssigned;

    var _this;
    return (
      <Grid fluid={true}>
        {categories}
        <AssignExistingCapabilityDialog
          open={assignDialogOpen}
          nodeBeingAssigned={nodeBeingAssigned}
          capability={capability}
          cancel={_this.cancelDialog.bind(_this)}
          submitAssignDialog={_this.submitAssignDialog.bind(_this)}
          submitAssignAlias={_this.submitAssignAlias.bind(_this)}
          />
      </Grid>
    );
  }
}
<commit_msg>committed file<commit_after>/*jshint esversion: 6 */

import React, {PropTypes} from 'react';
import {
  Grid,
  Row,
  Col,
  Jumbotron,
  Button,
  Table,
  ListGroup,
  Popover,
  OverlayTrigger,
  Glyphicon
} from 'react-bootstrap';
import Actions from './deduplicator-actions';
import _ from "underscore";
var AssignExistingCapabilityDialog = require('./assign-existing-capability');
import {getStyleForType} from './../editor/component-styles';
import MapLink from './maplink.js';
var UsageInfo = require('./usage-info');

var acceptorStyle = {
  width: "100%",
  height: "35px",
  lineHeight: "35px",
  textAlign: "center",
  verticalAlign: "middle",
  margin: "1px",
  color: "white"
};

var capabilityStyle = {
  textAlign: "center"
};

var greyLaneStyle = {
  position: 'absolute',
  left: 1,
  right: 1,
  top: 5,
  height: 20,
  margin: "1px",
  backgroundColor: '#f8f8f8'
};
export default class CapabilitiesView extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    this.state.assignExistingCapabilityDialog =  {
        open: false
    };
  }

  handleDragOver(e) {
    if (e.preventDefault) {
      e.preventDefault();
    }
    e.dataTransfer.dropEffect = 'copy';
  }

  handleDropExistingCapability(capability,  e) {
    var item = JSON.parse(e.dataTransfer.getData('json'));
    var newState = {
      assignExistingCapabilityDialog: {
        open: true,
        nodeBeingAssigned: item,
        capability: capability,
      }
    };
    this.setState(newState);
  }

  handleDropNewCapability(cat, e) {
    var item = JSON.parse(e.dataTransfer.getData('json'));
    Actions.createNewCapability(this.props.workspace._id, cat, item._id);
  }

  cancelDialog() {
    var newState = {
      assignExistingCapabilityDialog : {
        open:false
      }
    };
    this.setState(newState);
  }

  submitAssignDialog(nodeBeingAssignedID) {
    var capability = this.state.assignExistingCapabilityDialog.capability;
    Actions.assignNodeToCapability(this.props.workspace._id, capability._id, nodeBeingAssignedID);
    this.setState({
        assignExistingCapabilityDialog: {
          open: false
        }
      });
  }

  submitAssignAlias(nodeBeingAssignedID, aliasID) {
    Actions.assignNodeToAlias(this.props.workspace._id, aliasID, nodeBeingAssignedID);
    this.setState({
        assignExistingCapabilityDialog: {
          open: false
        }
      });
  }

  deleteCapability(capability, e) {
    if (e.preventDefault) {
      e.preventDefault();
    }
    e.stopPropagation();
    Actions.deleteCapability(this.props.workspace._id, capability._id);
  }

  renderSingleNode(node){
    if(!node){
      console.error('this node should not be null');
      return null;
    }
    var style = getStyleForType(node.type);
    style.left = node.x * 100 + '%';
    style.position = 'absolute';
    style.top = "10px";
    var workspaceID = this.props.workspace._id;
    var _popover = <Popover id={node._id} title="Component details">
            <UsageInfo node={node} workspaceID={workspaceID} emptyInfo={true} alternativeNames={false}/>
        </Popover>;
    return (
      <OverlayTrigger trigger="click" placement="bottom" overlay={_popover}>

      </OverlayTrigger>);
  }

  render() {
    var _acceptorStyleToSet = _.clone(acceptorStyle);
    var _capabilityStyleToSet = _.clone(capabilityStyle);
    var _greyLaneStyleToSet = _.clone(greyLaneStyle);
    var greyLaneText = null;
    var _this = this;
    if (this.props.dragStarted) {
      _acceptorStyleToSet = _.extend(_acceptorStyleToSet, {
        borderColor: "#00789b",
        boxShadow: "0 0 10px #00789b",
        border: '1px solid #00789b',
        color: 'black'
      });
      _greyLaneStyleToSet = _.extend(_greyLaneStyleToSet, {
        borderColor: "#00789b",
        boxShadow: "0 0 5px #00789b",
        border: '1px solid #00789b',
        zIndex:20
      });
      greyLaneText = "Drop here if the component does the same what this component";
    }

    var categories = [];
    if(!this.props.categories || !this.props.categories.capabilityCategories){
      return
 wait...

    }
    this.props.categories.capabilityCategories.forEach(function(category){

      var dragOver = _this.handleDragOver.bind(_this);
      var onDrop = _this.handleDropNewCapability.bind(_this, category._id);

      // first the title
      categories.push(
        <Row className="show-grid" key={category._id}>
          <Col xs={3}>

{category.name}

          </Col>
          <Col xs={9}>

Drop here if nothing in this category does the same job

          </Col>
        </Row>
      );

      category.capabilities.forEach(function(capability){
        console.log('rendering capability',capability );
        var _itemsToDisplay = [];
        capability.aliases.forEach(function(alias){
          console.log('rendering alias',alias );
            _itemsToDisplay.push(
              _this.renderSingleNode(alias.nodes[0])
            );
        });
        var name = capability.aliases[0].nodes[0].name;
        categories.push(
          <Row className="show-grid" key={capability._id}>
                      <Col xs={3}>

{name}

                      </Col>
                      <Col xs={8}>

{greyLaneText}

                          {_itemsToDisplay}

                      </Col>
                      <Col xs={1}>
                        <Button bsSize="xsmall" onClick={_this.deleteCapability.bind(_this, capability)}><Glyphicon glyph="remove"></Glyphicon></Button>
                      </Col>
                    </Row>
        );
      });

    });
    if(categories.length === 0){
      categories.push(<Row className="show-grid">
                  <Col xs={12}>
                    Nothing found
                  </Col>
                </Row>);
    }

    var assignDialogOpen = this.state.assignExistingCapabilityDialog.open;
    var capability = this.state.assignExistingCapabilityDialog.capability;
    var nodeBeingAssigned = this.state.assignExistingCapabilityDialog.nodeBeingAssigned;

    var _this;
    return (
      <Grid fluid={true}>
        {categories}
        <AssignExistingCapabilityDialog
          open={assignDialogOpen}
          nodeBeingAssigned={nodeBeingAssigned}
          capability={capability}
          cancel={_this.cancelDialog.bind(_this)}
          submitAssignDialog={_this.submitAssignDialog.bind(_this)}
          submitAssignAlias={_this.submitAssignAlias.bind(_this)}
          />
      </Grid>
    );
  }
}
<|endoftext|>


<commit_before>'use strict';

angular.module('facecards', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  var tpl = window.jQuery('.st-view-facecards').html();
  window.jQuery('.st-view-facecards').html(' ');
  $routeProvider.when('/play', {
    //templateUrl: 'views/facecards/facecards.html'
    template: tpl
  , controller: 'FacecardsController as FC'
  });
}])

.controller('FacecardsController', [
  '$scope'
, '$timeout'
, '$location'
, 'MyAppSession'
, 'StProgress'
, 'LdsIoApi'
, function ($scope, $timeout, $location, MyAppSession, StProgress, LdsIoApi) {
  var FC = this;
  var scope = this;
  var shuffle = window.knuthShuffle;
  var mine;
  var cardCache = {};

  MyAppSession.onLogout($scope, function () {
  console.log('logout');
    $location.url('/');
  });

  MyAppSession.requireSession().then(function (session) {
    // TODO this should be a directive and I realize that.
    // I'm just to lazy to do it right, right now
    $location.url('/play');

    FC.flashMessage = "Downloading Ward / Stake info...";
    StProgress.start(4 * 1000);
    return LdsIoApi.profile(session).then(function (profile) {
      FC.flashMessage = "Downloading Ward directory...";
      StProgress.restart(6 * 1000, { style: "success" });
      return LdsIoApi.ward(session, profile.home_stake_id, profile.home_ward_id).then(function (/*ward*/) {
        FC.flashMessage = "Downloading Photo directory...";
        StProgress.restart(10 * 1000, { style: "warning" });
        return LdsIoApi.wardPhotos(session, session.home_stake_id, profile.home_ward_id).then(function (/*photos*/) {
          FC.flashMessage = "";
          StProgress.stop(350);
          startGame();
          // update roster with photourls
        });
      });
    }, function (err) {
      console.error("[ERROR] when downloading directories");
      console.log(err);
      FC.flashMessage = err.message || "Unknown error while downloading ...";
      StProgress.stop(350);
    });
  }, function (e) {
    console.error('[ERROR] require session');
    console.log(e);
    $location().url('/');
  });

  //
  // GAME
  //
  //
  function startGame() {
    // TODO select stake and ward
    console.info("Game Start");
    MyAppSession.requireSession().then(function (session) {
      return LdsIoApi.profile(session).then(function (profile) {
        return LdsIoApi.ward(session, profile.home_stake_id, profile.home_ward_id).then(function (ward) {
          return LdsIoApi.wardPhotos(session, profile.home_stake_id, profile.home_ward_id).then(function (photos) {
            var memberMap = {};
            var photoMemberMap = {};
            var photoHomeMap = {};

            // TODO app_scoped_ids
            photos.members.forEach(function (m) {
              photoMemberMap[m.app_scoped_id || m.id] = m;
            });
            photos.families.forEach(function (h) {
              photoHomeMap[h.app_scoped_id || h.id] = h;
            });

            ward.members.forEach(function (m) {
              var id = m.app_scoped_id || m.id;
              var photo = photoMemberMap[id] || photoHomeMap[id] || null;

              memberMap[id] = m;

              // TODO check updated but deleted and flag in server api
              if (photo && photo.updated_at) {
                m.photoUrl = LdsIoApi.photoUrl(session, photo, 'medium', photo.type);
              }
            });

            console.log(ward.members[0]);
            return ward.members.filter(function (m) {
              return m.photoUrl;
            }).map(function (m) {
              return {
                id: m.app_scoped_id || m.id
              , photo: m.photoUrl
              , name: m.name + ' ' + m.surnames.join(' ')
              , callings: m.callings
              , gender: LdsIoApi.guessGender(m)
              , attempts: 0
              };
            });
          });
        }).then(function (cardables) {
          scope.goodCards = cardables.slice(0);
          scope.allCards = cardables.slice(0);
          scope.playAgain();
        });
      });
    });
  }

  scope.skip = function () {
    // TODO mark as missing picture
    scope.validCardCount -= 1;
    scope.goodCards.shift();
    nextCard();
  };
  scope.choose = function (i) {
    if (scope.card !== scope.choices[i]) {
      scope.misses[scope.card.id] = true;
      scope.missCount = Object.keys(scope.misses).length;
      //TODO scope.misses[scope.choices[i].id] = true;
      scope.choices[i].disabled = true;
      return;
    }

    if (!scope.misses[scope.card.id]) {
      scope.hits[scope.card.id] = true;
      scope.hitCount += 1;
    }

    // TODO mark against pic and wrong choice
    scope.choices.forEach(function (choice) {
      delete choice.disabled;
    });

    scope.goodCards.shift();
    nextCard();
  };
  /*
  scope.makeGuess = function (item, model, str) {
    if (str) {
      scope.guess = str;
    }

    scope.guess = scope.guess || '';

    console.log('guess', scope.guess);

    if (scope.guess.toLowerCase() === scope.card.name.toLowerCase()) {
      scope.goodCards.shift();
      nextCard();
    } else {
      scope.hint = scope.card.name.substr(0, scope.hint.length + 1);
      scope.guess = scope.hint;
    }
  };
  */

  scope.sortByName = function () {
    // TODO sort by first name
    return 0.5 - Math.random();
  };

  function gameOver() {
    // do something
    scope.finished = true;
    // https://www.facebook.com/sharer/sharer.php?s=100&p[url]=http://www.otlcampaign.org/blog/2013/02/04/inconvenient-truth-education-reform&p[images][0]=http://www.otlcampaign.org/sites/default/files/journey-for-justice-mlk-memorial.jpg&p[title]=The+Inconvenient+Truth+of+Education+%27Reform%27!&p[summary]=Recent+events+have+revealed+how+market-driven+education+policies,+deceivingly+labeled+as+%22reform,%22+are+revealing+their+truly+destructive+effects+on+the+streets+and+in+the+corridors+of+government:
    scope.xfbShare = "https://www.facebook.com/sharer/sharer.php"
      + '?s=100'
      + '&p[title]=How+well+do+you+know+your+wardies?'
      + '&p[url]=http://facecards.org'
      + '&p[images][0]=http://facecards.org/images/logo-256px.png'
      + '&p[summary]=Fight+the+awkward.+Learn+the+names+of+your+ward+members.'
      ;
    return;
  }

  function preloadImage(card) {
    console.log('preload', card);
    var id = card.id;
    if (cardCache[id]) {
      return;
    }
    cardCache[id] = { img: new Image() };
    cardCache[id].img.addEventListener('load', function () {
      cardCache[id].loaded = true;
    });
    cardCache[id].img.addEventListener('error', function () {
      cardCache[id].error = true;
    });
    cardCache[id].img.src = card.photo;
  }

  function nextCard() {
    var card = scope.goodCards[0];
    var id = scope.goodCards[0].id;

    console.log('nextcard', card);

    // always be preloading the next 6 cards
    scope.goodCards.slice(0, 6).forEach(function (card) {
      preloadImage(card);
    });

    if (cardCache[id].loaded) {
      realNextCard();
      return;
    }
    if (cardCache[id].error) {
      scope.skip();
      return;
    }

    StProgress.start(2500);
    cardCache[id].img.addEventListener('load', function () {
      StProgress.stop(250);
      realNextCard();
      $scope.$apply();
    });
    cardCache[id].img.addEventListener('error', function () {
      scope.skip();
      $scope.$apply();
    });
  }

  function realNextCard() {
    // TODO skip cards without pics, but put those names in the deck
    var choices = [];
    var card;
    var i = 1;

    scope.card = card = scope.goodCards[0];
    console.log('realNextCard', card);
    //scope.hint = '';
    //scope.guess = '';
    if (!scope.card) {
      gameOver();
      return;
    }

    i = 1; // 0 is the definite card
    while (i < scope.goodCards.length && choices.length < 12) {
      if (card.gender === scope.goodCards[i].gender) {
        choices.push(scope.goodCards[i]);
      }
      i += 1;
    }

    // TODO why isn't this one 1 also?
    // BUG XXX when hanna was playing she had the same dude's name
    // appear as 2 (not just 1) of the 4 options.
    i = 0;
    while (i < scope.allCards.length && choices.length < 12) {
      if (card.gender === scope.allCards[i].gender) {
        choices.push(scope.allCards[i]);
      }
      i += 1;
    }

    shuffle(choices);
    choices = [scope.card, choices[0], choices[1], choices[2]];
    shuffle(choices);

    scope.choices = choices;

    if (!scope.card.photo) {
      console.log('[MISSING PERSON REPORT]', scope.card);
      scope.skip();
      return;
    }
  }

  scope.shareOnFacebook = function () {
    var score = ((scope.hitCount / (scope.hitCount + scope.missCount)) * 100).toFixed(0)
      ;

    Facebook.ui(
      {
        method: 'feed',
        name: 'YSA Facecards',
        link: 'http://facecards.org',
        picture: 'http://facecards.org/images/logo-256px.png',
        caption: "I know " + score + "% of my ward members. How well do you know yours?",
        description: "Fight the awkward. Learn people's names!"
      },
      function(response) {
        if (response && response.post_id) {
          //window.alert('Post was published.');
        } else {
          //window.alert('Post was not published.');
        }
      }
    );
  };

  function loadsort(cards) {
    var groups = [[], []];
    cards.forEach(function (card, i) {
      groups[i % groups.length].push(card);
    });
    groups.forEach(function (group) {
      preload(group);
    });
  }

  function preload(cards) {
    var card = cards.pop();
    var img;

    if (!card) {
      // done preloading
      return;
    }

    if (cardCache[card.id]) {
      return preload(cards);
    }

    img = new Image();
    img.onload = function () {
      cardCache[card.id] = true;
      return preload(cards);
    };
    img.onerror = function () {
      return preload(cards);
    };
    img.src = card.photo;
  }

  scope.playAgain = function () {
    scope.hits = {};
    scope.hitCount = 0;
    scope.misses = {};
    scope.missings = {};
    scope.validCardCount = scope.allCards.length;
    scope.finished = false;
    shuffle(scope.allCards);
    scope.goodCards = scope.allCards.slice(0);
    //loadsort(scope.allCards.slice(0));
    nextCard();
  };

  function run() {
    /*
    var infog
      , rosterg
      ;
    */

    StProgress.start();
    LdsConnect.init().then(function (_mine) {
      console.log('mine');
      console.log(mine);
      mine = _mine;
      //loadStakes(me.currentStakes, me.currentUnits.stakeUnitNo);
      scope.stakes = mine.currentStakes;
      scope.units = mine.currentUnits;
      scope.groups = {
        brethren: true
      , sisters: true
      , leadership: true
      };
      scope.selectWard();
    });
  }
}])
;
<commit_msg>[FC] Chimney: burning logs and dead code<commit_after>'use strict';

angular.module('facecards', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  var tpl = window.jQuery('.st-view-facecards').html();
  window.jQuery('.st-view-facecards').html(' ');
  $routeProvider.when('/play', {
    //templateUrl: 'views/facecards/facecards.html'
    template: tpl
  , controller: 'FacecardsController as FC'
  });
}])

.controller('FacecardsController', [
  '$scope'
, '$timeout'
, '$location'
, 'MyAppSession'
, 'StProgress'
, 'LdsIoApi'
, function ($scope, $timeout, $location, MyAppSession, StProgress, LdsIoApi) {
  var FC = this;
  var scope = this;
  var shuffle = window.knuthShuffle;
  var mine;
  var cardCache = {};

  MyAppSession.onLogout($scope, function () {
    $location.url('/');
  });

  MyAppSession.requireSession().then(function (session) {
    // TODO this should be a directive and I realize that.
    // I'm just to lazy to do it right, right now
    $location.url('/play');

    FC.flashMessage = "Downloading Ward / Stake info...";
    StProgress.start(4 * 1000);
    return LdsIoApi.profile(session).then(function (profile) {
      FC.flashMessage = "Downloading Ward directory...";
      StProgress.restart(6 * 1000, { style: "success" });
      return LdsIoApi.ward(session, profile.home_stake_id, profile.home_ward_id).then(function (/*ward*/) {
        FC.flashMessage = "Downloading Photo directory...";
        StProgress.restart(10 * 1000, { style: "warning" });
        return LdsIoApi.wardPhotos(session, session.home_stake_id, profile.home_ward_id).then(function (/*photos*/) {
          FC.flashMessage = "";
          StProgress.stop(350);
          startGame();
          // update roster with photourls
        });
      });
    }, function (err) {
      console.error("[ERROR] when downloading directories");
      console.log(err);
      FC.flashMessage = err.message || "Unknown error while downloading ...";
      StProgress.stop(350);
    });
  }, function (e) {
    console.error('[ERROR] require session');
    console.log(e);
    $location().url('/');
  });

  //
  // GAME
  //
  //
  function startGame() {
    // TODO select stake and ward
    MyAppSession.requireSession().then(function (session) {
      return LdsIoApi.profile(session).then(function (profile) {
        return LdsIoApi.ward(session, profile.home_stake_id, profile.home_ward_id).then(function (ward) {
          return LdsIoApi.wardPhotos(session, profile.home_stake_id, profile.home_ward_id).then(function (photos) {
            var memberMap = {};
            var photoMemberMap = {};
            var photoHomeMap = {};

            // TODO app_scoped_ids
            photos.members.forEach(function (m) {
              photoMemberMap[m.app_scoped_id || m.id] = m;
            });
            photos.families.forEach(function (h) {
              photoHomeMap[h.app_scoped_id || h.id] = h;
            });

            ward.members.forEach(function (m) {
              var id = m.app_scoped_id || m.id;
              var photo = photoMemberMap[id] || photoHomeMap[id] || null;

              memberMap[id] = m;

              // TODO check updated but deleted and flag in server api
              if (photo && photo.updated_at) {
                m.photoUrl = LdsIoApi.photoUrl(session, photo, 'medium', photo.type);
              }
            });

            return ward.members.filter(function (m) {
              return m.photoUrl;
            }).map(function (m) {
              return {
                id: m.app_scoped_id || m.id
              , photo: m.photoUrl
              , name: m.name + ' ' + m.surnames.join(' ')
              , callings: m.callings
              , gender: LdsIoApi.guessGender(m)
              , attempts: 0
              };
            });
          });
        }).then(function (cardables) {
          scope.goodCards = cardables.slice(0);
          scope.allCards = cardables.slice(0);
          scope.playAgain();
        });
      });
    });
  }

  scope.skip = function () {
    // TODO mark as missing picture
    scope.validCardCount -= 1;
    scope.goodCards.shift();
    nextCard();
  };

  scope.choose = function (i) {
    if (scope.card !== scope.choices[i]) {
      scope.misses[scope.card.id] = true;
      scope.missCount = Object.keys(scope.misses).length;
      //TODO scope.misses[scope.choices[i].id] = true;
      scope.choices[i].disabled = true;
      return;
    }

    if (!scope.misses[scope.card.id]) {
      scope.hits[scope.card.id] = true;
      scope.hitCount += 1;
    }

    // TODO mark against pic and wrong choice
    scope.choices.forEach(function (choice) {
      delete choice.disabled;
    });

    scope.goodCards.shift();
    nextCard();
  };

  function gameOver() {
    // do something
    scope.finished = true;
    // https://www.facebook.com/sharer/sharer.php?s=100&p[url]=http://www.otlcampaign.org/blog/2013/02/04/inconvenient-truth-education-reform&p[images][0]=http://www.otlcampaign.org/sites/default/files/journey-for-justice-mlk-memorial.jpg&p[title]=The+Inconvenient+Truth+of+Education+%27Reform%27!&p[summary]=Recent+events+have+revealed+how+market-driven+education+policies,+deceivingly+labeled+as+%22reform,%22+are+revealing+their+truly+destructive+effects+on+the+streets+and+in+the+corridors+of+government:
    scope.xfbShare = "https://www.facebook.com/sharer/sharer.php"
      + '?s=100'
      + '&p[title]=How+well+do+you+know+your+wardies?'
      + '&p[url]=http://facecards.org'
      + '&p[images][0]=http://facecards.org/images/logo-256px.png'
      + '&p[summary]=Fight+the+awkward.+Learn+the+names+of+your+ward+members.'
      ;
    return;
  }

  function preloadImage(card) {
    var id = card.id;
    if (cardCache[id]) {
      return;
    }
    cardCache[id] = { img: new Image() };
    cardCache[id].img.addEventListener('load', function () {
      cardCache[id].loaded = true;
    });
    cardCache[id].img.addEventListener('error', function () {
      cardCache[id].error = true;
    });
    cardCache[id].img.src = card.photo;
  }

  function nextCard() {
    var card = scope.goodCards[0];
    var id = scope.goodCards[0].id;

    // always be preloading the next 6 cards
    scope.goodCards.slice(0, 6).forEach(function (card) {
      preloadImage(card);
    });

    if (cardCache[id].loaded) {
      realNextCard();
      return;
    }
    if (cardCache[id].error) {
      scope.skip();
      return;
    }

    StProgress.start(2500);
    cardCache[id].img.addEventListener('load', function () {
      StProgress.stop(250);
      realNextCard();
      $scope.$apply();
    });
    cardCache[id].img.addEventListener('error', function () {
      scope.skip();
      $scope.$apply();
    });
  }

  function realNextCard() {
    // TODO skip cards without pics, but put those names in the deck
    var choices = [];
    var card;
    var i = 1;

    scope.card = card = scope.goodCards[0];
    if (!scope.card) {
      gameOver();
      return;
    }

    i = 1; // 0 is the definite card
    while (i < scope.goodCards.length && choices.length < 12) {
      if (card.gender === scope.goodCards[i].gender) {
        choices.push(scope.goodCards[i]);
      }
      i += 1;
    }

    // TODO why isn't this one 1 also?
    // BUG XXX when hanna was playing she had the same dude's name
    // appear as 2 (not just 1) of the 4 options.
    i = 0;
    while (i < scope.allCards.length && choices.length < 12) {
      if (card.gender === scope.allCards[i].gender) {
        choices.push(scope.allCards[i]);
      }
      i += 1;
    }

    shuffle(choices);
    choices = [scope.card, choices[0], choices[1], choices[2]];
    shuffle(choices);

    scope.choices = choices;

    if (!scope.card.photo) {
      console.warn('[MISSING PERSON REPORT]', scope.card);
      scope.skip();
      return;
    }
  }

  scope.shareOnFacebook = function () {
    var score = ((scope.hitCount / (scope.hitCount + scope.missCount)) * 100).toFixed(0)
      ;

    Facebook.ui(
      {
        method: 'feed',
        name: 'YSA Facecards',
        link: 'http://facecards.org',
        picture: 'http://facecards.org/images/logo-256px.png',
        caption: "I know " + score + "% of my ward members. How well do you know yours?",
        description: "Fight the awkward. Learn people's names!"
      },
      function(response) {
        if (response && response.post_id) {
          //window.alert('Post was published.');
        } else {
          //window.alert('Post was not published.');
        }
      }
    );
  };

  scope.playAgain = function () {
    scope.hits = {};
    scope.hitCount = 0;
    scope.misses = {};
    scope.missings = {};
    scope.validCardCount = scope.allCards.length;
    scope.finished = false;
    shuffle(scope.allCards);
    scope.goodCards = scope.allCards.slice(0);
    //loadsort(scope.allCards.slice(0));
    nextCard();
  };
}])
;
<|endoftext|>


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

/**
 * Internal dependencies
 */
import { pageWait } from '../utils';

/**
 * Changes the currently selected date range.
 *
 * Currently only identifiable by the option values.
 *
 * @since 1.0.0
 *
 * @param {string} fromRange The currently selected date range.
 * @param {string} toRange   The new date range to select.
 */
export async function switchDateRange( fromRange, toRange ) {
	await pageWait();
	await Promise.all( [
		expect( page ).toClick( '.googlesitekit-date-range-selector .mdc-button__label', { text: new RegExp( fromRange, 'i' ) } ),
		page.waitForSelector( '.mdc-menu-surface--open .mdc-list-item' ),
	] );
	// Intentionally left off the '--open' suffix here as it proved problematic for stability.
	await expect( page ).toClick( '.mdc-menu-surface .mdc-list-item', { text: new RegExp( toRange, 'i' ) } );
}
<commit_msg>Update tests/e2e/utils/switch-date-range.js<commit_after>/**
 * Utility for switching the current date range.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Internal dependencies
 */
import { pageWait } from '../utils';

/**
 * Changes the currently selected date range.
 *
 * Currently only identifiable by the option values.
 *
 * @since 1.0.0
 *
 * @param {string} fromRange The currently selected date range.
 * @param {string} toRange   The new date range to select.
 */
export async function switchDateRange( fromRange, toRange ) {
	await pageWait();
	await Promise.all( [
		expect( page ).toClick( '.googlesitekit-date-range-selector .mdc-button__label', { text: new RegExp( fromRange, 'i' ) } ),
		page.waitForSelector( '.googlesitekit-date-range-selector .mdc-menu-surface--open .mdc-list-item' ),
	] );
	// Intentionally left off the '--open' suffix here as it proved problematic for stability.
	await expect( page ).toClick( '.mdc-menu-surface .mdc-list-item', { text: new RegExp( toRange, 'i' ) } );
}
<|endoftext|>


<commit_before>import {
  click,
  find,
  triggerEvent,
  visit
} from '@ember/test-helpers';
import hasEmberVersion from '@ember/test-helpers/has-ember-version';
import { A } from '@ember/array';
import { run } from '@ember/runloop';
import EmberComponent from '@ember/component';
import EmberRoute from '@ember/routing/route';
import EmberObject from '@ember/object';
import Controller from '@ember/controller';
import { module, test } from 'qunit';
import { hbs } from 'ember-cli-htmlbars';
import require from 'require';
import QUnit from 'qunit';
import { destroyEIApp, setupEIApp } from '../helpers/setup-destroy-ei-app';

const EmberDebug = require('ember-debug/main').default;
let port;
let App;

// TODO make the debounce configurable for tests
async function timeout(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

// TODO switch to an adapter architecture, similar to the acceptance tests
async function captureMessage(type, callback) {
  if (!port) {
    throw new Error('Cannot call captureMessage without a port');
  }

  let send = port.send;

  try {
    let captured;

    port.send = (name, message) => {
      if (!captured && name === type) {
        captured = message;
      } else {
        send.call(port, name, message);
      }
    };

    await callback();

    if (captured) {
      return captured;
    } else {
      throw new Error(`Did not send a message of type ${type}`);
    }
  } finally {
    port.send = send;
  }
}

async function getRenderTree() {
  let message = await captureMessage('view:renderTree', async () => {
    port.trigger('view:getTree', {});
    await timeout(300);
  });

  if (message) {
    return message.tree;
  }
}

function setTemplate(name, factory) {
  if (typeof factory.meta === 'object') {
    factory.meta.moduleName = `my-app/templates/${name}.hbs`;
  } else if (typeof factory.__meta === 'object') {
    // Ember 3.13+
    factory.__meta.moduleName = `my-app/templates/${name}.hbs`;
  }

  this.owner.register(`template:${name}`, factory);
}

function isVisible(element) {
  let { width, height } = element.getBoundingClientRect();
  return width > 0 && height > 0;
}

function setupApp() {
  this.owner.register('route:application', EmberRoute.extend({
    model() {
      return EmberObject.create({
        toString() {
          return 'Application model';
        }
      });
    }
  }));

  this.owner.register('route:simple', EmberRoute.extend({
    model() {
      return EmberObject.create({
        toString() {
          return 'Simple Model';
        }
      });
    }
  }));

  this.owner.register('route:comments.index', EmberRoute.extend({
    model() {
      return A(['first comment', 'second comment', 'third comment']);
    }
  }));

  this.owner.register('route:posts', EmberRoute.extend({
    model() {
      return 'String as model';
    }
  }));

  this.owner.register('controller:application', Controller.extend({
    toString() {
      return 'App.ApplicationController';
    }
  }));

  this.owner.register('controller:simple', Controller.extend({
    toString() {
      return 'App.SimpleController';
    }
  }));

  this.owner.register('component:test-foo', EmberComponent.extend({
    classNames: ['simple-component'],
    toString() {
      return 'App.TestFooComponent';
    }
  }));

  this.owner.register('component:test-bar', EmberComponent.extend({
    tagName: '',
    toString() {
      return 'App.TestBarComponent';
    }
  }));

  setTemplate.call(this, 'application', hbs`<div class="application">{{outlet}}</div>`);
  setTemplate.call(this, 'simple', hbs`Simple {{test-foo}} {{test-bar}}`);
  setTemplate.call(this, 'comments/index', hbs`{{#each this.comments as |comment|}}{{comment}}{{/each}}`);
  setTemplate.call(this, 'posts', hbs`Posts`);
  setTemplate.call(this, 'components/test-foo', hbs`test-foo`);
  setTemplate.call(this, 'components/test-bar', hbs`<!-- before --><div class="another-component"><span>test</span> <span class="bar-inner">bar</span></div><!-- after -->`);

}

function matchTree(tree, matchers) {
  QUnit.assert.equal(tree.length, matchers.length, 'tree and matcher should have the same length');

  for (let i = 0; i < matchers.length; i++) {
    match(tree[i], matchers[i]);
  }
}

function match(actual, matcher) {
  if (typeof matcher === 'function') {
    matcher(actual);
  } else if (Array.isArray(matcher)) {
    QUnit.assert.ok(matcher.indexOf(actual) > -1, `${actual} should be one of ${matcher.join('/')}`);
  } else if (matcher instanceof RegExp) {
    QUnit.assert.ok(actual.match(matcher), `${actual} should match ${matcher}`);
  } else if(matcher !== null && typeof matcher === 'object') {
    QUnit.assert.deepEqual(actual, matcher);
  } else {
    QUnit.assert.strictEqual(actual, matcher);
  }
}

function Any() {
  return () => {};
}

function Eq(item) {
  return actual => {
    QUnit.assert.strictEqual(actual, item);
  }
}

function Undefined() {
  return Eq(undefined);
}

function Serialized(id) {
  return actual => {
    QUnit.assert.ok(typeof actual === 'object' && actual !== null, 'serialized object should be an object');
    QUnit.assert.ok(typeof actual.id === 'string', 'serialized object should have a string id');

    if (id === undefined) {
      QUnit.assert.ok(actual.id.match(/^ember[0-9]+$/), 'serialized object should have an ember guid');
    } else {
      QUnit.assert.equal(actual.id, id, 'serialized object should have an ember guid');
    }
  };
}

function RenderNodeID(id) {
  return actual => {
    QUnit.assert.ok(typeof actual === 'string', 'render node id should be a string');

    if (id === undefined) {
      QUnit.assert.ok(actual.match(/^render-node:.+$/), 'render node id should have the right format');
    } else {
      QUnit.assert.equal(actual, id, 'render node id should match');
    }
  };
}

function Args({ names = [], positionals = 0 } = {}) {
  return actual => {
    QUnit.assert.ok(typeof actual === 'object' && actual !== null, 'serialized args should be an object');

    QUnit.assert.ok(typeof actual.named === 'object' && actual !== null, 'serialized named args should be an object');
    QUnit.assert.deepEqual(Object.keys(actual.named), names, 'serialized named args should have the right keys');

    QUnit.assert.ok(Array.isArray(actual.positional), 'serialized positional args should be an array');
    QUnit.assert.strictEqual(actual.positional.length, positionals, 'serialized positional args should have the right number of items');
  };
}

function RenderNode({
  id = RenderNodeID(),
  type,
  name,
  args = Args(),
  instance = Any(),
  template = /^.+\.hbs$/,
  bounds = ['single', 'range', null]
}, ...children) {
  return actual => {
    match(actual.id, id);
    match(actual.type, type);
    match(actual.name, name);
    match(actual.args, args);
    match(actual.instance, instance);
    match(actual.template, template);
    match(actual.bounds, bounds);
    matchTree(actual.children, children);
  };
}

function Component({
  name,
  instance = Serialized(),
  template = `my-app/templates/components/${name}.hbs`,
  bounds = 'single',
  ...options
}, ...children) {
  return RenderNode({ name, instance, template, bounds, ...options, type: 'component' },
    ...children
  );
}

function Route({
  name,
  args = hasEmberVersion(3, 14) ? Args({ names: ['model'] }) : Args(),
  instance = Serialized(),
  template = `my-app/templates/${name}.hbs`,
  ...options
}, ...children) {
  return RenderNode({ type: 'outlet', name: 'main', instance: undefined, template: null },
    RenderNode({ name, args, instance, template, ...options, type: 'route-template' },
      ...children
    )
  );
}

function TopLevel(...children) {
  return Route({
    name: '-top-level',
    args: Args(),
    instance: Undefined(),
    template: /^packages\/.+\/templates\/outlet\.hbs$/,
  }, ...children);
}

function findInspectorElement(kind) {
  for(let element of document.body.children) {
    if (element.id.startsWith(`ember-inspector-${kind}-`)) {
      return element;
    }
  }

  throw new Error(`Cannot find ${kind} inspector element`);
}

module('Ember Debug - View', function(hooks) {
  hooks.beforeEach(async function() {
    EmberDebug.Port = EmberDebug.Port.extend({
      init() {},
      send() {}
    });
    EmberDebug.IGNORE_DEPRECATIONS = true;

    App = await setupEIApp.call(this, EmberDebug, function() {
      this.route('simple');
      this.route('comments', { resetNamespace: true }, function() {});
      this.route('posts', { resetNamespace: true });
    });

    setupApp.call(this);

    port = EmberDebug.port;
  });

  hooks.afterEach(async function() {
    await destroyEIApp.call(this, EmberDebug, App);
  });

  test('Simple View Tree', async function() {
    await visit('/simple');

    let tree = await getRenderTree();

    matchTree(tree, [
      TopLevel(
        Route({ name: 'application' },
          Route({ name: 'simple' },
            Component({ name: 'test-foo', bounds: 'single' }),
            Component({ name: 'test-bar', bounds: 'range' })
          )
        )
      )
    ]);
  });

  test('Supports applications that don\'t have the ember-application CSS class', async function(assert) {
    await visit('/simple');

    assert.dom(this.element).hasClass(
      'ember-application',
      'The rootElement has the .ember-application CSS class'
    );

    this.element.classList.remove('ember-application');

    // Restart the inspector
    EmberDebug.start();
    port = EmberDebug.port;

    await visit('/simple');

    assert.dom(this.element).doesNotHaveClass(
      'ember-application',
      'The rootElement no longer has the .ember-application CSS class'
    );

    let tree = await getRenderTree();

    matchTree(tree, [
      TopLevel(
        Route({ name: 'application' },
          Route({ name: 'simple' },
            Component({ name: 'test-foo', bounds: 'single' }),
            Component({ name: 'test-bar', bounds: 'range' })
          )
        )
      )
    ]);
  });

  test('Does not list nested {{yield}} views', async function() {
    this.owner.register('component:x-first', EmberComponent.extend());
    this.owner.register('component:x-second', EmberComponent.extend());

    setTemplate.call(this, 'posts', hbs`{{#x-first}}Foo{{/x-first}}`);
    setTemplate.call(this, 'components/x-first', hbs`{{#x-second}}{{yield}}{{/x-second}}`);
    setTemplate.call(this, 'components/x-second', hbs`{{yield}}`);

    await visit('/posts');

    let tree = await getRenderTree();

    matchTree(tree, [
      TopLevel(
        Route({ name: 'application' },
          Route({ name: 'posts' },
            Component({ name: 'x-first' },
              Component({ name: 'x-second' })
            )
          )
        )
      )
    ]);
  });

  test('Highlighting Views on hover', async function(assert) {
    await visit('/simple');
    await getRenderTree();

    let foo = find('.simple-component');
    let bar = find('.another-component');
    let tooltip = findInspectorElement('tooltip');
    let highlight = findInspectorElement('highlight');

    assert.ok(!isVisible(tooltip), 'tooltip is not visible');
    assert.ok(!isVisible(highlight), 'highlight is not visible');

    run(() => port.trigger('view:inspectViews', { inspect: true }));

    await triggerEvent('.simple-component', 'mousemove');

    assert.ok(isVisible(tooltip), 'tooltip is visible');
    assert.dom('.ember-inspector-tooltip-header', tooltip).hasText('<TestFoo>');
    assert.dom('.ember-inspector-tooltip-detail-template', tooltip).hasText('my-app/templates/components/test-foo.hbs');
    assert.dom('.ember-inspector-tooltip-detail-instance', tooltip).hasText('App.TestFooComponent');

    let actual = highlight.getBoundingClientRect();
    let expected = foo.getBoundingClientRect();

    assert.ok(isVisible(highlight), 'highlight is visible');
    assert.equal(actual.x, expected.x, 'same x as component');
    assert.equal(actual.y, expected.y, 'same y as component');
    assert.equal(actual.width, expected.width, 'same width as component');
    assert.equal(actual.height, expected.height, 'same height as component');

    await triggerEvent('.bar-inner', 'mousemove');

    assert.ok(isVisible(tooltip), 'tooltip is visible');
    assert.dom('.ember-inspector-tooltip-header', tooltip).hasText('<TestBar>');
    assert.dom('.ember-inspector-tooltip-detail-template', tooltip).hasText('my-app/templates/components/test-bar.hbs');
    assert.dom('.ember-inspector-tooltip-detail-instance', tooltip).hasText('App.TestBarComponent');

    actual = highlight.getBoundingClientRect();
    expected = bar.getBoundingClientRect();

    assert.ok(isVisible(highlight), 'highlight is visible');
    assert.equal(actual.x, expected.x, 'same x as component');
    assert.equal(actual.y, expected.y, 'same y as component');
    assert.equal(actual.width, expected.width, 'same width as component');
    assert.equal(actual.height, expected.height, 'same height as component');

    await triggerEvent(document.body, 'mousemove');

    assert.ok(!isVisible(tooltip), 'tooltip is not visible');
    assert.ok(!isVisible(highlight), 'highlight is not visible');

    // Pin tooltip and stop inspecting
    await click('.simple-component');
    await triggerEvent('.bar-inner', 'mousemove');

    assert.ok(isVisible(tooltip), 'tooltip is visible');
    assert.dom('.ember-inspector-tooltip-header', tooltip).hasText('<TestFoo>');
    assert.dom('.ember-inspector-tooltip-detail-template', tooltip).hasText('my-app/templates/components/test-foo.hbs');
    assert.dom('.ember-inspector-tooltip-detail-instance', tooltip).hasText('App.TestFooComponent');

    actual = highlight.getBoundingClientRect();
    expected = foo.getBoundingClientRect();

    assert.ok(isVisible(highlight), 'highlight is visible');
    assert.equal(actual.x, expected.x, 'same x as component');
    assert.equal(actual.y, expected.y, 'same y as component');
    assert.equal(actual.width, expected.width, 'same width as component');
    assert.equal(actual.height, expected.height, 'same height as component');

    assert.ok(isVisible(tooltip), 'tooltip is visible');
    assert.dom('.ember-inspector-tooltip-header', tooltip).hasText('<TestFoo>');
    assert.dom('.ember-inspector-tooltip-detail-template', tooltip).hasText('my-app/templates/components/test-foo.hbs');
    assert.dom('.ember-inspector-tooltip-detail-instance', tooltip).hasText('App.TestFooComponent');

    await triggerEvent(this.element, 'mousemove');

    assert.ok(isVisible(tooltip), 'tooltip is pinned');
    assert.ok(isVisible(highlight), 'highlight is pinned');

    // TODO support clicking on the instance to open object inspector

    // Dismiss tooltip
    await click(this.element);

    assert.ok(!isVisible(tooltip), 'tooltip is not visible');
    assert.ok(!isVisible(highlight), 'highlight is not visible');

    await triggerEvent('.bar-inner', 'mousemove');

    assert.ok(!isVisible(tooltip), 'tooltip is not visible');
    assert.ok(!isVisible(highlight), 'highlight is not visible');
  });
});
<commit_msg>Fix component highlight test<commit_after>import {
  click,
  find,
  triggerEvent,
  visit
} from '@ember/test-helpers';
import hasEmberVersion from '@ember/test-helpers/has-ember-version';
import { A } from '@ember/array';
import { run } from '@ember/runloop';
import EmberComponent from '@ember/component';
import EmberRoute from '@ember/routing/route';
import EmberObject from '@ember/object';
import Controller from '@ember/controller';
import { module, test } from 'qunit';
import { hbs } from 'ember-cli-htmlbars';
import require from 'require';
import QUnit from 'qunit';
import { destroyEIApp, setupEIApp } from '../helpers/setup-destroy-ei-app';

const EmberDebug = require('ember-debug/main').default;
let port;
let App;

// TODO make the debounce configurable for tests
async function timeout(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

// TODO switch to an adapter architecture, similar to the acceptance tests
async function captureMessage(type, callback) {
  if (!port) {
    throw new Error('Cannot call captureMessage without a port');
  }

  let send = port.send;

  try {
    let captured;

    port.send = (name, message) => {
      if (!captured && name === type) {
        captured = message;
      } else {
        send.call(port, name, message);
      }
    };

    await callback();

    if (captured) {
      return captured;
    } else {
      throw new Error(`Did not send a message of type ${type}`);
    }
  } finally {
    port.send = send;
  }
}

async function getRenderTree() {
  let message = await captureMessage('view:renderTree', async () => {
    port.trigger('view:getTree', {});
    await timeout(300);
  });

  if (message) {
    return message.tree;
  }
}

function setTemplate(name, factory) {
  if (typeof factory.meta === 'object') {
    factory.meta.moduleName = `my-app/templates/${name}.hbs`;
  } else if (typeof factory.__meta === 'object') {
    // Ember 3.13+
    factory.__meta.moduleName = `my-app/templates/${name}.hbs`;
  }

  this.owner.register(`template:${name}`, factory);
}

function isVisible(element) {
  let { width, height } = element.getBoundingClientRect();
  return width > 0 && height > 0;
}

function setupApp() {
  this.owner.register('route:application', EmberRoute.extend({
    model() {
      return EmberObject.create({
        toString() {
          return 'Application model';
        }
      });
    }
  }));

  this.owner.register('route:simple', EmberRoute.extend({
    model() {
      return EmberObject.create({
        toString() {
          return 'Simple Model';
        }
      });
    }
  }));

  this.owner.register('route:comments.index', EmberRoute.extend({
    model() {
      return A(['first comment', 'second comment', 'third comment']);
    }
  }));

  this.owner.register('route:posts', EmberRoute.extend({
    model() {
      return 'String as model';
    }
  }));

  this.owner.register('controller:application', Controller.extend({
    toString() {
      return 'App.ApplicationController';
    }
  }));

  this.owner.register('controller:simple', Controller.extend({
    toString() {
      return 'App.SimpleController';
    }
  }));

  this.owner.register('component:test-foo', EmberComponent.extend({
    classNames: ['simple-component'],
    toString() {
      return 'App.TestFooComponent';
    }
  }));

  this.owner.register('component:test-bar', EmberComponent.extend({
    tagName: '',
    toString() {
      return 'App.TestBarComponent';
    }
  }));

  /*
    Setting line-height to normal because normalize.css sets the
    html line-height to 1.15. This seems to cause a measurement
    error with getBoundingClientRect
  */
  setTemplate.call(this, 'application', hbs`<div class="application" style="line-height: normal;">{{outlet}}</div>`);
  setTemplate.call(this, 'simple', hbs`Simple {{test-foo}} {{test-bar}}`);
  setTemplate.call(this, 'comments/index', hbs`{{#each this.comments as |comment|}}{{comment}}{{/each}}`);
  setTemplate.call(this, 'posts', hbs`Posts`);
  setTemplate.call(this, 'components/test-foo', hbs`test-foo`);
  setTemplate.call(this, 'components/test-bar', hbs`<!-- before --><div class="another-component"><span>test</span> <span class="bar-inner">bar</span></div><!-- after -->`);

}

function matchTree(tree, matchers) {
  QUnit.assert.equal(tree.length, matchers.length, 'tree and matcher should have the same length');

  for (let i = 0; i < matchers.length; i++) {
    match(tree[i], matchers[i]);
  }
}

function match(actual, matcher) {
  if (typeof matcher === 'function') {
    matcher(actual);
  } else if (Array.isArray(matcher)) {
    QUnit.assert.ok(matcher.indexOf(actual) > -1, `${actual} should be one of ${matcher.join('/')}`);
  } else if (matcher instanceof RegExp) {
    QUnit.assert.ok(actual.match(matcher), `${actual} should match ${matcher}`);
  } else if(matcher !== null && typeof matcher === 'object') {
    QUnit.assert.deepEqual(actual, matcher);
  } else {
    QUnit.assert.strictEqual(actual, matcher);
  }
}

function Any() {
  return () => {};
}

function Eq(item) {
  return actual => {
    QUnit.assert.strictEqual(actual, item);
  }
}

function Undefined() {
  return Eq(undefined);
}

function Serialized(id) {
  return actual => {
    QUnit.assert.ok(typeof actual === 'object' && actual !== null, 'serialized object should be an object');
    QUnit.assert.ok(typeof actual.id === 'string', 'serialized object should have a string id');

    if (id === undefined) {
      QUnit.assert.ok(actual.id.match(/^ember[0-9]+$/), 'serialized object should have an ember guid');
    } else {
      QUnit.assert.equal(actual.id, id, 'serialized object should have an ember guid');
    }
  };
}

function RenderNodeID(id) {
  return actual => {
    QUnit.assert.ok(typeof actual === 'string', 'render node id should be a string');

    if (id === undefined) {
      QUnit.assert.ok(actual.match(/^render-node:.+$/), 'render node id should have the right format');
    } else {
      QUnit.assert.equal(actual, id, 'render node id should match');
    }
  };
}

function Args({ names = [], positionals = 0 } = {}) {
  return actual => {
    QUnit.assert.ok(typeof actual === 'object' && actual !== null, 'serialized args should be an object');

    QUnit.assert.ok(typeof actual.named === 'object' && actual !== null, 'serialized named args should be an object');
    QUnit.assert.deepEqual(Object.keys(actual.named), names, 'serialized named args should have the right keys');

    QUnit.assert.ok(Array.isArray(actual.positional), 'serialized positional args should be an array');
    QUnit.assert.strictEqual(actual.positional.length, positionals, 'serialized positional args should have the right number of items');
  };
}

function RenderNode({
  id = RenderNodeID(),
  type,
  name,
  args = Args(),
  instance = Any(),
  template = /^.+\.hbs$/,
  bounds = ['single', 'range', null]
}, ...children) {
  return actual => {
    match(actual.id, id);
    match(actual.type, type);
    match(actual.name, name);
    match(actual.args, args);
    match(actual.instance, instance);
    match(actual.template, template);
    match(actual.bounds, bounds);
    matchTree(actual.children, children);
  };
}

function Component({
  name,
  instance = Serialized(),
  template = `my-app/templates/components/${name}.hbs`,
  bounds = 'single',
  ...options
}, ...children) {
  return RenderNode({ name, instance, template, bounds, ...options, type: 'component' },
    ...children
  );
}

function Route({
  name,
  args = hasEmberVersion(3, 14) ? Args({ names: ['model'] }) : Args(),
  instance = Serialized(),
  template = `my-app/templates/${name}.hbs`,
  ...options
}, ...children) {
  return RenderNode({ type: 'outlet', name: 'main', instance: undefined, template: null },
    RenderNode({ name, args, instance, template, ...options, type: 'route-template' },
      ...children
    )
  );
}

function TopLevel(...children) {
  return Route({
    name: '-top-level',
    args: Args(),
    instance: Undefined(),
    template: /^packages\/.+\/templates\/outlet\.hbs$/,
  }, ...children);
}

function findInspectorElement(kind) {
  for(let element of document.body.children) {
    if (element.id.startsWith(`ember-inspector-${kind}-`)) {
      return element;
    }
  }

  throw new Error(`Cannot find ${kind} inspector element`);
}

module('Ember Debug - View', function(hooks) {
  hooks.beforeEach(async function() {
    EmberDebug.Port = EmberDebug.Port.extend({
      init() {},
      send() {}
    });
    EmberDebug.IGNORE_DEPRECATIONS = true;

    App = await setupEIApp.call(this, EmberDebug, function() {
      this.route('simple');
      this.route('comments', { resetNamespace: true }, function() {});
      this.route('posts', { resetNamespace: true });
    });

    setupApp.call(this);

    port = EmberDebug.port;
  });

  hooks.afterEach(async function() {
    await destroyEIApp.call(this, EmberDebug, App);
  });

  test('Simple View Tree', async function() {
    await visit('/simple');

    let tree = await getRenderTree();

    matchTree(tree, [
      TopLevel(
        Route({ name: 'application' },
          Route({ name: 'simple' },
            Component({ name: 'test-foo', bounds: 'single' }),
            Component({ name: 'test-bar', bounds: 'range' })
          )
        )
      )
    ]);
  });

  test('Supports applications that don\'t have the ember-application CSS class', async function(assert) {
    await visit('/simple');

    assert.dom(this.element).hasClass(
      'ember-application',
      'The rootElement has the .ember-application CSS class'
    );

    this.element.classList.remove('ember-application');

    // Restart the inspector
    EmberDebug.start();
    port = EmberDebug.port;

    await visit('/simple');

    assert.dom(this.element).doesNotHaveClass(
      'ember-application',
      'The rootElement no longer has the .ember-application CSS class'
    );

    let tree = await getRenderTree();

    matchTree(tree, [
      TopLevel(
        Route({ name: 'application' },
          Route({ name: 'simple' },
            Component({ name: 'test-foo', bounds: 'single' }),
            Component({ name: 'test-bar', bounds: 'range' })
          )
        )
      )
    ]);
  });

  test('Does not list nested {{yield}} views', async function() {
    this.owner.register('component:x-first', EmberComponent.extend());
    this.owner.register('component:x-second', EmberComponent.extend());

    setTemplate.call(this, 'posts', hbs`{{#x-first}}Foo{{/x-first}}`);
    setTemplate.call(this, 'components/x-first', hbs`{{#x-second}}{{yield}}{{/x-second}}`);
    setTemplate.call(this, 'components/x-second', hbs`{{yield}}`);

    await visit('/posts');

    let tree = await getRenderTree();

    matchTree(tree, [
      TopLevel(
        Route({ name: 'application' },
          Route({ name: 'posts' },
            Component({ name: 'x-first' },
              Component({ name: 'x-second' })
            )
          )
        )
      )
    ]);
  });

  test('Highlighting Views on hover', async function(assert) {
    await visit('/simple');
    await getRenderTree();

    let foo = find('.simple-component');
    let bar = find('.another-component');
    let tooltip = findInspectorElement('tooltip');
    let highlight = findInspectorElement('highlight');

    assert.ok(!isVisible(tooltip), 'tooltip is not visible');
    assert.ok(!isVisible(highlight), 'highlight is not visible');

    run(() => port.trigger('view:inspectViews', { inspect: true }));

    await triggerEvent('.simple-component', 'mousemove');

    assert.ok(isVisible(tooltip), 'tooltip is visible');
    assert.dom('.ember-inspector-tooltip-header', tooltip).hasText('<TestFoo>');
    assert.dom('.ember-inspector-tooltip-detail-template', tooltip).hasText('my-app/templates/components/test-foo.hbs');
    assert.dom('.ember-inspector-tooltip-detail-instance', tooltip).hasText('App.TestFooComponent');

    let actual = highlight.getBoundingClientRect();
    let expected = foo.getBoundingClientRect();

    assert.ok(isVisible(highlight), 'highlight is visible');
    assert.equal(actual.x, expected.x, 'same x as component');
    assert.equal(actual.y, expected.y, 'same y as component');
    assert.equal(actual.width, expected.width, 'same width as component');
    assert.equal(actual.height, expected.height, 'same height as component');

    await triggerEvent('.bar-inner', 'mousemove');

    assert.ok(isVisible(tooltip), 'tooltip is visible');
    assert.dom('.ember-inspector-tooltip-header', tooltip).hasText('<TestBar>');
    assert.dom('.ember-inspector-tooltip-detail-template', tooltip).hasText('my-app/templates/components/test-bar.hbs');
    assert.dom('.ember-inspector-tooltip-detail-instance', tooltip).hasText('App.TestBarComponent');

    actual = highlight.getBoundingClientRect();
    expected = bar.getBoundingClientRect();

    assert.ok(isVisible(highlight), 'highlight is visible');
    assert.equal(actual.x, expected.x, 'same x as component');
    assert.equal(actual.y, expected.y, 'same y as component');
    assert.equal(actual.width, expected.width, 'same width as component');
    assert.equal(actual.height, expected.height, 'same height as component');

    await triggerEvent(document.body, 'mousemove');

    assert.ok(!isVisible(tooltip), 'tooltip is not visible');
    assert.ok(!isVisible(highlight), 'highlight is not visible');

    // Pin tooltip and stop inspecting
    await click('.simple-component');
    await triggerEvent('.bar-inner', 'mousemove');

    assert.ok(isVisible(tooltip), 'tooltip is visible');
    assert.dom('.ember-inspector-tooltip-header', tooltip).hasText('<TestFoo>');
    assert.dom('.ember-inspector-tooltip-detail-template', tooltip).hasText('my-app/templates/components/test-foo.hbs');
    assert.dom('.ember-inspector-tooltip-detail-instance', tooltip).hasText('App.TestFooComponent');

    actual = highlight.getBoundingClientRect();
    expected = foo.getBoundingClientRect();

    assert.ok(isVisible(highlight), 'highlight is visible');
    assert.equal(actual.x, expected.x, 'same x as component');
    assert.equal(actual.y, expected.y, 'same y as component');
    assert.equal(actual.width, expected.width, 'same width as component');
    assert.equal(actual.height, expected.height, 'same height as component');

    assert.ok(isVisible(tooltip), 'tooltip is visible');
    assert.dom('.ember-inspector-tooltip-header', tooltip).hasText('<TestFoo>');
    assert.dom('.ember-inspector-tooltip-detail-template', tooltip).hasText('my-app/templates/components/test-foo.hbs');
    assert.dom('.ember-inspector-tooltip-detail-instance', tooltip).hasText('App.TestFooComponent');

    await triggerEvent(this.element, 'mousemove');

    assert.ok(isVisible(tooltip), 'tooltip is pinned');
    assert.ok(isVisible(highlight), 'highlight is pinned');

    // TODO support clicking on the instance to open object inspector

    // Dismiss tooltip
    await click(this.element);

    assert.ok(!isVisible(tooltip), 'tooltip is not visible');
    assert.ok(!isVisible(highlight), 'highlight is not visible');

    await triggerEvent('.bar-inner', 'mousemove');

    assert.ok(!isVisible(tooltip), 'tooltip is not visible');
    assert.ok(!isVisible(highlight), 'highlight is not visible');
  });
});
<|endoftext|>


<commit_before>/*
 * Copyright 2014-2016 Mendix Technology bv
 */
require([
    "dojo/_base/declare", "mxui/widget/_WidgetBase", "dijit/_TemplatedMixin",
    "mxui/dom", "dojo/dom-class", "dojo/dom-style", "dojo/dom-construct"
], function(declare, _WidgetBase, _TemplatedMixin, dom, domClass, domStyle, domConstruct) {

    "use strict";

    return declare("CameraWidgetForPhoneGap.widget.CameraWidgetForPhoneGap", [ _WidgetBase ], {

        buttonClass: "wx-mxwx-button-extra",
        buttonText: "activate camera",
        imageContainerClass: "wx-mxwx-imagecontainer-extra",
        imageWidth: 150,
        imageHeight: 150,
        imageLocation: "right",
        targetWidth: 150,
        targetHeight: 150,
        autoSaveEnabled: false,
        onchangemf: "",
        pictureSource: "camera",

        _contextObj: null,
        _imageUrl: null,
        _previewNode: null,

        postCreate: function() {
            domClass.add(this.domNode, "wx-CameraWidgetForPhoneGap");

            var elements = [ this._setupPreview(), this._setupButton() ];
            if (/below|right/.test(this.imageLocation)) {
                elements.reverse();
            }

            var alignment = "horizontal";
            if (/above|below/.test(this.imageLocation)) {
                alignment = "vertical";
            }

            elements.forEach(function(el) {
                domClass.add(el, "wx-CameraWidgetForPhoneGap-" + alignment);
                this.domNode.appendChild(el);
            }, this);

            this.listen("save", this._sendFile);
        },

        update: function(obj, callback) {
            if (obj) {
                this._contextObj = obj;
                this._loadData();
                this._resetSubscriptions();
                this._setPicture("");
            }

            if (callback) callback();
        },

        _setupButton: function() {
            var button = dom.create("button", {
                "type": "button",
                "class": "btn btn-primary wx-CameraWidgetForPhoneGap-button " + this.buttonClass
            }, this.buttonText);

            this.connect(button, "click", "_getPicture");
            return button;
        },

        _setupPreview: function() {
            this._previewNode = dom.create("div", {
                "class": "wx-CameraWidgetForPhoneGap-preview " + this.imageContainerClass
            });
            return this._previewNode;
        },

        _loadData: function() {
            if (this._contextObj) {
                if (!this._contextObj.inheritsFrom("System.FileDocument")) {
                    var span = dom.create("span", {
                            "class": "alert-danger"
                        },
                        "Entity '" + this._contextObj.getEntity() + "' does not inherit from 'System.FileDocument'");
                    domConstruct.empty(this.domNode);
                    this.domNode.appendChild(span);
                }
            }

        },

        _setPicture: function(url) {
            this._imageUrl = url;
            this._setThumbnail(url);
        },

        _setThumbnail: function(url) {
            var urlDisplay = url ? "" : "none",
                width = this.imageWidth || 100,
                height = this.imageHeight || 100,
                background = url ? "url(" + url + ")" : "none";

            domStyle.set(this._previewNode, {
                "background-image": background,
                "display": urlDisplay,
                "width": width + "px",
                "height": height + "px"
            });
        },

        _getPicture: function() {
            var self = this;

            if (!navigator.camera) {
                window.mx.ui.error("Unable to detect camera.");
                return;
            }

            var sourceType = (this.pictureSource == "camera") ?
                    Camera.PictureSourceType.CAMERA : Camera.PictureSourceType.PHOTOLIBRARY;
            var params = {
                quality: 50,
                destinationType: Camera.DestinationType.FILE_URL,
                correctOrientation: true,
                sourceType: sourceType
            };
            if (this.targetWidth !== 0) params.targetWidth = this.targetWidth;
            if (this.targetHeight !== 0) params.targetHeight = this.targetHeight;
            // TODO: get rid of temp image files
            navigator.camera.getPicture(success, error, params);

            function success(url) {
                if (self.autoSaveEnabled) {
                    self._autoSave(url);
                } else {
                    self._setPicture(url);
                }
            }

            function error(e) {
                if (typeof e.code !== "undefined") {
                    window.mx.ui.error("Retrieving image from camera failed with error code " + e.code);
                }
            }
        },

        _sendFile: function(callback) {
            var self = this;

            if (!this._imageUrl) {
                callback();
                return;
            }

            var filename = /[^\/]*$/.exec(this._imageUrl)[0];
            if (window.mx.data.saveDocument && window.mx.data.saveDocument.length === 6) {
                var guid = this._contextObj.getGuid();
                window.resolveLocalFileSystemURL(this._imageUrl, function(fileEntry) {
                    fileEntry.file(function(blob) {
                        var fileReader = new FileReader();
                        fileReader.onload = function(e) {
                            window.mx.data.saveDocument(guid, filename, {}, new Blob([ e.target.result ]), success, error);
                        };

                        fileReader.onerror = function(e) {
                            error(e.target.error);
                        };

                        fileReader.readAsArrayBuffer(blob);
                    }, error);
                }, error);
            } else {
                // For Mendix versions < 6.3
                var options = new FileUploadOptions();
                options.fileKey = "mxdocument";
                options.fileName = filename;
                options.mimeType = "image/jpeg";
                options.useBrowserHttp = true;

                var url = mx.appUrl +
                    "file?guid=" + this._contextObj.getGuid() +
                    "&csrfToken=" + mx.session.getCSRFToken();

                var ft = new FileTransfer();
                ft.upload(this._imageUrl, url, success, error, options);
            }

            function success() {
                self._setPicture("");
                self._executeMicroflow();
                callback();
            }

            function error(e) {
                window.mx.ui.error("Uploading image failed with error code " + e.code);
            }
        },

        _autoSave: function(url) {
            this._imageUrl = url;
            if (this._contextObj){
                 window.mx.data.save({
                     mxobj: this._contextObj,
                     callback: function(){
                        this._sendFile();
                     }
                }, this);
            }
        },

        _resetSubscriptions: function() {
            this.unsubscribeAll();

            if (this._contextObj) {
                this.subscribe({
                    guid: this._contextObj.getGuid(),
                    callback: function(guid) {
                        window.mx.data.get({
                            guid: guid,
                            callback: function(obj) {
                                this._contextObj = obj;
                                this._loadData();
                            }
                        }, this);
                    }
                });
            }
        },

        _executeMicroflow: function() {
            if (this.onchangemf && this._contextObj) {
                window.mx.data.action({
                    actionname: this.onchangemf,
                    applyto: "selection",
                    guids: [ this._contextObj.getGuid() ],
                    callback: function(objs) {
                        //ok
                    },
                    error: function(e) {
                        console.warn("Error running microflow: ", e);
                    }
                });
            }
        }
    });
});
<commit_msg>Fix for widget crashing on successful send on iOS<commit_after>/*
 * Copyright 2014-2016 Mendix Technology bv
 */
require([
    "dojo/_base/declare", "mxui/widget/_WidgetBase", "dijit/_TemplatedMixin",
    "mxui/dom", "dojo/dom-class", "dojo/dom-style", "dojo/dom-construct"
], function(declare, _WidgetBase, _TemplatedMixin, dom, domClass, domStyle, domConstruct) {

    "use strict";

    return declare("CameraWidgetForPhoneGap.widget.CameraWidgetForPhoneGap", [ _WidgetBase ], {

        buttonClass: "wx-mxwx-button-extra",
        buttonText: "activate camera",
        imageContainerClass: "wx-mxwx-imagecontainer-extra",
        imageWidth: 150,
        imageHeight: 150,
        imageLocation: "right",
        targetWidth: 150,
        targetHeight: 150,
        autoSaveEnabled: false,
        onchangemf: "",
        pictureSource: "camera",

        _contextObj: null,
        _imageUrl: null,
        _previewNode: null,

        postCreate: function() {
            domClass.add(this.domNode, "wx-CameraWidgetForPhoneGap");

            var elements = [ this._setupPreview(), this._setupButton() ];
            if (/below|right/.test(this.imageLocation)) {
                elements.reverse();
            }

            var alignment = "horizontal";
            if (/above|below/.test(this.imageLocation)) {
                alignment = "vertical";
            }

            elements.forEach(function(el) {
                domClass.add(el, "wx-CameraWidgetForPhoneGap-" + alignment);
                this.domNode.appendChild(el);
            }, this);

            this.listen("save", this._sendFile);
        },

        update: function(obj, callback) {
            if (obj) {
                this._contextObj = obj;
                this._loadData();
                this._resetSubscriptions();
                this._setPicture("");
            }

            if (callback) callback();
        },

        _setupButton: function() {
            var button = dom.create("button", {
                "type": "button",
                "class": "btn btn-primary wx-CameraWidgetForPhoneGap-button " + this.buttonClass
            }, this.buttonText);

            this.connect(button, "click", "_getPicture");
            return button;
        },

        _setupPreview: function() {
            this._previewNode = dom.create("div", {
                "class": "wx-CameraWidgetForPhoneGap-preview " + this.imageContainerClass
            });
            return this._previewNode;
        },

        _loadData: function() {
            if (this._contextObj) {
                if (!this._contextObj.inheritsFrom("System.FileDocument")) {
                    var span = dom.create("span", {
                            "class": "alert-danger"
                        },
                        "Entity '" + this._contextObj.getEntity() + "' does not inherit from 'System.FileDocument'");
                    domConstruct.empty(this.domNode);
                    this.domNode.appendChild(span);
                }
            }

        },

        _setPicture: function(url) {
            this._imageUrl = url;
            this._setThumbnail(url);
        },

        _setThumbnail: function(url) {
            var urlDisplay = url ? "" : "none",
                width = this.imageWidth || 100,
                height = this.imageHeight || 100,
                background = url ? "url(" + url + ")" : "none";

            domStyle.set(this._previewNode, {
                "background-image": background,
                "display": urlDisplay,
                "width": width + "px",
                "height": height + "px"
            });
        },

        _getPicture: function() {
            var self = this;

            if (!navigator.camera) {
                window.mx.ui.error("Unable to detect camera.");
                return;
            }

            var sourceType = (this.pictureSource == "camera") ?
                    Camera.PictureSourceType.CAMERA : Camera.PictureSourceType.PHOTOLIBRARY;
            var params = {
                quality: 50,
                destinationType: Camera.DestinationType.FILE_URL,
                correctOrientation: true,
                sourceType: sourceType
            };
            if (this.targetWidth !== 0) params.targetWidth = this.targetWidth;
            if (this.targetHeight !== 0) params.targetHeight = this.targetHeight;
            // TODO: get rid of temp image files
            navigator.camera.getPicture(success, error, params);

            function success(url) {
                if (self.autoSaveEnabled) {
                    self._autoSave(url);
                } else {
                    self._setPicture(url);
                }
            }

            function error(e) {
                if (typeof e.code !== "undefined") {
                    window.mx.ui.error("Retrieving image from camera failed with error code " + e.code);
                }
            }
        },

        _sendFile: function(callback) {
            var self = this;

            if (!this._imageUrl) {
                if (callback) callback();
                return;
            }

            var filename = /[^\/]*$/.exec(this._imageUrl)[0];
            if (window.mx.data.saveDocument && window.mx.data.saveDocument.length === 6) {
                var guid = this._contextObj.getGuid();
                window.resolveLocalFileSystemURL(this._imageUrl, function(fileEntry) {
                    fileEntry.file(function(blob) {
                        var fileReader = new FileReader();
                        fileReader.onload = function(e) {
                            window.mx.data.saveDocument(guid, filename, {}, new Blob([ e.target.result ]), success, error);
                        };

                        fileReader.onerror = function(e) {
                            error(e.target.error);
                        };

                        fileReader.readAsArrayBuffer(blob);
                    }, error);
                }, error);
            } else {
                // For Mendix versions < 6.3
                var options = new FileUploadOptions();
                options.fileKey = "mxdocument";
                options.fileName = filename;
                options.mimeType = "image/jpeg";
                options.useBrowserHttp = true;

                var url = mx.appUrl +
                    "file?guid=" + this._contextObj.getGuid() +
                    "&csrfToken=" + mx.session.getCSRFToken();

                var ft = new FileTransfer();
                ft.upload(this._imageUrl, url, success, error, options);
            }

            function success() {
                self._setPicture("");
                self._executeMicroflow();
                if (callback) callback();
            }

            function error(e) {
                window.mx.ui.error("Uploading image failed with error code " + e.code);
            }
        },

        _autoSave: function(url) {
            this._imageUrl = url;
            if (this._contextObj){
                 window.mx.data.save({
                     mxobj: this._contextObj,
                     callback: function(){
                        this._sendFile();
                     }
                }, this);
            }
        },

        _resetSubscriptions: function() {
            this.unsubscribeAll();

            if (this._contextObj) {
                this.subscribe({
                    guid: this._contextObj.getGuid(),
                    callback: function(guid) {
                        window.mx.data.get({
                            guid: guid,
                            callback: function(obj) {
                                this._contextObj = obj;
                                this._loadData();
                            }
                        }, this);
                    }
                });
            }
        },

        _executeMicroflow: function() {
            if (this.onchangemf && this._contextObj) {
                window.mx.data.action({
                    actionname: this.onchangemf,
                    applyto: "selection",
                    guids: [ this._contextObj.getGuid() ],
                    callback: function(objs) {
                        //ok
                    },
                    error: function(e) {
                        console.warn("Error running microflow: ", e);
                    }
                });
            }
        }
    });
});
<|endoftext|>


<commit_before>/* global describe, it, before, beforeEach, after, afterEach */
import {expect} from 'chai';

import PseudoFetch from '../../src/index.js';

describe('Basic usage integration', () => {
  it('should handle a simple get request, and give us some data back', () => {
    const server = PseudoFetch();
    server
      .get('/myroute')
      .send('Hello world');

    return expect(
      fetch('/myroute')
        .then(response => response.body)
    ).to.eventually.equal('Hello world');
  });
  it('should set a status code', () => {
    const server = PseudoFetch();
    server
      .get('/myroute')
      .status(201);

    return expect(
      fetch('/myroute')
        .then(response => response.status)
    ).to.eventually.equal(201);
  });
});<commit_msg>Added some more basicusage tests<commit_after>/* global describe, it */
import {expect} from 'chai';

import pseudoFetch from '../../src/index.js';

describe('Basic usage integration', () => {
  it('should handle a simple get request, and give us some data back', () => {
    const server = pseudoFetch();
    server
      .get('/myroute')
      .send('Hello world');

    return expect(
      fetch('/myroute')
        .then((response) => response.body)
    ).to.eventually.equal('Hello world');
  });
  it('should set a status code', () => {
    const server = pseudoFetch();
    server
      .get('/myroute')
      .status(201);

    return expect(
      fetch('/myroute')
        .then((response) => response.status)
    ).to.eventually.equal(201);
  });
  it('should handle only correct methods', () => {
    const server = pseudoFetch();
    server
      .post('/myroute')
      .send('test');

    return Promise.all([
      expect(
        fetch('/myroute', {method: 'POST'})
          .then((response) => response.status)
      ).to.eventually.equal(200),
      expect(
        fetch('/myroute', {method: 'GET'})
          .then((response) => response.status)
      ).to.eventually.equal(404),
    ]);
  });
  it('should be able to serve json objects', () => {
    const server = pseudoFetch();
    server
      .get('/myroute')
      .send({a: 2});

    expect(
      fetch('/myroute')
        .then((response) => response.json())
    ).to.eventually.deep.equal({a: 2});
  });
  it('should be able to serve json as strings', () => {
    const server = pseudoFetch();
    server
      .get('/myroute')
      .send('{"a": 2}');

    expect(
      fetch('/myroute')
        .then((response) => response.json())
    ).to.eventually.deep.equal({a: 2});
  });
});
<|endoftext|>


<commit_before>import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import { fillIn, find, triggerEvent } from 'ember-native-dom-helpers/test-support/helpers';

moduleForComponent('input-mask', 'Integration | Component | input-mask', {
  integration: true
});

test('regex with invalid value', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='regex'
    pattern='you_can_only_type_thi[s]+'}}`);
  fillIn('input', 'test');
  triggerEvent('input', 'blur');
  assert.equal(find('input').value, '');
  assert.equal(this.unmaskedValue, '');
});

test('regex with valid value', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='regex'
    pattern='you_can_only_type_thi[s]+'}}`);
  fillIn('input', 'you_can_only_type_this');
  triggerEvent('input', 'blur');
  assert.equal(find('input').value, 'you_can_only_type_this');
  assert.equal(this.unmaskedValue, 'you_can_only_type_this');
});

test('regex with another valid value', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='regex'
    pattern='you_can_only_type_thi[s]+'}}`);
  fillIn('input', 'you_can_only_type_thisssss');
  triggerEvent('input', 'blur');
  assert.equal(find('input').value, 'you_can_only_type_thisssss');
  assert.equal(this.unmaskedValue, 'you_can_only_type_thisssss');
});

test('showMaskOnHover=true (default) works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9-9+9'}}`);
  triggerEvent('input', 'mouseenter');
  assert.equal(find('input').value, '_-_+_');
  triggerEvent('input', 'mouseleave');
  assert.equal(find('input').value, '');
});

test('showMaskOnHover=false works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9-9+9'
    showMaskOnHover=false}}`);
  triggerEvent('input', 'mouseenter');
  assert.equal(find('input').value, '');
});

test('showMaskOnFocus=true (default) works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9-9+9'}}`);
  triggerEvent('input', 'focus');
  assert.equal(find('input').value, '_-_+_');
  triggerEvent('input', 'blur');
  assert.equal(find('input').value, '');
});

test('showMaskOnFocus=false works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9-9+9'
    showMaskOnFocus=false}}`);
  triggerEvent('input', 'focus');
  assert.equal(find('input').value, '');
});

test('clearIncomplete works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9-9+9'
    clearIncomplete=true}}`);
  fillIn('input', '6');
  triggerEvent('input', 'blur');
  assert.equal(find('input').value, '');
});

test('greedyMask=false (default) works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9[99]'}}`);
  triggerEvent('input', 'mouseenter');
  assert.equal(find('input').value, '_');
  fillIn('input', '66');
  triggerEvent('input', 'blur');
  triggerEvent('input', 'mouseenter');
  assert.equal(find('input').value, '66_');
});

test('greedyMask=true works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9[99]'
    greedyMask=true}}`);
  triggerEvent('input', 'mouseenter');
  assert.equal(find('input').value, '___');
});
<commit_msg>Remove invalid test cases (not supposed to work that way)<commit_after>import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import { fillIn, find, triggerEvent } from 'ember-native-dom-helpers/test-support/helpers';

moduleForComponent('input-mask', 'Integration | Component | input-mask', {
  integration: true
});

test('regex with invalid value', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='regex'
    pattern='you_can_only_type_thi[s]+'}}`);
  fillIn('input', 'test');
  triggerEvent('input', 'blur');
  assert.equal(find('input').value, '');
  assert.equal(this.unmaskedValue, '');
});

test('regex with valid value', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='regex'
    pattern='you_can_only_type_thi[s]+'}}`);
  fillIn('input', 'you_can_only_type_this');
  triggerEvent('input', 'blur');
  assert.equal(find('input').value, 'you_can_only_type_this');
  assert.equal(this.unmaskedValue, 'you_can_only_type_this');
});

test('regex with another valid value', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='regex'
    pattern='you_can_only_type_thi[s]+'}}`);
  fillIn('input', 'you_can_only_type_thisssss');
  triggerEvent('input', 'blur');
  assert.equal(find('input').value, 'you_can_only_type_thisssss');
  assert.equal(this.unmaskedValue, 'you_can_only_type_thisssss');
});

test('showMaskOnHover=true (default) works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9-9+9'}}`);
  triggerEvent('input', 'mouseenter');
  assert.equal(find('input').value, '_-_+_');
  triggerEvent('input', 'mouseleave');
  assert.equal(find('input').value, '');
});

test('showMaskOnHover=false works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9-9+9'
    showMaskOnHover=false}}`);
  triggerEvent('input', 'mouseenter');
  assert.equal(find('input').value, '');
});

test('showMaskOnFocus=true (default) works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9-9+9'}}`);
  triggerEvent('input', 'focus');
  assert.equal(find('input').value, '_-_+_');
});

test('showMaskOnFocus=false works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9-9+9'
    showMaskOnFocus=false}}`);
  triggerEvent('input', 'focus');
  assert.equal(find('input').value, '');
});

test('clearIncomplete works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9-9+9'
    clearIncomplete=true}}`);
  fillIn('input', '6');
  triggerEvent('input', 'blur');
  assert.equal(find('input').value, '');
});

test('greedyMask=false (default) works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9[99]'}}`);
  triggerEvent('input', 'mouseenter');
  assert.equal(find('input').value, '_');
  fillIn('input', '66');
  triggerEvent('input', 'blur');
  triggerEvent('input', 'mouseenter');
  assert.equal(find('input').value, '66_');
});

test('greedyMask=true works', function(assert) {
  this.render(hbs`{{input-mask unmaskedValue=unmaskedValue mask='9[99]'
    greedyMask=true}}`);
  triggerEvent('input', 'mouseenter');
  assert.equal(find('input').value, '___');
});
<|endoftext|>