<commit_before>import React from 'react'
import MapView from 'react-native-maps'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { Platform, View, Dimensions } from 'react-native'
import _ from 'lodash'
import * as actions from '../../store/reducer/business'
import { maxCollapsedHeight, SEARCH_BAR_MARGIN, SEARCH_BAR_HEIGHT } from './SearchTabStyle'
import platform from '../../util/Platforms'
import { horizontalAbsolutePosition } from '../../util/StyleUtils'

const MAP_PAN_DEBOUNCE_DURATION = 150
const BOTTOM_OFFSET = -25

const markerImage = {
  [platform.IOS]: require('./assets/Marker.png'),
  [platform.ANDROID]: require('./assets/Android_marker.png')
}

const selectedMarkerImage = {
  [platform.IOS]: require('./assets/selected_trader.png'),
  [platform.ANDROID]: require('./assets/Android_selected_marker.png')
}

const mapTopOffset = -1 * maxCollapsedHeight + SEARCH_BAR_MARGIN + SEARCH_BAR_HEIGHT + BOTTOM_OFFSET
const mapHeight =  Dimensions.get('window').height - mapTopOffset - BOTTOM_OFFSET
const mapWidth = Dimensions.get('window').width

const style = {
  container: {
    ...horizontalAbsolutePosition(0, 0),
    top: mapTopOffset,
    height: mapHeight,
  },
  map: {
    height: mapHeight,
    width: mapWidth,
    position: 'absolute'
  },
  loadingOverlay: {
    height: mapHeight,
    width: mapWidth,
    backgroundColor: 'white'
  }
}

class BackgroundMap extends React.Component {
  constructor() {
    super()
    this.ignoreLocationUpdates = false
    this.state = ({ loading: true })
  }

  componentDidMount() {
    // To prevent the user seeing the centre of the earth when opening the app
    setTimeout(() => this.setState({ loading: false }), 500)
  }

  updateViewport(...args) {
    this.ignoreLocationUpdates = true
    this.props.updateMapViewport(...args)
  }

  isSelected(business) {
    return this.props.selectedBusinessId === business.id
  }

  renderMarker(business) {
    // we have to separate out the behaviour by platform for:
    // * behaviour - https://github.com/airbnb/react-native-maps#custom-callouts
    //     android moves viewspace when the marker is selected, ios shouldn't.
    // * Image placing:
    //     https://github.com/airbnb/react-native-maps/blob/master/docs/marker.md
    //       ios - centerOffset: By default, the center point of an annotation view is placed at the coordinate point of the associated annotation.
    //       android - anchor: manually center

      const marker = this.isSelected(business) ? selectedMarkerImage : markerImage

      const markerProps = Platform.select({
        android: {
          onPress: () => {
            this.props.updateMapViewportAndSelectClosestTrader(business.address.location)
          },
          anchor: {x: 0.5, y: 0.5},  // center image on location.
          image: marker[platform.ANDROID]
        },
        ios: {
          onPress: () => this.props.selectBusiness(business.id),
          image: marker[platform.IOS]
        }
      })

      // custom image file
      return <MapView.Marker
          key={business.id}
          coordinate={business.address.location}
          {...markerProps}
      />
  }

  render() {
    // Prevent the map from 'jumping back' to the last processed location
    const region = this.ignoreLocationUpdates ? undefined : this.props.mapViewport
    let markerArray = undefined
    if (this.props.businessList) {
      markerArray = this.props.businessList.filter(b => b.address)
        .map(this.renderMarker.bind(this))
    }

    return (
      <View style={style.container}>
        <MapView style={style.map}
            region={region}
            showsPointsOfInterest={false}
            showsUserLocation={true}
            showsCompass={false}
            rotateEnabled={false}
            pitchEnabled={false}
            onRegionChange={_.debounce(this.updateViewport.bind(this), MAP_PAN_DEBOUNCE_DURATION)}
            loadingEnabled={true}>
          {markerArray}
        </MapView>
        {this.state.loading
          ? <View style={style.loadingOverlay}/>
          : undefined}
      </View>
    )
  }
}

const mapStateToProps = (state) => ({
  ...state.business,
})

const mapDispatchToProps = (dispatch) =>
  bindActionCreators(actions, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(BackgroundMap)
<commit_msg>larger-marker-ios<commit_after>import React from 'react'
import MapView from 'react-native-maps'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { Platform, View, Dimensions } from 'react-native'
import _ from 'lodash'
import * as actions from '../../store/reducer/business'
import { maxCollapsedHeight, SEARCH_BAR_MARGIN, SEARCH_BAR_HEIGHT } from './SearchTabStyle'
import platform from '../../util/Platforms'
import { horizontalAbsolutePosition } from '../../util/StyleUtils'

const MAP_PAN_DEBOUNCE_DURATION = 150
const BOTTOM_OFFSET = -25

const markerImage = {
  [platform.IOS]: require('./assets/Marker_alt.png'),
  [platform.ANDROID]: require('./assets/Android_marker.png')
}

const selectedMarkerImage = {
  [platform.IOS]: require('./assets/selected_trader.png'),
  [platform.ANDROID]: require('./assets/Android_selected_marker.png')
}

const mapTopOffset = -1 * maxCollapsedHeight + SEARCH_BAR_MARGIN + SEARCH_BAR_HEIGHT + BOTTOM_OFFSET
const mapHeight =  Dimensions.get('window').height - mapTopOffset - BOTTOM_OFFSET
const mapWidth = Dimensions.get('window').width

const style = {
  container: {
    ...horizontalAbsolutePosition(0, 0),
    top: mapTopOffset,
    height: mapHeight,
  },
  map: {
    height: mapHeight,
    width: mapWidth,
    position: 'absolute'
  },
  loadingOverlay: {
    height: mapHeight,
    width: mapWidth,
    backgroundColor: 'white'
  }
}

class BackgroundMap extends React.Component {
  constructor() {
    super()
    this.ignoreLocationUpdates = false
    this.state = ({ loading: true })
  }

  componentDidMount() {
    // To prevent the user seeing the centre of the earth when opening the app
    setTimeout(() => this.setState({ loading: false }), 500)
  }

  updateViewport(...args) {
    this.ignoreLocationUpdates = true
    this.props.updateMapViewport(...args)
  }

  isSelected(business) {
    return this.props.selectedBusinessId === business.id
  }

  renderMarker(business) {
    // we have to separate out the behaviour by platform for:
    // * behaviour - https://github.com/airbnb/react-native-maps#custom-callouts
    //     android moves viewspace when the marker is selected, ios shouldn't.
    // * Image placing:
    //     https://github.com/airbnb/react-native-maps/blob/master/docs/marker.md
    //       ios - centerOffset: By default, the center point of an annotation view is placed at the coordinate point of the associated annotation.
    //       android - anchor: manually center

      const marker = this.isSelected(business) ? selectedMarkerImage : markerImage

      const markerProps = Platform.select({
        android: {
          onPress: () => {
            this.props.updateMapViewportAndSelectClosestTrader(business.address.location)
          },
          anchor: {x: 0.5, y: 0.5},  // center image on location.
          image: marker[platform.ANDROID]
        },
        ios: {
          onPress: () => this.props.selectBusiness(business.id),
          image: marker[platform.IOS]
        }
      })

      // custom image file
      return <MapView.Marker
          key={business.id}
          coordinate={business.address.location}
          {...markerProps}
      />
  }

  render() {
    // Prevent the map from 'jumping back' to the last processed location
    const region = this.ignoreLocationUpdates ? undefined : this.props.mapViewport
    let markerArray = undefined
    if (this.props.businessList) {
      markerArray = this.props.businessList.filter(b => b.address)
        .map(this.renderMarker.bind(this))
    }

    return (
      <View style={style.container}>
        <MapView style={style.map}
            region={region}
            showsPointsOfInterest={false}
            showsUserLocation={true}
            showsCompass={false}
            rotateEnabled={false}
            pitchEnabled={false}
            onRegionChange={_.debounce(this.updateViewport.bind(this), MAP_PAN_DEBOUNCE_DURATION)}
            loadingEnabled={true}>
          {markerArray}
        </MapView>
        {this.state.loading
          ? <View style={style.loadingOverlay}/>
          : undefined}
      </View>
    )
  }
}

const mapStateToProps = (state) => ({
  ...state.business,
})

const mapDispatchToProps = (dispatch) =>
  bindActionCreators(actions, dispatch)

export default connect(mapStateToProps, mapDispatchToProps)(BackgroundMap)
<|endoftext|>


<commit_before>import {customAttribute, bindable} from 'aurelia-templating';
import {inject} from 'aurelia-dependency-injection';
import {Router} from 'aurelia-router';
import {DOM} from 'aurelia-pal';
import * as LogManager from 'aurelia-logging';

const logger = LogManager.getLogger('route-href');

@customAttribute('route-href')
@bindable({name: 'route', changeHandler: 'processChange'})
@bindable({name: 'params', changeHandler: 'processChange'})
@bindable({name: 'attribute', defaultValue: 'href'})
@inject(Router, DOM.Element)
export class RouteHref {
  constructor(router, element) {
    this.router = router;
    this.element = element;
  }

  bind() {
    this.isActive = true;
    this.processChange();
  }

  unbind() {
    this.isActive = false;
  }

  attributeChanged(value, previous) {
    if (previous) {
      this.element.removeAttribute(previous);
    }

    this.processChange();
  }

  processChange() {
    return this.router.ensureConfigured()
      .then(() => {
        if (!this.isActive) {
          return null;
        }

        let href = this.router.generate(this.route, this.params);

        if (this.element.au.controller) {
          this.element.au.controller.viewModel.href = href;
        } else {
          this.element.setAttribute(this.attribute, href);
        }

        return null;
      }).catch(reason => {
        logger.error(reason);
      });
  }
}
<commit_msg>Adds space to make the commit CLA signed and trigger tests<commit_after>import {customAttribute, bindable} from 'aurelia-templating';
import {inject} from 'aurelia-dependency-injection';
import {Router} from 'aurelia-router';
import {DOM} from 'aurelia-pal';
import * as LogManager from 'aurelia-logging';

const logger = LogManager.getLogger('route-href');

@customAttribute('route-href')
@bindable({name: 'route', changeHandler: 'processChange'})
@bindable({name: 'params', changeHandler: 'processChange'})
@bindable({name: 'attribute', defaultValue: 'href'})
@inject(Router, DOM.Element)
export class RouteHref {
  constructor(router, element) {
    this.router = router;
    this.element = element;
  }

  bind() {
    this.isActive = true;
    this.processChange();
  }

  unbind() {
    this.isActive = false;
  }

  attributeChanged(value, previous) {
    if (previous) {
      this.element.removeAttribute(previous);
    }

    this.processChange();
  }

  processChange() {
    return this.router.ensureConfigured()
      .then(() => {
        if (!this.isActive) {
          return null;
        }

        let href = this.router.generate(this.route, this.params);

        if (this.element.au.controller) {
          this.element.au.controller.viewModel.href = href;
        } else {
          this.element.setAttribute(this.attribute, href);
        }

        return null;
      }).catch(reason => {
        logger.error(reason);
      });
  }
}
<|endoftext|>


<commit_before>!function(e){function t(n){if(r[n])return r[n].exports;var u=r[n]={exports:{},id:n,loaded:!1};return e[n].call(u.exports,u,u.exports,t),u.loaded=!0,u.exports}var r={};return t.m=e,t.c=r,t.p="",t(0)}([function(e,t,r){r(5),e.exports=r(2)},function(e,t){e.exports=require("express")},function(e,t,r){(function(e){"use strict";function t(e){return e&&e.__esModule?e:{"default":e}}var n=r(1),u=t(n),o=r(12),s=t(o),i=r(6),c=t(i),a=r(7),f=t(a),l=r(9),d=t(l),p=r(11),v=t(p),m=r(13),x=t(m),b=r(3),y=t(b),g=r(4),h=t(g);r(8).config();var j=(0,u["default"])(),_=process.env.PORT||3e3,q=d["default"].createWriteStream(s["default"].join(e,"access.log"),{flags:"a"});j.use((0,v["default"])("combined",{stream:q})),j.use((0,f["default"])()),j.use(c["default"].json()),j.use(c["default"].urlencoded({extended:!1})),j.use((0,x["default"])(s["default"].resolve(e,"../favicon.ico"))),j.use(u["default"]["static"](s["default"].join(e,"../build"))),j.use("/feedback",h["default"]),j.use(y["default"]),j.get("/*",function(t,r){r.sendFile(s["default"].resolve(e,"../build/index.html"))}),j.listen(_,process.env.SERVER_IP,function(){console.log("Listening on port "+_+"...")})}).call(t,"server")},function(e,t){"use strict";function r(e){return{success:!1,errorType:e.type,error:e}}function n(e,t,n,u){n.status(200).json(r(e)),u()}Object.defineProperty(t,"__esModule",{value:!0}),t["default"]=n},function(e,t,r){"use strict";function n(e){return e&&e.__esModule?e:{"default":e}}function u(e){return function(){var t=e.apply(this,arguments);return new Promise(function(e,r){function n(u,o){try{var s=t[u](o),i=s.value}catch(c){return void r(c)}return s.done?void e(i):Promise.resolve(i).then(function(e){return n("next",e)},function(e){return n("throw",e)})}return n("next")})}}function o(e,t){return e?{success:!0,message:"Email sent successfully, thank you for your feedback!",subject:t}:{success:!1,type:"emailError",message:"There was an issue sending your email. This is a problem with the website.",subject:t}}Object.defineProperty(t,"__esModule",{value:!0});var s=r(1),i=n(s),c=r(10),a=n(c),f=i["default"].Router(),l=process.env.MAILGUN_API_KEY,d=process.env.MAILGUN_DOMAIN,p=process.env.EMAIL;f.route("/").post(function(){var e=u(regeneratorRuntime.mark(function t(e,r,n){var u,s,i,c,f,v,m;return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:u=e.body,s=u.subject,i=u.comments,c=u.userEmail,f=u.name,v=new a["default"]({apiKey:l,domain:d}),m={from:c,to:p,subject:"From - "+f,text:i},v.messages().send(m,function(e,t){return e?n(o(!1)):r.json(o(!0,s))});case 8:case"end":return t.stop()}},t,this)}));return function(t,r,n){return e.apply(this,arguments)}}()),t["default"]=f},function(e,t){e.exports=require("babel-polyfill")},function(e,t){e.exports=require("body-parser")},function(e,t){e.exports=require("compression")},function(e,t){e.exports=require("dotenv")},function(e,t){e.exports=require("fs")},function(e,t){e.exports=require("mailgun-js")},function(e,t){e.exports=require("morgan")},function(e,t){e.exports=require("path")},function(e,t){e.exports=require("serve-favicon")}]);<commit_msg>v1.4.2<commit_after>!function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){n(5),e.exports=n(2)},function(e,t){e.exports=require("express")},function(e,t,n){(function(e){"use strict";function t(e){return e&&e.__esModule?e:{"default":e}}var r=n(1),o=t(r),u=n(12),s=t(u),i=n(6),c=t(i),a=n(7),f=t(a),l=n(9),d=t(l),p=n(11),m=t(p),v=n(13),x=t(v),g=n(3),b=t(g),y=n(4),h=t(y);n(8).config();var j=(0,o["default"])(),_=process.env.PORT||3e3,q=d["default"].createWriteStream(s["default"].join(e,"access.log"),{flags:"a"});j.use((0,m["default"])("combined",{stream:q})),j.use((0,f["default"])()),j.use(c["default"].json()),j.use(c["default"].urlencoded({extended:!1})),j.use((0,x["default"])(s["default"].resolve(e,"../favicon.ico"))),j.use(o["default"]["static"](s["default"].join(e,"../build"))),j.use("/feedback",h["default"]),j.use(b["default"]),j.get("/*",function(t,n){n.sendFile(s["default"].resolve(e,"../build/index.html"))}),j.listen(_,process.env.SERVER_IP,function(){console.log("Listening on port "+_+"...")})}).call(t,"server")},function(e,t){"use strict";function n(e){return{success:!1,errorType:e.type,error:e}}function r(e,t,r,o){r.status(200).json(n(e)),o()}Object.defineProperty(t,"__esModule",{value:!0}),t["default"]=r},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function o(e){return function(){var t=e.apply(this,arguments);return new Promise(function(e,n){function r(o,u){try{var s=t[o](u),i=s.value}catch(c){return void n(c)}return s.done?void e(i):Promise.resolve(i).then(function(e){return r("next",e)},function(e){return r("throw",e)})}return r("next")})}}function u(e,t){return e?{success:!0,message:"Email sent successfully, thank you for your feedback!",subject:t}:{success:!1,type:"emailError",message:"There was an issue sending your email. This is a problem with the website.",subject:t}}Object.defineProperty(t,"__esModule",{value:!0});var s=n(1),i=r(s),c=n(10),a=r(c),f=i["default"].Router(),l=process.env.MAILGUN_API_KEY,d=process.env.MAILGUN_DOMAIN,p=process.env.EMAIL;f.route("/").post(function(){var e=o(regeneratorRuntime.mark(function t(e,n,r){var o,s,i,c,f,m,v;return regeneratorRuntime.wrap(function(t){for(;;)switch(t.prev=t.next){case 0:o=e.body,s=o.subject,i=o.comments,c=o.userEmail,f=o.name,m=new a["default"]({apiKey:l,domain:d}),v={from:c,to:p,subject:"From - "+f,text:i},m.messages().send(v,function(e,t){return e?(console.log("email not sent"),r(u(!1))):(console.log(t),console.log("maeesage sent"),n.json(u(!0,s)))});case 8:case"end":return t.stop()}},t,this)}));return function(t,n,r){return e.apply(this,arguments)}}()),t["default"]=f},function(e,t){e.exports=require("babel-polyfill")},function(e,t){e.exports=require("body-parser")},function(e,t){e.exports=require("compression")},function(e,t){e.exports=require("dotenv")},function(e,t){e.exports=require("fs")},function(e,t){e.exports=require("mailgun-js")},function(e,t){e.exports=require("morgan")},function(e,t){e.exports=require("path")},function(e,t){e.exports=require("serve-favicon")}]);<|endoftext|>


<commit_before>'use strict';

/**
 * Parameters for rendering
 */
let mixfile     = 'deep_60s.mp3';
let background  = '2016-aug-deep.jpg';
let datascript  = '2016-aug-deep.js';
let duration    = 3600; // set max duration for 1 minute (equal to audio length)

let aepxfile  = 'nm05ae12.aepx';
let audio     = 'mp3';

/**
 * Settings for renderer
 * DONT FORGET TO CHANGE aebinary ACCORDING TO YOUR SYSTEM
 * On Windows might look like: 'C:\\Program Files\\Adobe\\After Effects CC\\aerender.exe'
 */
const aebinary  = '/Applications/Adobe After Effects CC/aerender';
const port      = 23234;

/**
 * Dependencies
 */
const http      = require('http');
const url       = require('url');
const path      = require('path');
const fs        = require('fs');

const Project   = require('nexrender').Project;
const renderer  = require('nexrender').renderer;

/**
 * HTTP server
 */
let server = http.createServer((req, res) => {

    let uri         = url.parse(req.url).pathname;
    let filename    = path.join(process.cwd(), uri);

    fs.exists(filename, (exists) => {
        if(!exists) {
            res.writeHead(404, {"Content-Type": "text/plain"});
            res.write("404 Not Found\n");

            return res.end();
        }

        fs.readFile(filename, "binary", function(err, file) {
            if(err) {
                res.writeHead(500, {"Content-Type": "text/plain"});
                res.write(err + "\n");
                return res.end();
            }

            // send 200
            res.writeHead(200);
            res.write(file, "binary");
            return res.end();
        });
    });
});

/**
 * Renderer
 */
server.listen(port, () => {

    console.log('Started local static server at port:', port);

    // addtional info about configuring project can be found at:
    // https://github.com/Inlife/nexrender/wiki/Project-model
    let project = new Project({
        "template": "project.aepx",
        "composition": "main",
        "type": "default",
        "settings": {
            // dont forget to setup the right output module; info:
            // https://helpx.adobe.com/after-effects/using/basics-rendering-exporting.html#output_modules_and_output_module_settings
            "outputModule": "h264",
            "startFrame": 0,
            "endFrame": duration
        },
        "assets": [
            { "type": "project", "name": "project.aepx",    "src": `http://localhost:${port}/assets/${aepxfile}`},
            { "type": "image",   "name": "background.jpg",  "src": `http://localhost:${port}/assets/${background}`, "filters": [ {"name":"cover", "params": [1280, 720] }] },
            { "type": "image",   "name": "nm.png",          "src": `http://localhost:${port}/assets/nm.png` },
            { "type": "audio",   "name": `audio.${audio}`,  "src": `http://localhost:${port}/assets/${mixfile}` },
            { "type": "script",  "name": "data.js",         "src": `http://localhost:${port}/assets/${datascript}` }
        ]
    });

    console.log(project);

    // start rendering
    renderer.render(aebinary, project).then(() => {
        // success
        server.close();
        console.log('rendering finished');
    }).catch((err) => {
        // error
        console.err(err);
        server.close();
    });

});
<commit_msg>fix: use console.error instead of console.err<commit_after>'use strict';

/**
 * Parameters for rendering
 */
let mixfile     = 'deep_60s.mp3';
let background  = '2016-aug-deep.jpg';
let datascript  = '2016-aug-deep.js';
let duration    = 3600; // set max duration for 1 minute (equal to audio length)

let aepxfile  = 'nm05ae12.aepx';
let audio     = 'mp3';

/**
 * Settings for renderer
 * DONT FORGET TO CHANGE aebinary ACCORDING TO YOUR SYSTEM
 * On Windows might look like: 'C:\\Program Files\\Adobe\\After Effects CC\\aerender.exe'
 */
const aebinary  = '/Applications/Adobe After Effects CC/aerender';
const port      = 23234;

/**
 * Dependencies
 */
const http      = require('http');
const url       = require('url');
const path      = require('path');
const fs        = require('fs');

const Project   = require('nexrender').Project;
const renderer  = require('nexrender').renderer;

/**
 * HTTP server
 */
let server = http.createServer((req, res) => {

    let uri         = url.parse(req.url).pathname;
    let filename    = path.join(process.cwd(), uri);

    fs.exists(filename, (exists) => {
        if(!exists) {
            res.writeHead(404, {"Content-Type": "text/plain"});
            res.write("404 Not Found\n");

            return res.end();
        }

        fs.readFile(filename, "binary", function(err, file) {
            if(err) {
                res.writeHead(500, {"Content-Type": "text/plain"});
                res.write(err + "\n");
                return res.end();
            }

            // send 200
            res.writeHead(200);
            res.write(file, "binary");
            return res.end();
        });
    });
});

/**
 * Renderer
 */
server.listen(port, () => {

    console.log('Started local static server at port:', port);

    // addtional info about configuring project can be found at:
    // https://github.com/Inlife/nexrender/wiki/Project-model
    let project = new Project({
        "template": "project.aepx",
        "composition": "main",
        "type": "default",
        "settings": {
            // dont forget to setup the right output module; info:
            // https://helpx.adobe.com/after-effects/using/basics-rendering-exporting.html#output_modules_and_output_module_settings
            "outputModule": "h264",
            "startFrame": 0,
            "endFrame": duration
        },
        "assets": [
            { "type": "project", "name": "project.aepx",    "src": `http://localhost:${port}/assets/${aepxfile}`},
            { "type": "image",   "name": "background.jpg",  "src": `http://localhost:${port}/assets/${background}`, "filters": [ {"name":"cover", "params": [1280, 720] }] },
            { "type": "image",   "name": "nm.png",          "src": `http://localhost:${port}/assets/nm.png` },
            { "type": "audio",   "name": `audio.${audio}`,  "src": `http://localhost:${port}/assets/${mixfile}` },
            { "type": "script",  "name": "data.js",         "src": `http://localhost:${port}/assets/${datascript}` }
        ]
    });

    console.log(project);

    // start rendering
    renderer.render(aebinary, project).then(() => {
        // success
        server.close();
        console.log('rendering finished');
    }).catch((err) => {
        // error
        console.error(err);
        server.close();
    });

});
<|endoftext|>


<commit_before>define(["tuvero", "lib/extend", "ui/fileloadcontroller", "ui/toast", "ui/strings",
    "ui/state", "ui/playermodel", "ui/teammodel", "presets"
  ],
  function (tuvero, extend, FileLoadController, Toast, Strings,
    State, PlayerModel, TeamModel, Presets) {

    function TeamsFileLoadController($button) {
      TeamsFileLoadController.superconstructor.call(this, $button);
    }
    extend(TeamsFileLoadController, FileLoadController);

    TeamsFileLoadController.prototype.readFile = TeamsFileLoadController.load;
    TeamsFileLoadController.prototype.unreadFile = function () {};

    TeamsFileLoadController.parseCSVString = tuvero.io.csv.read;
    TeamsFileLoadController.parseDPVString = tuvero.io.dpv.import.csv;

    function dpv2player(dpv) {
      var name, player;

      name = (dpv.Vorname + " " + dpv.Name) || dpv.SpielerID || dpv.LizNr;
      player = new PlayerModel(name);

      player.club = dpv.Verein;
      player.license = dpv.LizNr;
      player.firstname = dpv.Vorname;
      player.lastname = dpv.Name;

      return player;
    }

    function dpv2team(dpv) {
      var team = new TeamModel(dpv.Spieler.map(dpv2player), dpv.Teamnummer);

      team.number = dpv.Teamnummer;
      team.alias = dpv.Pseudonym;
      team.club = dpv["Anmeldender Verein"];
      team.rankingpoints = Number(dpv.RLpunkteTeam);

      return team;
    }

    function teamsizeFromTeams(teams) {
      return Math.min.apply(undefined, teams.map(function (team) {
        return team.length;
      }));
    }

    /**
     * Read teamsize from teams array
     *
     * @param {[[string]]} teams
     *          a 2d teams array
     * @returns {number} the team size, or 0 on failure.
     */
    TeamsFileLoadController.guessCSVTeamsize = function (teams) {
      var teamsizes, teamsize;

      if (teams.length === 0) {
        return 0;
      }

      teamsizes = teams.map(function (team) {
        return team.length;
      });

      teamsize = teamsizes[0];

      if (teamsizes.some(function (size) {
          return size !== teamsize;
        })) {
        return 0;
      }

      return teamsize;
    };

    /**
     * load the teams from a csv string and write them to State
     *
     * @param {string} input A (multiline) csv string
     * @returns {boolean} true on success, false otherwise
     */
    TeamsFileLoadController.load = function (input) {
      var teams, teamsize;

      input = tuvero.io.utf8.latin2utf8(input);
      if (State.teams.length !== 0) {
        new Toast(Strings.teamsnotempty);
        return false;
      }

      teams = TeamsFileLoadController.loadDPV(input);
      if (!teams) {
        teams = TeamsFileLoadController.loadCSV(input);
      }

      if (!teams) {
        new Toast(Strings.invalidfileformat);
        return false;
      }

      teamsize = teamsizeFromTeams(teams);

      State.teamsize.set(teamsize);

      teams.forEach(function (team) {
        State.teams.push(team);
      });

      new Toast(Strings.loaded);

      return true;
    };

    TeamsFileLoadController.loadCSV = function (input) {
      var teams, teamsize;

      teams = TeamsFileLoadController.parseCSVString(input);
      teamsize = TeamsFileLoadController.guessCSVTeamsize(teams);

      // validate team size
      if (teamsize >= Presets.registration.minteamsize &&
        teamsize <= Presets.registration.maxteamsize) {

        // create TeamModels
        return teams.map(function (names) {
          var players = names.map(function (name) {
            return new PlayerModel(name);
          });

          return new TeamModel(players);
        });
      }

      return undefined;
    };

    TeamsFileLoadController.loadDPV = function (input) {
      var teams;

      try {
        teams = TeamsFileLoadController.parseDPVString(input);
        if (teams.length > 0) {
          return teams.map(dpv2team);
        }
      } catch (e) {}

      return undefined;
    };

    return TeamsFileLoadController;
  });<commit_msg>Distinguish between different CSV formats<commit_after>define(["tuvero", "lib/extend", "ui/fileloadcontroller", "ui/toast", "ui/strings",
    "ui/state", "ui/playermodel", "ui/teammodel", "presets"
  ],
  function (tuvero, extend, FileLoadController, Toast, Strings,
    State, PlayerModel, TeamModel, Presets) {

    function TeamsFileLoadController($button) {
      TeamsFileLoadController.superconstructor.call(this, $button);
    }
    extend(TeamsFileLoadController, FileLoadController);

    TeamsFileLoadController.prototype.readFile = TeamsFileLoadController.load;
    TeamsFileLoadController.prototype.unreadFile = function () {};

    TeamsFileLoadController.parseCSVString = tuvero.io.csv.read;
    TeamsFileLoadController.parseDPVString = tuvero.io.dpv.import.csv;

    function dpv2player(dpv) {
      var name, player;

      name = (dpv.Vorname + " " + dpv.Name) || dpv.SpielerID || dpv.LizNr;
      player = new PlayerModel(name);

      player.club = dpv.Verein;
      player.license = dpv.LizNr;
      player.firstname = dpv.Vorname;
      player.lastname = dpv.Name;

      return player;
    }

    function dpv2team(dpv) {
      var team = new TeamModel(dpv.Spieler.map(dpv2player), dpv.Teamnummer);

      team.number = dpv.Teamnummer;
      team.alias = dpv.Pseudonym;
      team.club = dpv["Anmeldender Verein"];
      team.rankingpoints = Number(dpv.RLpunkteTeam);

      return team;
    }

    function teamsizeFromTeams(teams) {
      return Math.min.apply(undefined, teams.map(function (team) {
        return team.length;
      }));
    }

    TeamsFileLoadController.guessCSVType = function (teams) {
      if (teams.length === 0) {
        return "empty";
      }

      if (teams[0].every(function (field) {
          return ["No.", "Name", "Team", "Spieler"].indexOf(field) !== -1;
        })) {
        return "tuvero_teams_export";
      }

      return "pure_csv";
    };

    /**
     * Read teamsize from teams array
     *
     * @param {[[string]]} teams
     *          a 2d teams array
     * @returns {number} the team size, or 0 on failure.
     */
    TeamsFileLoadController.guessCSVTeamsize = function (teams) {
      var teamsizes, teamsize;

      if (teams.length === 0) {
        return 0;
      }

      teamsizes = teams.map(function (team) {
        return team.length;
      });

      teamsize = teamsizes[0];

      if (teamsizes.some(function (size) {
          return size !== teamsize;
        })) {
        return 0;
      }

      return teamsize;
    };

    /**
     * load the teams from a csv string and write them to State
     *
     * @param {string} input A (multiline) csv string
     * @returns {boolean} true on success, false otherwise
     */
    TeamsFileLoadController.load = function (input) {
      var teams, teamsize;

      input = tuvero.io.utf8.latin2utf8(input);
      if (State.teams.length !== 0) {
        new Toast(Strings.teamsnotempty);
        return false;
      }

      teams = TeamsFileLoadController.loadDPV(input);
      if (!teams) {
        teams = TeamsFileLoadController.loadCSV(input);
      }

      if (!teams) {
        new Toast(Strings.invalidfileformat);
        return false;
      }

      teamsize = teamsizeFromTeams(teams);

      State.teamsize.set(teamsize);

      teams.forEach(function (team) {
        State.teams.push(team);
      });

      new Toast(Strings.loaded);

      return true;
    };

    TeamsFileLoadController.loadCSV = function (input) {
      var teams, type;

      teams = TeamsFileLoadController.parseCSVString(input);

      type = TeamsFileLoadController.guessCSVType(teams);
      switch (type) {
        case "tuvero_teams_export":
          return TeamsFileLoadController.loadTuveroTeamExport(teams);
        case "pure_csv":
        case "empty":
          return TeamsFileLoadController.loadPureCSV(teams);
        default:
          console.error("unknown csv type: " + type);
          return TeamsFileLoadController.loadPureCSV(teams);
      }
    };

    TeamsFileLoadController.loadTuveroTeamExport = function (teams) {
      var teamsize, header, hasTeamNumber;

      header = teams.shift();
      hasTeamNumber = header[0] === "No.";
      teamsize = TeamsFileLoadController.guessCSVTeamsize(teams);

      if (hasTeamNumber) {
        teamsize -= 1;
      }

      if (teamsize >= Presets.registration.minteamsize &&
        teamsize <= Presets.registration.maxteamsize) {

        // create TeamModels
        return teams.map(function (names) {
          var teamNumber, team;

          if (hasTeamNumber) {
            teamNumber = names.shift();
          }
          var players = names.map(function (name) {
            return new PlayerModel(name);
          });

          team = new TeamModel(players);
          if (hasTeamNumber) {
            TeamModel.number = teamNumber;
          }
          return team;
        });
      } else {
        // TODO handle failure gracefully
      }

      return undefined;
    };

    TeamsFileLoadController.loadPureCSV = function (teams) {
      var teamsize = TeamsFileLoadController.guessCSVTeamsize(teams);

      // validate team size
      if (teamsize >= Presets.registration.minteamsize &&
        teamsize <= Presets.registration.maxteamsize) {

        // create TeamModels
        return teams.map(function (names) {
          var players = names.map(function (name) {
            return new PlayerModel(name);
          });

          return new TeamModel(players);
        });
      } else {
        // TODO handle failure gracefully
      }

      return undefined;
    };

    TeamsFileLoadController.loadDPV = function (input) {
      var teams;

      try {
        teams = TeamsFileLoadController.parseDPVString(input);
        if (teams.length > 0) {
          return teams.map(dpv2team);
        }
      } catch (e) {}

      return undefined;
    };

    return TeamsFileLoadController;
  });<|endoftext|>


<commit_before>/**
 * @ngdoc service
 * @name umbraco.services.clipboardService
 *
 * @requires notificationsService
 * @requires eventsService
 *
 * @description
 * Service to handle clipboard in general across the application. Responsible for handling the data both storing and retrive.
 * The service has a set way for defining a data-set by a entryType and alias, which later will be used to retrive the posible entries for a paste scenario.
 *
 */
function clipboardService(notificationsService, eventsService) {

    var STORAGE_KEY = "umbClipboardService";

    var supportsLocalStorage = function () {
        var test = "test";

        try {
            window.localStorage.setItem(test, test);
            window.localStorage.removeItem(test);
            return true;
        } catch (e) {
            return false;
        }

        return false;
    }

    var retriveStorage = function() {
        if (supportsLocalStorage === false) {
            return null;
        }

        var dataJSON;
        var dataString = window.localStorage.getItem(STORAGE_KEY);
        if (dataString != null) {
            dataJSON = JSON.parse(dataString);
        }

        if(dataJSON == null) {
            dataJSON = new Object();
        }

        if(dataJSON.entries === undefined) {
            dataJSON.entries = [];
        }

        return dataJSON;
    }

    var saveStorage = function(storage) {
        var storageString = JSON.stringify(storage)

        try {
            var storageJSON = JSON.parse(storageString);
            window.localStorage.setItem(STORAGE_KEY, storageString);

            eventsService.emit("clipboardService.storageUpdate");

            return true;
        } catch(e) {
            return false;
        }

        return false;
    }

    var service = {};

    /**
    * @ngdoc method
    * @name umbraco.services.clipboardService#copy
    * @methodOf umbraco.services.clipboardService
    *
    * @description
    * Saves a single JS-object with a type and alias to the clipboard.
    */
    service.copy = function(type, alias, data) {

        var storage = retriveStorage();

        var shallowCloneData = Object.assign({}, data);// Notice only a shallow copy, since we dont need to deep copy. (that will happen when storing the data)
        delete shallowCloneData.key;
        delete shallowCloneData.$$hashKey;

        var key = data.key || data.$$hashKey || console.error("missing unique key for this content");

        // remove previous copies of this entry:
        storage.entries = storage.entries.filter(
            (entry) => {
                return entry.unique !== key;
            }
        );

        var entry = {unique:key, type:type, alias:alias, data:shallowCloneData};
        storage.entries.push(entry);

        if (saveStorage(storage) === true) {
            notificationsService.success("Clipboard", "Copied to clipboard.");
        } else {
            notificationsService.success("Clipboard", "Couldnt copy this data to clipboard.");
        }

    };

    /**
    * @ngdoc method
    * @name umbraco.services.supportsCopy#supported
    * @methodOf umbraco.services.clipboardService
    *
    * @description
    * Determins wether the current browser is able to performe its actions.
    */
    service.isSupported = supportsLocalStorage;

    /**
    * @ngdoc method
    * @name umbraco.services.supportsCopy#hasEntriesOfType
    * @methodOf umbraco.services.clipboardService
    *
    * @description
    * Determins wether the current clipboard has entries that matches given type and a one of the aliases.
    */
    service.hasEntriesOfType = function(type, aliases) {

        if(service.retriveEntriesOfType(type, aliases).length > 0) {
            return true;
        }

        return false;
    };

    /**
    * @ngdoc method
    * @name umbraco.services.supportsCopy#retriveEntriesOfType
    * @methodOf umbraco.services.clipboardService
    *
    * @description
    * Returns an array of entries matching the given type and one of the provided aliases.
    */
    service.retriveEntriesOfType = function(type, aliases) {

        var storage = retriveStorage();

        // Find entries that are furfilling the criterias for this nodeType and nodeTypesAliases.
        var filteretEntries = storage.entries.filter(
            (entry) => {
                return (entry.type === type && aliases.filter(alias => alias === entry.alias).length > 0);
            }
        );

        return filteretEntries;
    };

    /**
    * @ngdoc method
    * @name umbraco.services.supportsCopy#retriveEntriesOfType
    * @methodOf umbraco.services.clipboardService
    *
    * @description
    * Returns an array of data of entries matching the given type and one of the provided aliases.
    */
    service.retriveDataOfType = function(type, aliases) {
        return service.retriveEntriesOfType(type, aliases).map((x) => x.data);
    };

    /**
    * @ngdoc method
    * @name umbraco.services.supportsCopy#retriveEntriesOfType
    * @methodOf umbraco.services.clipboardService
    *
    * @description
    * Removes entries matching the given type and one of the provided aliases.
    */
    service.clearEntriesOfType = function(type, aliases) {

        var storage = retriveStorage();

        // Find entries that are NOT furfilling the criterias for this nodeType and nodeTypesAliases.
        var filteretEntries = storage.entries.filter(
            (entry) => {
                return !(entry.type === type && aliases.filter(alias => alias === entry.alias).length > 0);
            }
        );

        storage.entries = filteretEntries;

        saveStorage(storage);
    };

    return service;
}

angular.module("umbraco.services").factory("clipboardService", clipboardService);

<commit_msg>using localStorageService + applied further documentation<commit_after>/**
 * @ngdoc service
 * @name umbraco.services.clipboardService
 *
 * @requires notificationsService
 * @requires eventsService
 *
 * @description
 * Service to handle clipboard in general across the application. Responsible for handling the data both storing and retrive.
 * The service has a set way for defining a data-set by a entryType and alias, which later will be used to retrive the posible entries for a paste scenario.
 *
 */
function clipboardService(notificationsService, eventsService, localStorageService) {

    var STORAGE_KEY = "umbClipboardService";

    var retriveStorage = function() {
        if (localStorageService.isSupported === false) {
            return null;
        }
        var dataJSON;
        var dataString = localStorageService.get(STORAGE_KEY);
        if (dataString != null) {
            dataJSON = JSON.parse(dataString);
        }

        if(dataJSON == null) {
            dataJSON = new Object();
        }

        if(dataJSON.entries === undefined) {
            dataJSON.entries = [];
        }

        return dataJSON;
    }

    var saveStorage = function(storage) {
        var storageString = JSON.stringify(storage);

        try {
            var storageJSON = JSON.parse(storageString);
            localStorageService.set(STORAGE_KEY, storageString);

            eventsService.emit("clipboardService.storageUpdate");

            return true;
        } catch(e) {
            return false;
        }

        return false;
    }

    var service = {};

    /**
    * @ngdoc method
    * @name umbraco.services.clipboardService#copy
    * @methodOf umbraco.services.clipboardService
    *
    * @param type {string} umbraco A string defining the type of data to storing, example: 'elementType', 'contentNode'
    * @param alias {string} umbraco A string defining the alias of the data to store, example: 'product'
    * @param data {object} umbraco A object containing the properties to be saved.
    *
    * @description
    * Saves a single JS-object with a type and alias to the clipboard.
    */
    service.copy = function(type, alias, data) {

        var storage = retriveStorage();

        var shallowCloneData = Object.assign({}, data);// Notice only a shallow copy, since we dont need to deep copy. (that will happen when storing the data)
        delete shallowCloneData.key;
        delete shallowCloneData.$$hashKey;

        var key = data.key || data.$$hashKey || console.error("missing unique key for this content");

        // remove previous copies of this entry:
        storage.entries = storage.entries.filter(
            (entry) => {
                return entry.unique !== key;
            }
        );

        var entry = {unique:key, type:type, alias:alias, data:shallowCloneData};
        storage.entries.push(entry);

        if (saveStorage(storage) === true) {
            notificationsService.success("Clipboard", "Copied to clipboard.");
        } else {
            notificationsService.success("Clipboard", "Couldnt copy this data to clipboard.");
        }

    };

    /**
    * @ngdoc method
    * @name umbraco.services.supportsCopy#supported
    * @methodOf umbraco.services.clipboardService
    *
    * @description
    * Determins wether the current browser is able to performe its actions.
    */
    service.isSupported = function() {
        return localStorageService.isSupported;
    };

    /**
    * @ngdoc method
    * @name umbraco.services.supportsCopy#hasEntriesOfType
    * @methodOf umbraco.services.clipboardService
    *
    * @param type {string} umbraco A string defining the type of data test for.
    * @param aliases {string} umbraco A array of strings providing the alias of the data you want to test for.
    *
    * @description
    * Determins wether the current clipboard has entries that matches given type and a one of the aliases.
    */
    service.hasEntriesOfType = function(type, aliases) {

        if(service.retriveEntriesOfType(type, aliases).length > 0) {
            return true;
        }

        return false;
    };

    /**
    * @ngdoc method
    * @name umbraco.services.supportsCopy#retriveEntriesOfType
    * @methodOf umbraco.services.clipboardService
    *
    * @param type {string} umbraco A string defining the type of data to recive.
    * @param aliases {string} umbraco A array of strings providing the alias of the data you want to recive.
    *
    * @description
    * Returns an array of entries matching the given type and one of the provided aliases.
    */
    service.retriveEntriesOfType = function(type, aliases) {

        var storage = retriveStorage();

        // Find entries that are furfilling the criterias for this nodeType and nodeTypesAliases.
        var filteretEntries = storage.entries.filter(
            (entry) => {
                return (entry.type === type && aliases.filter(alias => alias === entry.alias).length > 0);
            }
        );

        return filteretEntries;
    };

    /**
    * @ngdoc method
    * @name umbraco.services.supportsCopy#retriveEntriesOfType
    * @methodOf umbraco.services.clipboardService
    *
    * @param type {string} umbraco A string defining the type of data to recive.
    * @param aliases {string} umbraco A array of strings providing the alias of the data you want to recive.
    *
    * @description
    * Returns an array of data of entries matching the given type and one of the provided aliases.
    */
    service.retriveDataOfType = function(type, aliases) {
        return service.retriveEntriesOfType(type, aliases).map((x) => x.data);
    };

    /**
    * @ngdoc method
    * @name umbraco.services.supportsCopy#retriveEntriesOfType
    * @methodOf umbraco.services.clipboardService
    *
    * @param type {string} umbraco A string defining the type of data to remove.
    * @param aliases {string} umbraco A array of strings providing the alias of the data you want to remove.
    *
    * @description
    * Removes entries matching the given type and one of the provided aliases.
    */
    service.clearEntriesOfType = function(type, aliases) {

        var storage = retriveStorage();

        // Find entries that are NOT furfilling the criterias for this nodeType and nodeTypesAliases.
        var filteretEntries = storage.entries.filter(
            (entry) => {
                return !(entry.type === type && aliases.filter(alias => alias === entry.alias).length > 0);
            }
        );

        storage.entries = filteretEntries;

        saveStorage(storage);
    };

    return service;
}

angular.module("umbraco.services").factory("clipboardService", clipboardService);

<|endoftext|>


<commit_before>(function(global) {
  class SceneSwitcherNode extends NIN.Node {
    constructor(id) {
      super(id, {
        inputs: {
          A: new NIN.TextureInput(),
          B: new NIN.TextureInput(),
        },
        outputs: {
          render: new NIN.TextureOutput(),
        }
      });
    }

    beforeUpdate() {
      this.inputs.A.enabled = false;
      this.inputs.B.enabled = false;

      let selectedScene;
      if (BEAN < 48 * 4) {
        selectedScene = this.inputs.A;
      } else {
        selectedScene = this.inputs.B;
      }

      selectedScene.enabled = true;
      this.outputs.render.setValue(selectedScene.getValue());
    }
  }

  global.SceneSwitcherNode = SceneSwitcherNode;
})(this);
<commit_msg>Move selectedScene.getValue() to render()<commit_after>(function(global) {
  class SceneSwitcherNode extends NIN.Node {
    constructor(id) {
      super(id, {
        inputs: {
          A: new NIN.TextureInput(),
          B: new NIN.TextureInput(),
        },
        outputs: {
          render: new NIN.TextureOutput(),
        }
      });
    }

    beforeUpdate() {
      this.inputs.A.enabled = false;
      this.inputs.B.enabled = false;

      let selectedScene;
      if (BEAN < 48 * 4) {
        selectedScene = this.inputs.A;
      } else {
        selectedScene = this.inputs.B;
      }

      selectedScene.enabled = true;
      this.selectedScene = selectedScene;
    }

    render() {
      this.outputs.render.setValue(this.selectedScene.getValue());
    }
  }

  global.SceneSwitcherNode = SceneSwitcherNode;
})(this);
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import AbstractFormComponent from './AbstractFormComponent';
import {
  TextInput,
  View,
  Text,
  StyleSheet,
  Platform
} from 'react-native';
import Icon from '../Icon';

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    borderWidth: 1,
    borderColor: '#CCC'
  },
  icon: {
    justifyContent: 'center',
    alignItems: 'center',
    width: 25
  },
  input: {
    flex: 1,
    height: 40,
    marginTop: Platform.OS === 'ios' ? 2 : 0
  },
  label: {
    justifyContent: 'center'
  }
});
const INPUT_PROPS_KEYS = [
  'autoCapitalize',
  'autoCorrect',
  'autoFocus',
  'defaultValue',
  'editable',
  'keyboardType',
  'maxLength',
  'multiline',
  'onBlur',
  'onChange',
  'onChangeText',
  'onEndEditing',
  'onFocus',
  'onSubmitEditing',
  'placeholder',
  'placeholderTextColor',
  'secureTextEntry',
  'value',
  'blurOnSubmit',
  'clearButtonMode',
  'clearTextOnFocus',
  'enablesReturnKeyAutomatically',
  'keyboardAppearance',
  'numberOfLines',
  'onKeyPress',
  'returnKeyType',
  'selectTextOnFocus',
  'selectionState',
  'textAlign',
  'underlineColorAndroid',
  'name'
];
class Input extends AbstractFormComponent {

  static propTypes = {
    ...TextInput.propTypes,
    /**
     * Input 之后,如果为String  则显示Icon
     */
    after: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
    /**
     * Input 之前,如果为String 则显示Icon
     */
    before: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
    /**
     * 自定义value验证
     */
    customValid: PropTypes.func,
    /**
     * input 默认值
     */
    defaultValue: PropTypes.string,
    /**
     * Input 所属表单
     */
    form: PropTypes.object,
    /**
     * 图标属性
     */
    iconProps: PropTypes.object,
    /**
     * 图标样式
     */
    iconStyle: View.propTypes.style,
    /**
     * 输入框样式
     */
    inputStyle: TextInput.propTypes.style,
    /**
     * input label
     */
    label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
    /**
     * label props
     */
    labelProps: PropTypes.object,
    /**
     * 字段名字,用于提交表单获取值
     */
    name: PropTypes.string,
    /**
     * input value 验证正则表达式
     */
    pattern: PropTypes.instanceOf(RegExp),
    /**
     * 表明表单的值是必须的
     */
    required: PropTypes.bool,
    /**
     * container 样式
     */
    style: View.propTypes.style,
    /**
     * Android input 下划线
     */
    underlineColorAndroid: PropTypes.string,
    /**
     * 是否验证input的值
     */
    validate: PropTypes.bool,
    /**
     * input 值
     */
    value: PropTypes.string,
    /**
     * input 值更改回调函数
     */
    onChangeText: PropTypes.func,
  };

  static defaultProps = {
    defaultValue: '',
    iconProps: {},
    underlineColorAndroid: 'transparent',
    validate: true
  };

  constructor(props) {
    super(props);
    let {name, form} = props;
    name && form && form.putFormValue(name, props.defaultValue);

    this.state.value = props.defaultValue || '';
  }

  state = {};

  // refs
  textInput;

  componentWillReceiveProps(nextProps) {
    if (nextProps.defaultValue !== this.props.defaultValue) {
      this.handleChangeText(nextProps.defaultValue);
    }
  }

  getComp(name, isLabel) {
    let {iconProps, iconStyle, label, labelProps} = this.props;
    if (name) {
      if (typeof name === 'string') {
        return (
          <View style={[styles.icon, iconStyle]}>
            <Icon {...iconProps}
              name={name}
            />
          </View>
        );
      }
      return name;
    } else if (label && isLabel) {
      if (typeof label === 'string') {
        return (
          <View style={styles.label}>
            <Text {...labelProps}>
              {label}
            </Text>
          </View>
        );
      }
      return label;
    }
    return null;
  }

  getValue() {
    return this.state.value;
  }

  valid() {
    let {validate, customValid, pattern, required, name, form} = this.props;
    if (validate) {
      if (customValid) {
        let result = customValid(this.state.value);
        this.setFormFieldInfo(result);
        return result;
      }

      if (required) {
        if (this.state.value === '') {
          name && form && form.putMissField(name);
          return false;
        }
        name && form && form.deleteErrOrMissField(name);
        return true;
      }

      if (pattern) {
        let result = pattern.test(this.state.value);
        this.setFormFieldInfo(result);
        return result;
      }
    }

    return true;
  }

  setFormFieldInfo(result) {
    let {name, form} = this.props;
    if (result) {
      name && form && form.deleteErrOrMissField(name);
    } else {
      name && form && form.putErrorField(name);
    }
  }

  handleChangeText(value) {
    this.setState({value});
    let {onChangeText, name, form} = this.props;
    name && form && form.putFormValue(name, value);
    onChangeText && onChangeText(value);
  }

  render() {
    let {before, after, style, inputStyle, ...other} = this.props;

    let newInputProps = {};
    let newProps = Object.assign({}, other);

    INPUT_PROPS_KEYS.forEach(key => {
      newInputProps[key] = this.props[key];
      newProps[key] && delete newProps[key];
    });

    return (
      <View {...newProps}
        style={[styles.container, style]}
      >
        {this.getComp(before, true)}
        <TextInput {...newInputProps}
          onChangeText={this.handleChangeText}
          ref={textInput => this.textInput = textInput}
          style={[styles.input, inputStyle]}
        />
        {this.getComp(after)}
      </View>
    );
  }

}

export default Input;
<commit_msg>delete 使用Reflect<commit_after>import React, { PropTypes } from 'react';
import AbstractFormComponent from './AbstractFormComponent';
import { TextInput, View, Text, StyleSheet, Platform } from 'react-native';
import Icon from '../Icon';

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    borderWidth: 1,
    borderColor: '#CCC'
  },
  icon: {
    justifyContent: 'center',
    alignItems: 'center',
    width: 25
  },
  input: {
    flex: 1,
    height: 40,
    marginTop: Platform.OS === 'ios' ? 2 : 0
  },
  label: {
    justifyContent: 'center'
  }
});
const INPUT_PROPS_KEYS = [
  'autoCapitalize',
  'autoCorrect',
  'autoFocus',
  'defaultValue',
  'editable',
  'keyboardType',
  'maxLength',
  'multiline',
  'onBlur',
  'onChange',
  'onChangeText',
  'onEndEditing',
  'onFocus',
  'onSubmitEditing',
  'placeholder',
  'placeholderTextColor',
  'secureTextEntry',
  'value',
  'blurOnSubmit',
  'clearButtonMode',
  'clearTextOnFocus',
  'enablesReturnKeyAutomatically',
  'keyboardAppearance',
  'numberOfLines',
  'onKeyPress',
  'returnKeyType',
  'selectTextOnFocus',
  'selectionState',
  'textAlign',
  'underlineColorAndroid',
  'name'
];
class Input extends AbstractFormComponent {

  static propTypes = {
    ...TextInput.propTypes,
    /**
     * Input 之后,如果为String  则显示Icon
     */
    after: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
    /**
     * Input 之前,如果为String 则显示Icon
     */
    before: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
    /**
     * 自定义value验证
     */
    customValid: PropTypes.func,
    /**
     * input 默认值
     */
    defaultValue: PropTypes.string,
    /**
     * Input 所属表单
     */
    form: PropTypes.object,
    /**
     * 图标属性
     */
    iconProps: PropTypes.object,
    /**
     * 图标样式
     */
    iconStyle: View.propTypes.style,
    /**
     * 输入框样式
     */
    inputStyle: TextInput.propTypes.style,
    /**
     * input label
     */
    label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
    /**
     * label props
     */
    labelProps: PropTypes.object,
    /**
     * 字段名字,用于提交表单获取值
     */
    name: PropTypes.string,
    /**
     * input value 验证正则表达式
     */
    pattern: PropTypes.instanceOf(RegExp),
    /**
     * 表明表单的值是必须的
     */
    required: PropTypes.bool,
    /**
     * container 样式
     */
    style: View.propTypes.style,
    /**
     * Android input 下划线
     */
    underlineColorAndroid: PropTypes.string,
    /**
     * 是否验证input的值
     */
    validate: PropTypes.bool,
    /**
     * input 值
     */
    value: PropTypes.string,
    /**
     * input 值更改回调函数
     */
    onChangeText: PropTypes.func,
  };

  static defaultProps = {
    defaultValue: '',
    iconProps: {},
    underlineColorAndroid: 'transparent',
    validate: true
  };

  constructor(props) {
    super(props);
    let {name, form} = props;
    name && form && form.putFormValue(name, props.defaultValue);

    this.state.value = props.defaultValue || '';
  }

  state = {};

  // refs
  textInput;

  componentWillReceiveProps(nextProps) {
    if (nextProps.defaultValue !== this.props.defaultValue) {
      this.handleChangeText(nextProps.defaultValue);
    }
  }

  getComp(name, isLabel) {
    let {iconProps, iconStyle, label, labelProps} = this.props;
    if (name) {
      if (typeof name === 'string') {
        return (
          <View style={[styles.icon, iconStyle]}>
            <Icon {...iconProps}
              name={name}
            />
          </View>
        );
      }
      return name;
    } else if (label && isLabel) {
      if (typeof label === 'string') {
        return (
          <View style={styles.label}>
            <Text {...labelProps}>
              {label}
            </Text>
          </View>
        );
      }
      return label;
    }
    return null;
  }

  getValue() {
    return this.state.value;
  }

  valid() {
    let {validate, customValid, pattern, required, name, form} = this.props;
    if (validate) {
      if (customValid) {
        let result = customValid(this.state.value);
        this.setFormFieldInfo(result);
        return result;
      }

      if (required) {
        if (this.state.value === '') {
          name && form && form.putMissField(name);
          return false;
        }
        name && form && form.deleteErrOrMissField(name);
        return true;
      }

      if (pattern) {
        let result = pattern.test(this.state.value);
        this.setFormFieldInfo(result);
        return result;
      }
    }

    return true;
  }

  setFormFieldInfo(result) {
    let {name, form} = this.props;
    if (result) {
      name && form && form.deleteErrOrMissField(name);
    } else {
      name && form && form.putErrorField(name);
    }
  }

  handleChangeText(value) {
    this.setState({value});
    let {onChangeText, name, form} = this.props;
    name && form && form.putFormValue(name, value);
    onChangeText && onChangeText(value);
  }

  render() {
    let {before, after, style, inputStyle, ...other} = this.props;

    let newInputProps = {};
    let newProps = Object.assign({}, other);

    INPUT_PROPS_KEYS.forEach(key => {
      newInputProps[key] = this.props[key];
      newProps[key] && Reflect.deleteProperty(newProps, key);
    });

    return (
      <View {...newProps}
        style={[styles.container, style]}
      >
        {this.getComp(before, true)}
        <TextInput {...newInputProps}
          onChangeText={this.handleChangeText}
          ref={textInput => this.textInput = textInput}
          style={[styles.input, inputStyle]}
        />
        {this.getComp(after)}
      </View>
    );
  }

}

export default Input;
<|endoftext|>


<commit_before>module.exports = {
    name: 'App Kernel',
    environment: 'development',
    loaders: {
        modules: './modules',
        handlers: './handlers/*.js'
    }
};
<commit_msg>wip: refactoring app to use in for all file loading<commit_after>module.exports = {
    name: 'App Kernel',
    environment: 'development',
    loaders: {
        modules: './modules',
        // handlers: './handlers'
        handlers: './handlers/*.js'
    }
};
<|endoftext|>


<commit_before>'use strict';

/**
 * The callback from an apiCall
 * @callback apiResponse
 * @param {Object} error - null if the call was successful
 * @param {Object} response - The resulting data from making the call
 */

/**
 * The call object to store information necessary to a call to a RESTful API
 * @typedef {Object} apiCall
 * @property {string} url - The address (usually localhost) and port of siad
 * @property {string} type - The call type, such as 'POST' or 'GET'
 * @property {Object} args - The arguments to send with the call
 */

/**
 * Creates an XMLHttpRequest to send
 * @param {string} url - The address (usually localhost) and port of siad
 * @param {string} type - The call type, such as 'POST' or 'GET'
 * @param {Object} args - The arguments to send with the call
 * @param {apiResponse} callback
 * @private
 */
function sendCall(url, type, args, callback) {
	// Detect improper calls, each one needs a url
	if (!url || !type || !args) {
		process.nextTick(function() {
			callback(new Error('Improper API call!'));
		});
		return;
	}
	// make request via jquery
	$.ajax({
		url: url,
		type: type,
		success: function(resp) {
			callback(null, JSON.parse(resp));
		},
		error: function() {
			callback("Oops");
		},
		data: args
	});
}

/**
 * Takes any call object and makes the correct call
 * @param {apiCall} call - The call object with parameters
 * @param {apiResponse} callback
 * @private
 */
function discernCall(call, callback) {
	// Extract call attributes, default call is 'GET'
	var url = call.url;
	var type = call.type || 'GET';
	var args = call.args || {};

	// Make the call
	sendCall(url, type, args, callback);
}

/**
 * Can be used for its functions to perform API calls
 * @module DaemonAPI
 */
module.exports = {
	/**
	 * Performs a 'GET' call
	 * @param {string} url - The url to send the call to
	 * @param {apiResponse} callback
	 */
	getCall: function getCall(url, callback) {
		sendCall(url, 'GET', '', callback);
	},
	/**
	 * Performs a 'POST' call
	 * @param {string} url - The url to send the call to
	 * @param {string|Object} url - The arguments to send with the call
	 * @param {apiResponse} callback
	 */
	postCall: function postCall(url, args, callback) {
		sendCall(url, 'POST', args, callback);
	},
	/**
	 * Performs any API call
	 * @param {apiCall} call - The call object with parameters
	 * @param {apiResponse} callback
	 */
	makeCall: discernCall,
};
<commit_msg>Made the daemonAPI callbacks more descriptive<commit_after>'use strict';

/**
 * The callback from an apiCall
 * @callback apiResponse
 * @param {Object} error - null if the call was successful
 * @param {Object} response - The resulting data from making the call
 */

/**
 * The call object to store information necessary to a call to a RESTful API
 * @typedef {Object} apiCall
 * @property {string} url - The address (usually localhost) and port of siad
 * @property {string} type - The call type, such as 'POST' or 'GET'
 * @property {Object} args - The arguments to send with the call
 */

/**
 * Creates an XMLHttpRequest to send
 * @param {string} url - The address (usually localhost) and port of siad
 * @param {string} type - The call type, such as 'POST' or 'GET'
 * @param {Object} args - The arguments to send with the call
 * @param {apiResponse} callback
 * @private
 */
function sendCall(url, type, args, callback) {
	// Detect improper calls, each one needs a url
	if (!url || !type || !args) {
		process.nextTick(function() {
			callback(new Error('Improper API call!'));
		});
		return;
	}
	// make request via jquery
	$.ajax({
		url: url,
		type: type,
		success: function(responseData, textStatus, jqXHR) {
			callback(null, JSON.parse(responseData), textStatus, jqXHR);
		},
		error: function(jqXHR, textStatus, exception) {
			callback(textStatus);
		},
		data: args
	});
}

/**
 * Takes any call object and makes the correct call
 * @param {apiCall} call - The call object with parameters
 * @param {apiResponse} callback
 * @private
 */
function discernCall(call, callback) {
	// Extract call attributes, default call is 'GET'
	var url = call.url;
	var type = call.type || 'GET';
	var args = call.args || {};

	// Make the call
	sendCall(url, type, args, callback);
}

/**
 * Can be used for its functions to perform API calls
 * @module DaemonAPI
 */
module.exports = {
	/**
	 * Performs a 'GET' call
	 * @param {string} url - The url to send the call to
	 * @param {apiResponse} callback
	 */
	getCall: function getCall(url, callback) {
		sendCall(url, 'GET', '', callback);
	},
	/**
	 * Performs a 'POST' call
	 * @param {string} url - The url to send the call to
	 * @param {string|Object} url - The arguments to send with the call
	 * @param {apiResponse} callback
	 */
	postCall: function postCall(url, args, callback) {
		sendCall(url, 'POST', args, callback);
	},
	/**
	 * Performs any API call
	 * @param {apiCall} call - The call object with parameters
	 * @param {apiResponse} callback
	 */
	makeCall: discernCall,
};
<|endoftext|>


<commit_before>(function (root, factory) {

    'use strict';

    if (typeof define === 'function' && define.amd) {
        // AMD environment
        define('notify', [], function () {
            return factory(root, document);
        });
    } else {
        // Browser environment
        root.Notify = factory(root, document);
    }

}(this, function (w, d) {

    'use strict';

    function Notify(title, options) {

        this.title = typeof title === 'string' ? title : null;

        this.options = {
            icon: '',
            body: '',
            tag: '',
            notifyShow: null,
            notifyClose: null,
            notifyClick: null,
            notifyError: null,
            permissionGranted: null,
            permissionDenied: null
        };

        this.permission = null;

        if (!Notify.isSupported()) {
            return;
        }

        if (!this.title) {
            throw new Error('Notify(): first arg (title) must be a string.');
        }

        //User defined options for notification content
        if (typeof options === 'object') {

            for (var i in options) {
                if (options.hasOwnProperty(i)) {
                    this.options[i] = options[i];
                }
            }

            //callback when notification is displayed
            if (typeof this.options.notifyShow === 'function') {
                this.onShowCallback = this.options.notifyShow;
            }

            //callback when notification is closed
            if (typeof this.options.notifyClose === 'function') {
                this.onCloseCallback = this.options.notifyClose;
            }

            //callback when notification is clicked
            if (typeof this.options.notifyClick === 'function') {
                this.onClickCallback = this.options.notifyClick;
            }

            //callback when notification throws error
            if (typeof this.options.notifyError === 'function') {
                this.onErrorCallback = this.options.notifyError;
            }
        }
    }

    // return true if the browser supports HTML5 Notification
    Notify.isSupported = function () {
        if ('Notification' in w) {
            return true;
        }
        return false;
    };

    // returns true if the permission is not granted
    Notify.needsPermission = function () {
        if (Notify.isSupported() && Notification.permission === 'granted') {
            return false;
        }
        return true;
    };

    // asks the user for permission to display notifications.  Then calls the callback functions is supplied.
    Notify.requestPermission = function (onPermissionGrantedCallback, onPermissionDeniedCallback) {
        if (Notify.isSupported()) {
            w.Notification.requestPermission(function (perm) {
                switch (perm) {
                    case 'granted':
                        if (typeof onPermissionGrantedCallback === 'function') {
                            onPermissionGrantedCallback();
                        }
                        break;
                    case 'denied':
                        if (typeof onPermissionDeniedCallback === 'function') {
                            onPermissionDeniedCallback();
                        }
                        break;
                }
            });
        }
    };

    Notify.prototype.show = function () {

        if (!Notify.isSupported()) {
            return;
        }

        this.myNotify = new Notification(this.title, {
            'body': this.options.body,
            'tag' : this.options.tag,
            'icon' : this.options.icon
        });

        this.myNotify.addEventListener('show', this, false);
        this.myNotify.addEventListener('error', this, false);
        this.myNotify.addEventListener('close', this, false);
        this.myNotify.addEventListener('click', this, false);
    };

    Notify.prototype.onShowNotification = function (e) {
        if (this.onShowCallback) {
            this.onShowCallback(e);
        }
    };

    Notify.prototype.onCloseNotification = function () {
        if (this.onCloseCallback) {
            this.onCloseCallback();
        }
        this.destroy();
    };

    Notify.prototype.onClickNotification = function () {
        if (this.onClickCallback) {
            this.onClickCallback();
        }
    };

    Notify.prototype.onErrorNotification = function () {
        if (this.onErrorCallback) {
            this.onErrorCallback();
        }
        this.destroy();
    };

    Notify.prototype.destroy = function () {
        this.myNotify.removeEventListener('show', this, false);
        this.myNotify.removeEventListener('error', this, false);
        this.myNotify.removeEventListener('close', this, false);
        this.myNotify.removeEventListener('click', this, false);
    };

    Notify.prototype.handleEvent = function (e) {
        switch (e.type) {
        case 'show':
            this.onShowNotification(e);
            break;
        case 'close':
            this.onCloseNotification(e);
            break;
        case 'click':
            this.onClickNotification(e);
            break;
        case 'error':
            this.onErrorNotification(e);
            break;
        }
    };

    return Notify;

}));
<commit_msg>Added the method close to the Notify prototype<commit_after>(function (root, factory) {

    'use strict';

    if (typeof define === 'function' && define.amd) {
        // AMD environment
        define('notify', [], function () {
            return factory(root, document);
        });
    } else {
        // Browser environment
        root.Notify = factory(root, document);
    }

}(this, function (w, d) {

    'use strict';

    function Notify(title, options) {

        this.title = typeof title === 'string' ? title : null;

        this.options = {
            icon: '',
            body: '',
            tag: '',
            notifyShow: null,
            notifyClose: null,
            notifyClick: null,
            notifyError: null,
            permissionGranted: null,
            permissionDenied: null
        };

        this.permission = null;

        if (!Notify.isSupported()) {
            return;
        }

        if (!this.title) {
            throw new Error('Notify(): first arg (title) must be a string.');
        }

        //User defined options for notification content
        if (typeof options === 'object') {

            for (var i in options) {
                if (options.hasOwnProperty(i)) {
                    this.options[i] = options[i];
                }
            }

            //callback when notification is displayed
            if (typeof this.options.notifyShow === 'function') {
                this.onShowCallback = this.options.notifyShow;
            }

            //callback when notification is closed
            if (typeof this.options.notifyClose === 'function') {
                this.onCloseCallback = this.options.notifyClose;
            }

            //callback when notification is clicked
            if (typeof this.options.notifyClick === 'function') {
                this.onClickCallback = this.options.notifyClick;
            }

            //callback when notification throws error
            if (typeof this.options.notifyError === 'function') {
                this.onErrorCallback = this.options.notifyError;
            }
        }
    }

    // return true if the browser supports HTML5 Notification
    Notify.isSupported = function () {
        if ('Notification' in w) {
            return true;
        }
        return false;
    };

    // returns true if the permission is not granted
    Notify.needsPermission = function () {
        if (Notify.isSupported() && Notification.permission === 'granted') {
            return false;
        }
        return true;
    };

    // asks the user for permission to display notifications.  Then calls the callback functions is supplied.
    Notify.requestPermission = function (onPermissionGrantedCallback, onPermissionDeniedCallback) {
        if (Notify.isSupported()) {
            w.Notification.requestPermission(function (perm) {
                switch (perm) {
                    case 'granted':
                        if (typeof onPermissionGrantedCallback === 'function') {
                            onPermissionGrantedCallback();
                        }
                        break;
                    case 'denied':
                        if (typeof onPermissionDeniedCallback === 'function') {
                            onPermissionDeniedCallback();
                        }
                        break;
                }
            });
        }
    };

    Notify.prototype.show = function () {

        if (!Notify.isSupported()) {
            return;
        }

        this.myNotify = new Notification(this.title, {
            'body': this.options.body,
            'tag' : this.options.tag,
            'icon' : this.options.icon
        });

        this.myNotify.addEventListener('show', this, false);
        this.myNotify.addEventListener('error', this, false);
        this.myNotify.addEventListener('close', this, false);
        this.myNotify.addEventListener('click', this, false);
    };

    Notify.prototype.onShowNotification = function (e) {
        if (this.onShowCallback) {
            this.onShowCallback(e);
        }
    };

    Notify.prototype.onCloseNotification = function () {
        if (this.onCloseCallback) {
            this.onCloseCallback();
        }
        this.destroy();
    };

    Notify.prototype.onClickNotification = function () {
        if (this.onClickCallback) {
            this.onClickCallback();
        }
    };

    Notify.prototype.onErrorNotification = function () {
        if (this.onErrorCallback) {
            this.onErrorCallback();
        }
        this.destroy();
    };

    Notify.prototype.destroy = function () {
        this.myNotify.removeEventListener('show', this, false);
        this.myNotify.removeEventListener('error', this, false);
        this.myNotify.removeEventListener('close', this, false);
        this.myNotify.removeEventListener('click', this, false);
    };

    Notify.prototype.close = function () {
        this.myNotify.close();
    };

    Notify.prototype.handleEvent = function (e) {
        switch (e.type) {
        case 'show':
            this.onShowNotification(e);
            break;
        case 'close':
            this.onCloseNotification(e);
            break;
        case 'click':
            this.onClickNotification(e);
            break;
        case 'error':
            this.onErrorNotification(e);
            break;
        }
    };

    return Notify;

}));
<|endoftext|>


<commit_before>/*
 *  Drawphone Client
 *  By Tanner Krewson
 */

//blocks use of https, required for the uploads.im api,
//  as it does not have https
window.onload = function(){
   $(function(){
       if(window.location.protocol==="https:")
           window.location.protocol="http";
   });
}

var socket = io();

var relativeUrl = window.location.pathname + window.location.search;

if (relativeUrl === "/dev") {
  socket.emit('joinGame', {
    code: 'ffff',
    name: Math.random().toString()
  });
}

//prevent form submit
$(function() {
    $("form").submit(function() { return false; });
});

//initialize sketch.js
var canvas = new fabric.Canvas('game-drawing-canvas');
canvas.isDrawingMode = true;

window.addEventListener('resize', resizeCanvas, false);

function resizeCanvas() {
  var container = $('#game-drawing');
  canvas.setHeight(container.width());
  canvas.setWidth(container.width());
  canvas.renderAll();
}

// resize on init
resizeCanvas();

//store game code globally
var gameCode;

//
//  UI
//

setTitle('Drawphone');
setSubtitle('Telephone with pictures');

//  Main Menu

$('#joinbtn').click(function() {
  hideAll();
  showElement('#joinmenu');
});

$('#newbtn').click(function() {
  hideAll();
  showElement('#newmenu');
});

//  Join Game Menu

$('#joinmenu-back').click(function() {
  hideAll();
  showElement('#mainmenu');
});

$('#joinmenu-go').click(function() {
  var code = $('#joinincode').val();
  var name = $('#joininname').val();

  if (name.length > 1 && code.length === 4) {
    socket.emit('joinGame', {
      code,
      name
    });
  }
});

//  New Game Menu

$('#newmenu-back').click(function() {
  hideAll();
  showElement('#mainmenu');
});

$('#newmenu-go').click(function() {
  var name = $('#newinname').val();

  if (name.length > 1) {
    socket.emit('newGame', {
      name
    });
  }
});

//  Lobby

$('#lobby-leave').click(function() {
  //refresh the page
  location.reload();
});

$('#lobby-start').click(function() {
  socket.emit('tryStartGame', {});
});

function showLobby(data) {
  if (data.success) {
    hideAll();
    showElement('#lobby');
    gameCode = data.game.code;
    setTitle('Game Code: ' + gameCode);
    setSubtitle('Waiting for players...');
    updatePlayerList(data.game.players);
  } else {
    alert(data.error);
  }
}

function updatePlayerList(list) {
  var playerList = $('#lobby-players');

  playerList.empty();

  for (var i = 0; i < list.length; i++) {
    var listItem = $('
' + list[i].name + '
').appendTo(playerList);
    listItem.addClass('list-group-item');
  }
}

//  Game

function showGame() {
  hideAll();
  hideLinkCreators();
  showElement('#game');

  setSubtitle('Game in progress');
}

function nextLink(data) {
  var lastLink = data.link
  var lastLinkType = lastLink.type;
  var newLinkType = oppositeLinkType(lastLinkType);
  var doneButton = $("#game-send");

  showGame();
  showElement('#game-buttons');

  if (lastLinkType === 'drawing') {
    //show the word creator
    showElement('#game-word');

    //show the previous drawing
    $('#game-word-drawingtoname').attr("src", lastLink.data);

    setTitle('What is this a drawing of?');

  } else if (lastLinkType === 'word'){
    //clear the previous drawing
    canvas.clear();

    //show drawing creator
    showElement('#game-drawing');

    //bind clear canvas to clear drawing button
    $('#game-cleardrawing').off('click');
    $('#game-cleardrawing').click(function() {
      canvas.clear();
    });

    //show clear button
    showElement('#game-cleardrawing');

    //calculate size of canvas dynamically
    resizeCanvas();

    setTitle('Please draw: ' + lastLink.data);
  }

  doneButton.off("click");
  doneButton.click(done);

  //run done when enter key is pressed in word input
  $('#game-word-in').keypress(function(e) {
    var key = e.which;
    if (key === 13) {
       done();
    }
  });

  function done() {
    var newLink;
    if (newLinkType === 'drawing') {
      if (isDrawingBlank()) {
        alert('Please draw something!');
      } else {
        uploadCanvas(function(url) {
          newLink = url;
          send();
        }, function() {
          //reshow the canvas and allow the user to try again
          showElement('#game-drawing');
          showElement('#game-buttons');
          setTitle('Upload failed, try again.');
        });
      }
    } else if (newLinkType === 'word') {
      newLink = $('#game-word-in').val().trim();
      //check if it is blank
      if (newLink === '') {
        alert('Please enter a guess!');
      } else {
        //clear the input
        $('#game-word-in').val('')
        send();
      }
    }

    function send() {
      setTitle('Sending...');
      //hide the drawing
      hideLinkCreators();

      socket.emit('finishedLink', {
        link: {
          type: newLinkType,
          data: newLink
        }
      });
      startWaiting();
    }
  }
}

function isDrawingBlank() {
    var blank = document.createElement('canvas');
    blank.width = canvas.width;
    blank.height = canvas.height;

    return canvas.toDataURL() == blank.toDataURL();
}

function roundOver(data) {
  returnToLobby('The round is over!');
}

function someoneLeft(data) {
  returnToLobby(data.name + ' disconnected.');
}

function returnToLobby(message) {
  hideAll();
  showElement('#lobby');
  setTitle('Game Code: ' + gameCode);
  setSubtitle('Waiting for players...');
  alert(message);
}

function hideLinkCreators() {
  $('#game-drawing').addClass('hidden');
  $('#game-word').addClass('hidden');
  $('#game-buttons').addClass('hidden');
}

function uploadCanvas(next, err) {
  // this code was copied from:
  // http://community.mybb.com/thread-150592.html
  // https://github.com/blueimp/JavaScript-Canvas-to-Blob#usage

  var file = canvas.toDataURL('image/png');
  var blob = window.dataURLtoBlob(file);
  var formData = new FormData();
  formData.append('upload', blob, 'drawing.png');
  var xhr = new XMLHttpRequest();
  xhr.open("POST", "http://uploads.im/api");
  xhr.onload = function() {
    var res = JSON.parse(xhr.responseText);
    if (res.status_code === 200) {
      var url = res.data.img_url;
      next(url);
    } else {
      err();
    }
  }
  xhr.onerror = err;
  try {
    xhr.send(formData);
  } catch (e) {
    err();
  }
}

//  Result

function viewResults(data) {
  console.log('View results!');
  var ourChain = data.links;
  var ourName = ourChain[0].player.name;

  hideAll();
  showElement('#result');

  setTitle(ourName + "'s Drawphone results");
  setSubtitle('Show everyone how it turned out!');

  var results = $('#result-content');

  for (var i = 0; i < data.links.length; i++) {
    var link = data.links[i];
    if (i === 0) {
      results.append('
The first word:

' + link.data + '
');
    } else if (link.type === 'drawing') {
      results.append('
' + link.player.name + ' drew:
');
    } else if (link.type === 'word') {
      results.append('
' + link.player.name + ' thought that was:

' + link.data + '
');
    } else {
      console.log('We should never get here');
    }
  }

  var doneButton = $('#result-done');
  doneButton.off('click');
  doneButton.on('click', function() {
    hideAll();
    setTitle('Thanks for playing Drawphone!');
    setSubtitle('Waiting for other players...');
    socket.emit('doneViewingResults', {});
  });
}

//  Waiting

function startWaiting() {
  hideAll();
  showElement('#waiting');
  setTitle('Waiting for other players...');
}

//ran when we receive updateWaitingList from the server
function updateWaitingList(data) {
  var list = data.players;
  var waitingList = $('#waiting-players');

  waitingList.empty();

  for (var i = 0; i < list.length; i++) {
    var listItem = $('
' + list[i].name + '
').appendTo(waitingList);
    listItem.addClass('list-group-item');
  }
}

//  UI Methods

function hideAll() {
  $('#mainmenu').addClass('hidden');
  $('#joinmenu').addClass('hidden');
  $('#newmenu').addClass('hidden');
  $('#lobby').addClass('hidden');
  $('#game').addClass('hidden');
  $('#result').addClass('hidden');
  $('#waiting').addClass('hidden');

  $('#game-cleardrawing').addClass('hidden');
  $('#result-content').empty();

}

function showElement(jq) {
  $(jq).removeClass('hidden');
}

function setTitle(newTitle) {
  $('#title').text(newTitle);
}

function setSubtitle(newSubtitle) {
  $('#subtitle').text(newSubtitle);
}

function oppositeLinkType(linkType) {
  if (linkType === 'drawing') {
    return 'word';
  } else {
    return 'drawing';
  }
}

//
//  Real-time Communication via Socket.IO
//

socket.on('disconnect', function() {
  alert('Connection lost!');
  //refresh the page
  location.reload();
});

socket.on('joinGameRes', showLobby);

socket.on('updatePlayerList', updatePlayerList);

socket.on('gameStart', showGame);

socket.on('nextLink', nextLink);

socket.on('roundOver', roundOver);

socket.on('someoneLeft', someoneLeft);

socket.on('viewResults', viewResults);

socket.on('updateWaitingList', updateWaitingList);
<commit_msg>Fix multiple events being hooked to the enter key<commit_after>/*
 *  Drawphone Client
 *  By Tanner Krewson
 */

//blocks use of https, required for the uploads.im api,
//  as it does not have https
window.onload = function(){
   $(function(){
       if(window.location.protocol==="https:")
           window.location.protocol="http";
   });
}

var socket = io();

var relativeUrl = window.location.pathname + window.location.search;

if (relativeUrl === "/dev") {
  socket.emit('joinGame', {
    code: 'ffff',
    name: Math.random().toString()
  });
}

//prevent form submit
$(function() {
    $("form").submit(function() { return false; });
});

//initialize sketch.js
var canvas = new fabric.Canvas('game-drawing-canvas');
canvas.isDrawingMode = true;

window.addEventListener('resize', resizeCanvas, false);

function resizeCanvas() {
  var container = $('#game-drawing');
  canvas.setHeight(container.width());
  canvas.setWidth(container.width());
  canvas.renderAll();
}

// resize on init
resizeCanvas();

//store game code globally
var gameCode;

//
//  UI
//

setTitle('Drawphone');
setSubtitle('Telephone with pictures');

//  Main Menu

$('#joinbtn').click(function() {
  hideAll();
  showElement('#joinmenu');
});

$('#newbtn').click(function() {
  hideAll();
  showElement('#newmenu');
});

//  Join Game Menu

$('#joinmenu-back').click(function() {
  hideAll();
  showElement('#mainmenu');
});

$('#joinmenu-go').click(function() {
  var code = $('#joinincode').val();
  var name = $('#joininname').val();

  if (name.length > 1 && code.length === 4) {
    socket.emit('joinGame', {
      code,
      name
    });
  }
});

//  New Game Menu

$('#newmenu-back').click(function() {
  hideAll();
  showElement('#mainmenu');
});

$('#newmenu-go').click(function() {
  var name = $('#newinname').val();

  if (name.length > 1) {
    socket.emit('newGame', {
      name
    });
  }
});

//  Lobby

$('#lobby-leave').click(function() {
  //refresh the page
  location.reload();
});

$('#lobby-start').click(function() {
  socket.emit('tryStartGame', {});
});

function showLobby(data) {
  if (data.success) {
    hideAll();
    showElement('#lobby');
    gameCode = data.game.code;
    setTitle('Game Code: ' + gameCode);
    setSubtitle('Waiting for players...');
    updatePlayerList(data.game.players);
  } else {
    alert(data.error);
  }
}

function updatePlayerList(list) {
  var playerList = $('#lobby-players');

  playerList.empty();

  for (var i = 0; i < list.length; i++) {
    var listItem = $('
' + list[i].name + '
').appendTo(playerList);
    listItem.addClass('list-group-item');
  }
}

//  Game

function showGame() {
  hideAll();
  hideLinkCreators();
  showElement('#game');

  setSubtitle('Game in progress');
}

function nextLink(data) {
  var lastLink = data.link
  var lastLinkType = lastLink.type;
  var newLinkType = oppositeLinkType(lastLinkType);
  var doneButton = $("#game-send");

  showGame();
  showElement('#game-buttons');

  if (lastLinkType === 'drawing') {
    //show the word creator
    showElement('#game-word');

    //show the previous drawing
    $('#game-word-drawingtoname').attr("src", lastLink.data);

    setTitle('What is this a drawing of?');

  } else if (lastLinkType === 'word'){
    //clear the previous drawing
    canvas.clear();

    //show drawing creator
    showElement('#game-drawing');

    //bind clear canvas to clear drawing button
    $('#game-cleardrawing').off('click');
    $('#game-cleardrawing').click(function() {
      canvas.clear();
    });

    //show clear button
    showElement('#game-cleardrawing');

    //calculate size of canvas dynamically
    resizeCanvas();

    setTitle('Please draw: ' + lastLink.data);
  }

  doneButton.off("click");
  doneButton.click(done);

  //run done when enter key is pressed in word input
  $('#game-word-in').unbind("keypress");
  $('#game-word-in').keypress(function(e) {
    var key = e.which;
    if (key === 13) {
       done();
    }
  });

  function done() {
    var newLink;
    if (newLinkType === 'drawing') {
      if (isDrawingBlank()) {
        alert('Please draw something!');
      } else {
        uploadCanvas(function(url) {
          newLink = url;
          send();
        }, function() {
          //reshow the canvas and allow the user to try again
          showElement('#game-drawing');
          showElement('#game-buttons');
          setTitle('Upload failed, try again.');
        });
      }
    } else if (newLinkType === 'word') {
      newLink = $('#game-word-in').val().trim();
      //check if it is blank
      if (newLink === '') {
        alert('Please enter a guess!');
      } else {
        //clear the input
        $('#game-word-in').val('')
        send();
      }
    }

    function send() {
      setTitle('Sending...');
      //hide the drawing
      hideLinkCreators();

      socket.emit('finishedLink', {
        link: {
          type: newLinkType,
          data: newLink
        }
      });
      startWaiting();
    }
  }
}

function isDrawingBlank() {
    var blank = document.createElement('canvas');
    blank.width = canvas.width;
    blank.height = canvas.height;

    return canvas.toDataURL() == blank.toDataURL();
}

function roundOver(data) {
  returnToLobby('The round is over!');
}

function someoneLeft(data) {
  returnToLobby(data.name + ' disconnected.');
}

function returnToLobby(message) {
  hideAll();
  showElement('#lobby');
  setTitle('Game Code: ' + gameCode);
  setSubtitle('Waiting for players...');
  alert(message);
}

function hideLinkCreators() {
  $('#game-drawing').addClass('hidden');
  $('#game-word').addClass('hidden');
  $('#game-buttons').addClass('hidden');
}

function uploadCanvas(next, err) {
  // this code was copied from:
  // http://community.mybb.com/thread-150592.html
  // https://github.com/blueimp/JavaScript-Canvas-to-Blob#usage

  var file = canvas.toDataURL('image/png');
  var blob = window.dataURLtoBlob(file);
  var formData = new FormData();
  formData.append('upload', blob, 'drawing.png');
  var xhr = new XMLHttpRequest();
  xhr.open("POST", "http://uploads.im/api");
  xhr.onload = function() {
    var res = JSON.parse(xhr.responseText);
    if (res.status_code === 200) {
      var url = res.data.img_url;
      next(url);
    } else {
      err();
    }
  }
  xhr.onerror = err;
  try {
    xhr.send(formData);
  } catch (e) {
    err();
  }
}

//  Result

function viewResults(data) {
  var ourChain = data.links;
  var ourName = ourChain[0].player.name;

  hideAll();
  showElement('#result');

  setTitle(ourName + "'s Drawphone results");
  setSubtitle('Show everyone how it turned out!');

  var results = $('#result-content');

  for (var i = 0; i < data.links.length; i++) {
    var link = data.links[i];
    if (i === 0) {
      results.append('
The first word:

' + link.data + '
');
    } else if (link.type === 'drawing') {
      results.append('
' + link.player.name + ' drew:
');
    } else if (link.type === 'word') {
      results.append('
' + link.player.name + ' thought that was:

' + link.data + '
');
    } else {
      console.log('We should never get here');
    }
  }

  var doneButton = $('#result-done');
  doneButton.off('click');
  doneButton.on('click', function() {
    hideAll();
    setTitle('Thanks for playing Drawphone!');
    setSubtitle('Waiting for other players...');
    socket.emit('doneViewingResults', {});
  });
}

//  Waiting

function startWaiting() {
  hideAll();
  showElement('#waiting');
  setTitle('Waiting for other players...');
}

//ran when we receive updateWaitingList from the server
function updateWaitingList(data) {
  var list = data.players;
  var waitingList = $('#waiting-players');

  waitingList.empty();

  for (var i = 0; i < list.length; i++) {
    var listItem = $('
' + list[i].name + '
').appendTo(waitingList);
    listItem.addClass('list-group-item');
  }
}

//  UI Methods

function hideAll() {
  $('#mainmenu').addClass('hidden');
  $('#joinmenu').addClass('hidden');
  $('#newmenu').addClass('hidden');
  $('#lobby').addClass('hidden');
  $('#game').addClass('hidden');
  $('#result').addClass('hidden');
  $('#waiting').addClass('hidden');

  $('#game-cleardrawing').addClass('hidden');
  $('#result-content').empty();

}

function showElement(jq) {
  $(jq).removeClass('hidden');
}

function setTitle(newTitle) {
  $('#title').text(newTitle);
}

function setSubtitle(newSubtitle) {
  $('#subtitle').text(newSubtitle);
}

function oppositeLinkType(linkType) {
  if (linkType === 'drawing') {
    return 'word';
  } else {
    return 'drawing';
  }
}

//
//  Real-time Communication via Socket.IO
//

socket.on('disconnect', function() {
  alert('Connection lost!');
  //refresh the page
  location.reload();
});

socket.on('joinGameRes', showLobby);

socket.on('updatePlayerList', updatePlayerList);

socket.on('gameStart', showGame);

socket.on('nextLink', nextLink);

socket.on('roundOver', roundOver);

socket.on('someoneLeft', someoneLeft);

socket.on('viewResults', viewResults);

socket.on('updateWaitingList', updateWaitingList);
<|endoftext|>


<commit_before>'use strict';

/**
 * App mock without http server
 * to run in spec tests or with command line tools
 */

var app = {};
exports = module.exports = app;

var mongoose = require('mongoose');
var config = require('../config')();

app.config = config;
app.mongoose = mongoose;

require('../modules/apputil')(app);

app.connect = function(callback) {

	app.db = mongoose.createConnection(config.mongodb.prefix + config.mongodb.dbname);
	app.db.on('error', console.error.bind(console, 'Headless mock mongoose connection error: '));

	app.db.once('open', function() {

		//config data models
		var models = require('../models');
		models.requirements = {
			mongoose: mongoose,
			db: app.db,
			autoIndex: true
		};
		models.load();

		callback();
	});
};

app.disconnect = function(callback) {
	app.db.close(function () {
        if (callback) {
            callback();
        }
	});
};

/**
 * Load a service
 *
 * @param {String} path
 *
 * @return {apiService}
 */
app.getService = function(path) {
    var apiservice = require('restitute').service;
    var getService = require('../api/services/'+path);
    return getService(apiservice, app);
}
<commit_msg>jshint fix<commit_after>'use strict';

/**
 * App mock without http server
 * to run in spec tests or with command line tools
 */

var app = {};
exports = module.exports = app;

var mongoose = require('mongoose');
var config = require('../config')();

app.config = config;
app.mongoose = mongoose;

require('../modules/apputil')(app);

app.connect = function(callback) {

	app.db = mongoose.createConnection(config.mongodb.prefix + config.mongodb.dbname);
	app.db.on('error', console.error.bind(console, 'Headless mock mongoose connection error: '));

	app.db.once('open', function() {

		//config data models
		var models = require('../models');
		models.requirements = {
			mongoose: mongoose,
			db: app.db,
			autoIndex: true
		};
		models.load();

		callback();
	});
};

app.disconnect = function(callback) {
	app.db.close(function () {
        if (callback) {
            callback();
        }
	});
};

/**
 * Load a service
 *
 * @param {String} path
 *
 * @return {apiService}
 */
app.getService = function(path) {
    var apiservice = require('restitute').service;
    var getService = require('../api/services/'+path);
    return getService(apiservice, app);
};
<|endoftext|>