<commit_before>
//Mustache.compilePartial('title', "{{#param}}http://purl.org/dc/elements/1.1/title{{/param}}");

var untitled =
function(){
    return function(val, render){
        var rend = render(val);
        if(/^\s*$/.test(rend)){
            return "untitled";
        }else{
            return rend;
        }
    };
};

var first =
function(){
    return function(val, render){
         return render(val);
    };
};

function joinString(array, joinStr){
    return _.reduce(array, function(accum,val){ return accum+joinStr+val; }, "");
}

function flat(fn){
    return function(context){
        //context.first = context.children[0];
        //context.second = context.children[1];
        //context.third = context.children[2];
        //context.fourth = context.children[3];
        //context.fifth = context.children[4];
        //context.sixth = context.children[5];
        context.children = joinString(context.children, "");
        return fn(context);
    };
};

//Mustache.compilePartial('untitled', "{{#untitled}}{{title}}{{/untitled}}");

var projector = flat(Mustache.compile(
"{{#untitled}}{{{children}}}{{/untitled}}" ));

var listItem = Mustache.compile(
"
"
+ "{{{children.1}}}
");

var li = flat(Mustache.compile(
"
"
+ "{{{children}}}
"));

var column8 = flat(Mustache.compile(
"
{{{children}}}
"));

var column4 = flat(Mustache.compile(
"
{{{children}}}
"));

var row = flat(Mustache.compile(
"
{{{children}}}
"));

var list = flat(Mustache.compile(
"{{label}}:
"
+ "{{{children}}}
"));

var span = flat(Mustache.compile(
"{{before}}{{#untitled}}{{{children}}}{{/untitled}}{{after}}"));

var join = function(context){
    return joinString(context.children,context.joinWith);
};

var deleter = flat(Mustache.compile(
"{{label}}"));

var creator = flat(Mustache.compile(
"Create"));

var checkbox = Mustache.compile(
"<input type='checkbox' class='checkbox-binding' data-uri='{{{uri}}}' data-binding='{{{binding}}}' value='{{{children.0}}}' {{#children.1}}checked='checked'{{/children.1}}>");

var check = flat(Mustache.compile(
"checked='checked'"));

var value = flat(Mustache.compile(
"{{{uri}}}"));

var paragraph = flat(Mustache.compile(
"
{{label}}: {{{children}}}
"));

var textField = flat(Mustache.compile(
"{{{label}}} <input type='text' class='text-field-binding' data-uri='{{{uri}}}'"
+ "data-binding='{{{binding}}}' value='{{{children}}}'>"));

var popup = flat(Mustache.compile(
"{{label}}"
+ "
"
+"
"
+"    <button type='button' class='close' data-dismiss='modal' aria-hidden='true'>×</button>"
+"
{{label}}
"
+"
"
+"
"
+"{{{children}}}"
+"
"
+"
"
+"    <button class='btn' data-dismiss='modal' aria-hidden='true'>Done</button>"
+"
"
+"
"));

var string = Mustache.compile(
"{{{label}}}");

var datepicker = flat(Mustache.compile(
"{{label}} <input class='date-binding' data-provide='datepicker' data-date-format='dd-mm-yyyy' data-binding='{{{binding}}}' data-uri='{{{uri}}}' size='12' type='text' value='{{{children}}}'>"));
<commit_msg>fix bug with checkboxes<commit_after>
//Mustache.compilePartial('title', "{{#param}}http://purl.org/dc/elements/1.1/title{{/param}}");

var untitled =
function(){
    return function(val, render){
        var rend = render(val);
        if(/^\s*$/.test(rend)){
            return "untitled";
        }else{
            return rend;
        }
    };
};

var first =
function(){
    return function(val, render){
         return render(val);
    };
};

function joinString(array, joinStr){
    return _.reduce(array, function(accum,val){ return accum+joinStr+val; }, "");
}

function flat(fn){
    return function(context){
        //context.first = context.children[0];
        //context.second = context.children[1];
        //context.third = context.children[2];
        //context.fourth = context.children[3];
        //context.fifth = context.children[4];
        //context.sixth = context.children[5];
        context.children = joinString(context.children, "");
        return fn(context);
    };
};

//Mustache.compilePartial('untitled', "{{#untitled}}{{title}}{{/untitled}}");

var projector = flat(Mustache.compile(
"{{#untitled}}{{{children}}}{{/untitled}}" ));

var listItem = Mustache.compile(
"
"
+ "{{{children.1}}}
");

var li = flat(Mustache.compile(
"
"
+ "{{{children}}}
"));

var column8 = flat(Mustache.compile(
"
{{{children}}}
"));

var column4 = flat(Mustache.compile(
"
{{{children}}}
"));

var row = flat(Mustache.compile(
"
{{{children}}}
"));

var list = flat(Mustache.compile(
"{{label}}:
"
+ "{{{children}}}
"));

var span = flat(Mustache.compile(
"{{before}}{{#untitled}}{{{children}}}{{/untitled}}{{after}}"));

var join = function(context){
    return joinString(context.children,context.joinWith);
};

var deleter = flat(Mustache.compile(
"{{label}}"));

var creator = flat(Mustache.compile(
"Create"));

var checkbox = Mustache.compile(
"<input type='checkbox' class='checkbox-binding' data-uri='{{{uri}}}' data-binding='{{{binding}}}' value='{{{children.1}}}' {{#children.0}}checked='checked'{{/children.0}}>");

var check = flat(Mustache.compile(
"checked='checked'"));

var value = flat(Mustache.compile(
"{{{uri}}}"));

var paragraph = flat(Mustache.compile(
"
{{label}}: {{{children}}}
"));

var textField = flat(Mustache.compile(
"{{{label}}} <input type='text' class='text-field-binding' data-uri='{{{uri}}}'"
+ "data-binding='{{{binding}}}' value='{{{children}}}'>"));

var popup = flat(Mustache.compile(
"{{label}}"
+ "
"
+"
"
+"    <button type='button' class='close' data-dismiss='modal' aria-hidden='true'>×</button>"
+"
{{label}}
"
+"
"
+"
"
+"{{{children}}}"
+"
"
+"
"
+"    <button class='btn' data-dismiss='modal' aria-hidden='true'>Done</button>"
+"
"
+"
"));

var string = Mustache.compile(
"{{{label}}}");

var datepicker = flat(Mustache.compile(
"{{label}} <input class='date-binding' data-provide='datepicker' data-date-format='dd-mm-yyyy' data-binding='{{{binding}}}' data-uri='{{{uri}}}' size='12' type='text' value='{{{children}}}'>"));
<|endoftext|>


<commit_before>// appCacheNanny
// =============
//
// Teaches your applicationCache some manners! Because, you know,
// http://alistapart.com/article/application-cache-is-a-douchebag
//

/* global define */
'use strict';

(function (root, factory) {
  var appCache = (typeof applicationCache === 'undefined') ? undefined : applicationCache;

  // based on https://github.com/allouis/minivents/blob/master/minivents.js
  function Events(){
    var events = {};
    var api = this;

    // listen to events
    api.on = function on(type, func, ctx){
      if (!events[type]) (events[type] = []);
      events[type].push({f:func, c:ctx});
    };

    // stop listening to event / specific callback
    api.off = function off(type, func){
      var list = events[type] || [];
      var i = list.length = func ? list.length : 0;
      while(i-->0) if (func === list[i].f) list.splice(i,1);
    };

    // send event, callbacks will be triggered
    api.trigger = function trigger(){
      var args = Array.apply([], arguments);
      var list = events[args.shift()] || [];
      var i = list.length;
      var j;
      for(j=0;j<i;j++) list[j].f.apply(list[j].c, args);
    };

    // aliases
    api.bind = api.on;
    api.unbind = api.off;
    api.emit = api.trigger;
  }

  if (typeof define === 'function' && define.amd) {
    define([], function () {
      root.appCacheNanny = factory(appCache, Events);
      return root.appCacheNanny;
    });
  } else if (typeof exports === 'object') {
    module.exports = factory(appCache, Events);
  } else {
    root.appCacheNanny = factory(appCache, Events);
  }
})(this, function(applicationCache, Events){ // jshint ignore:line

  var DEFAULT_MANIFEST_LOADER_PATH = '/appcache-loader.html';
  var DEFAULT_CHECK_INTERVAL = 30000;

  var appCacheNanny = new Events();
  var nannyOptions = {
    loaderPath: DEFAULT_MANIFEST_LOADER_PATH,
    checkInterval: DEFAULT_CHECK_INTERVAL,
    offlineCheckInterval: DEFAULT_CHECK_INTERVAL
  };

  var iframe;
  var setupDone = false;
  var setupPending = false;

  //
  //
  //
  appCacheNanny.isSupported = function isSupported() {
    return !! applicationCache;
  };

  //
  // request the appcache.manifest file and check if there's an update
  //
  appCacheNanny.update = function update() {
    trigger('update');
    if (! setupDone) {
      setupCallbacks.push(appCacheNanny.update);
      if (! setupPending) {
        setup();
        setupPending = true;
      }
      return true;
    }
    if (! appCacheNanny.isSupported()) return false;
    try {
      applicationCache.update();
      return true;
    } catch (e) {
      // there might still be cases when ApplicationCache is not support
      // e.g. in Chrome, when returned HTML is status code 40X, or if
      // the applicationCache became obsolete
      appCacheNanny.update = noop;
      return false;
    }
  };

  //
  // start auto updating. Optionally pass interval in ms to
  // overwrite the current.
  //
  var intervalPointer;
  appCacheNanny.start = function start(options) {
    if (options) appCacheNanny.set(options);

    if (! setupDone) {
      setupCallbacks.push(appCacheNanny.start);
      if (! setupPending) {
        setup();
        setupPending = true;
      }
      return true;
    }

    clearInterval(intervalPointer);

    // check with offline interval
    checkInterval = hasNetworkError ? appCacheNanny.get('offlineCheckInterval') : appCacheNanny.get('checkInterval');

    intervalPointer = setInterval(appCacheNanny.update, checkInterval);
    isCheckingForUpdatesFlag = true;
    trigger('start');
  };

  //
  // stop auto updating
  //
  appCacheNanny.stop = function stop() {
    if (! isCheckingForUpdatesFlag) return;
    clearInterval(intervalPointer);
    isCheckingForUpdatesFlag = false;
    trigger('stop');
  };

  //
  // returns true if the nanny is checking periodically for updates
  //
  appCacheNanny.isCheckingForUpdates = function isCheckingForUpdates() {
    return isCheckingForUpdatesFlag;
  };

  //
  // returns true if an update has been fully received, otherwise false
  //
  appCacheNanny.hasUpdate = function hasUpdate() {
    return hasUpdateFlag;
  };

  //
  //
  //
  appCacheNanny.set = function setOption(key,  value) {
    var property, newSettings;
    if (typeof key === 'object') {
      newSettings = key;
      for (property in newSettings) if (newSettings.hasOwnProperty(property)) {
        nannyOptions[property] = newSettings[property];
      }
      return;
    }
    nannyOptions[key] = value;
  };

  //
  //
  //
  appCacheNanny.get = function getOption(key) {
    var property, settings = {};
    if (key) return nannyOptions[key];

    for (property in nannyOptions) if (nannyOptions.hasOwnProperty(property)) {
      settings[property] = nannyOptions[property];
    }
    return settings;
  };

  // Private
  // -------

  // this is the internal state of checkInterval.
  // It usually differs between online / offline state
  var checkInterval = DEFAULT_CHECK_INTERVAL;

  // flag if there is a pending update, being applied after next page reload
  var hasUpdateFlag = false;

  // flag whether the nanny is checking for updates in the background
  var isCheckingForUpdatesFlag = false;

  // flag if there was an error updating the appCache, usually meaning
  // it couldn't connect, a.k.a. you're offline.
  var hasNetworkError = false;

  //
  var isInitialDownload = false;

  //
  // setup appCacheNanny
  //
  var noop = function(){};
  var APPCACHE_STORE_KEY = '_appcache_nanny';
  var setupCallbacks = [];
  function setup() {
    var scriptTag;

    try {
      isInitialDownload = ! localStorage.getItem(APPCACHE_STORE_KEY);
      localStorage.setItem(APPCACHE_STORE_KEY, '1');
    } catch(e) {}

    if (! appCacheNanny.isSupported()) {
      appCacheNanny.update = noop;
      return;
    }

    // https://github.com/gr2m/appcache-nanny/issues/7
    if (applicationCache.status !== applicationCache.UNCACHED) {
      subscribeToEvents();
      setupPending = false;
      setupDone = true;
      setupCallbacks.forEach(function(callback) {
        callback();
      });
      return;
    }

    // load the appcache-loader.html using an iframe
    iframe = document.createElement('iframe');
    iframe.src = nannyOptions.loaderPath;
    iframe.style.display = 'none';
    iframe.onload = function() {
      // we use the iFrame's applicationCache Object now
      applicationCache = iframe.contentWindow.applicationCache;

      subscribeToEvents();
      setupPending = false;
      setupDone = true;

      // adding a timeout prevented Safari 7.1.4 from throwing
      // a InvalidStateError on the first applicationCache.update() call
      setTimeout(function () {
        setupCallbacks.forEach(function(callback) {
          callback();
        });
      }, 100);
    };
    iframe.onerror = function() {
      throw new Error('/appcache-loader.html could not be loaded.');
    };

    scriptTag = document.getElementsByTagName('script')[0];
    scriptTag.parentNode.insertBefore(iframe,scriptTag);
  }

  //
  //
  //
  function subscribeToEvents () {
    // Fired when the manifest resources have been downloaded.
    on('updateready', handleUpdateReady);

    // fired when manifest download request failed
    // (no connection or 5xx server response)
    on('error',        handleNetworkError);

    // fired when manifest download request succeeded
    // but server returned 404 / 410
    on('obsolete',     handleNetworkObsolete);

    // fired when manifest download succeeded
    on('noupdate',     handleNetworkSucces);
    on('cached',       handleNetworkSucces);
    on('progress',     handleNetworkSucces);
    on('downloading',  handleNetworkSucces);

    // when browser goes online/offline, look for updates to make sure.
    addEventListener('online', appCacheNanny.update, false);
    addEventListener('offline', appCacheNanny.update, false);
  }

  //
  // interface to bind events to cache events
  //
  function on(eventName, callback) {
    applicationCache.addEventListener(eventName, callback, false);
  }

  //
  // Trigger event on appCacheNanny. Once an update is ready, we
  // keep looking for another update, but we stop triggering events.
  //
  function trigger(eventName) {
    if (hasUpdateFlag) return;
    appCacheNanny.trigger(eventName);
  }

  //
  //
  //
  var pendingUpdateReady = false;
  function handleUpdateReady () {
    // Safari and Firefox (in private mode) can get into an invalid
    // applicationCache state, which throws an InvalidStateError error
    // on applicationCache.swapCache(). To workaround that, we reset
    // everything and set a flag that the next "noupdate" event, that
    // will now be triggered when the iframe gets reloadd, is actually
    // an "updateready" event.
    if (applicationCache.status !== applicationCache.UPDATEREADY) {
      pendingUpdateReady = true;
      reset();
      return;
    }

    if (! hasUpdateFlag) {
      hasUpdateFlag = true;
      // don't use trigger here, otherwise the event wouldn't get triggered
      appCacheNanny.trigger('updateready');
    }
    applicationCache.swapCache();
  }

  //
  //
  //
  function handleNetworkSucces(event) {
    var prefix = '';

    // when page gets opened for the very first time, it already has
    // the correct assets, but appCache still triggers 'downloading',
    // 'progress' and 'cached' events. Once the first 'cached' event
    // gets triggered, all assets are cached offline. We prefix these
    // initial events with 'init:'
    if (isInitialDownload) {
      prefix = 'init:';
      if (event.type === 'cached') {
        isInitialDownload = false;
      }
    }

    // re-trigger event via appCacheNanny
    if (pendingUpdateReady) {
      trigger('updateready')
      pendingUpdateReady = false
    } else {
      trigger(prefix + event.type);
    }

    if (! hasNetworkError) return;
    hasNetworkError = false;

    appCacheNanny.start();
    trigger('online');
  }

  //
  //
  //
  function handleNetworkError () {
    // re-trigger event via appCacheNanny
    trigger('error');

    if (hasNetworkError) return;
    hasNetworkError = true;

    // Edge case: private mode in Safari & FF say they support applicationCache,
    // but they fail. To get arround that, we only trigger the offline event
    // when applicationCache.status != uncached
    if (applicationCache.status === applicationCache.UNCACHED) return;

    appCacheNanny.start();

    trigger('offline');
  }

  //
  // The 'obsolete' event gets triggered if the requested *.appcache file
  // has been removed or renamed. The intent behind renaming an *.appcache
  // file is to clear all locally cached files, it's the only way to do so.
  // Therefore we don't treet it as an error, it usually means that there
  // is an update availble that becomes visible after the next page reload.
  //
  function handleNetworkObsolete () {
    // re-trigger event via appCacheNanny
    trigger('obsolete');

    if (hasNetworkError) {
      hasNetworkError = false;
      trigger('online');
    }

    // Once applicationCache status is obsolete, calling .udate() throws
    // an error, so we stop checking here
    appCacheNanny.stop();
  }

  function reset () {
    if (iframe) {
      iframe.remove();
    }

    setupDone = false;
    setupPending = false;
    appCacheNanny.update();
  }

  return appCacheNanny;
});
<commit_msg>removing app-cache-nanny.js in the route - i dont think it was being used<commit_after><|endoftext|>


<commit_before>'use strict';

var React = require('react-native');
var {
  Image,
  PixelRatio,
  ScrollView,
  StyleSheet,
  Text,
  TouchableOpacity,
  TouchableHighlight,
  View,
  Component
} = React;

var Icon = require('FontAwesome'),
    getImage = require('./helpers/getImage'),
    HTML = require('react-native-htmlview'),
    screen = require('Dimensions').get('window'),
    ParallaxView = require('react-native-parallax-view'),
    Modal = require('react-native-modal');

var Player = require('./Player');

var ShotDetails = React.createClass({
  getInitialState: function() {
    return {
      isModalOpen: false
    }
  },

  openModal: function() {
    this.setState({
      isModalOpen: true
    });
  },

  closeModal: function() {
    this.setState({
      isModalOpen: false
    });
  },

  render: function() {
    var player = this.props.shot.player;

    return (
      <ParallaxView
        backgroundSource={getImage.shotImage(this.props.shot)}
        windowHeight={300}
        header={(
          <TouchableOpacity onPress={this.openModal}>
            <View style={styles.invisibleView}></View>
          </TouchableOpacity>
        )}
        >
        <View>
          <TouchableHighlight style={styles.invisibleTouch}
                              onPress={this.selectPlayer.bind(this, player)}>
            <View style={styles.headerContent}>
              <Image source={getImage.authorAvatar(player)}
                     style={styles.playerAvatar} />
              <Text style={styles.shotTitle}>{this.props.shot.title}</Text>
              <Text style={styles.playerContent}>by <Text style={styles.player}>{player.name}</Text></Text>
            </View>
          </TouchableHighlight>
          <View style={styles.mainSection}>
            <View style={styles.shotDetailsRow}>
              <View style={styles.shotCounter}>
                <Icon name="heart-o" size={24} color="#333"/>
                <Text style={styles.shotCounterText}> {this.props.shot.likes_count} </Text>
              </View>
              <View style={styles.shotCounter}>
                <Icon name="comments-o" size={24} color="#333"/>
                <Text style={styles.shotCounterText}> {this.props.shot.comments_count} </Text>
              </View>
              <View style={styles.shotCounter}>
                <Icon name="eye" size={24} color="#333"/>
                <Text style={styles.shotCounterText}> {this.props.shot.views_count} </Text>
              </View>
            </View>
            <View style={styles.separator} />
            <Text>
              <HTML value={this.props.shot.description}
                    stylesheet={styles}/>
            </Text>
          </View>
        </View>
        <Modal isVisible={this.state.isModalOpen}
               onClose={this.closeModal}
               backdropType="blur"
               backdropBlur="dark"
               forceToFront={true}
               customShowHandler={this._showModalTransition}
               customHideHandler={this._hideModalTransition}
               onPressBackdrop={this.closeModal}>
          <Image source={getImage.shotImage(this.props.shot)}
                 style={styles.customModalImage}
                 resizeMode="contain"/>
        </Modal>
      </ParallaxView>
    );
  },

  _showModalTransition: function(transition) {
    transition('opacity', {
      duration: 200,
      begin: 0,
      end: 1
    });
    transition('height', {
      duration: 200,
      begin: - screen.height * 2,
      end: screen.height
    });
  },

  _hideModalTransition: function(transition) {
    transition('height', {
      duration: 200,
      begin: screen.height,
      end: screen.height * 2,
      reset: true
    });
    transition('opacity', {
      duration: 200,
      begin: 1,
      end: 0
    });
  },

  selectPlayer: function(player: Object) {
    this.props.navigator.push({
      component: Player,
      passProps: {player},
      title: player.name
    });
  },
});

var styles = StyleSheet.create({
  a: {
    fontWeight: '300',
    color: '#ea4c89'
  },
  invisibleView: {
    flex: 1,
    position: 'absolute',
    top: 0,
    left: 0,
    bottom: 0,
    right:0
  },
  customModalImage: {
    height: screen.height / 2
  },
  headerContent: {
    flex: 1,
    paddingBottom: 20,
    paddingTop: 40,
    alignItems: 'center',
    width: screen.width,
    backgroundColor: '#fff'
  },
  shotTitle: {
    fontSize: 16,
    fontWeight: '400',
    color: '#ea4c89',
    lineHeight: 18
  },
  playerContent: {
    fontSize: 12
  },
  player: {
    fontWeight: '900',
    lineHeight: 18
  },
  playerAvatar: {
    borderRadius: 40,
    width: 80,
    height: 80,
    position: 'absolute',
    bottom: 60,
    left: screen.width / 2 - 40,
    borderWidth: 2,
    borderColor: '#fff'
  },
  rightPane: {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'center'
  },
  shotDetailsRow: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'white',
    flexDirection: 'row'
  },
  shotCounter: {
    flex: 2,
    alignItems: 'center',
    justifyContent: 'space-between'
  },
  shotCounterText: {
    color: '#333'
  },
  mainSection: {
    backgroundColor: 'white',
    flex: 1,
    alignItems: 'stretch',
    padding: 10
  },
  separator: {
    backgroundColor: 'rgba(0, 0, 0, 0.1)',
    height: 1 / PixelRatio.get(),
    marginVertical: 10,
  }
});

module.exports = ShotDetails;
<commit_msg>Refactor shot details #2<commit_after>"use strict";

var React = require("react-native");
var {
  Image,
  PixelRatio,
  ScrollView,
  StyleSheet,
  Text,
  TouchableOpacity,
  TouchableHighlight,
  ActivityIndicatorIOS,
  View,
  ListView,
  Component
} = React;

var api = require("./helpers/api");

var Icon = require("FontAwesome"),
    getImage = require("./helpers/getImage"),
    HTML = require("react-native-htmlview"),
    screen = require("Dimensions").get("window"),
    ParallaxView = require("react-native-parallax-view"),
    Modal = require("react-native-modal");

var Player = require("./Player");
var CommentItem = require("./CommentItem");
var Loading = require("./Loading");

var ShotDetails = React.createClass({
  getInitialState: function() {
    return {
      isModalOpen: false,
      isLoading: true,
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
    };
  },

  openModal: function() {
    this.setState({
      isModalOpen: true
    });
  },

  closeModal: function() {
    this.setState({
      isModalOpen: false
    });
  },

  componentDidMount: function() {
    api.getResources(this.props.shot.comments_url).then((responseData) => {
      this.setState({
        dataSource: this.state.dataSource.cloneWithRows(responseData),
        isLoading: false
      });
    }).done();
  },

  render: function() {
    var player = this.props.shot.user;

    return (
      <ParallaxView
        backgroundSource={getImage.shotImage(this.props.shot)}
        windowHeight={300}
        header={(
          <TouchableOpacity onPress={this.openModal}>
            <View style={styles.invisibleView}></View>
          </TouchableOpacity>
        )}
        >
        <View>
          <TouchableHighlight style={styles.invisibleTouch}
                              onPress={this.selectPlayer.bind(this, player)}
                              underlayColor={"#333"}
                              activeOpacity={0.95}>
            <View style={styles.headerContent}>
              <Image source={getImage.authorAvatar(player)}
                     style={styles.playerAvatar} />
              <Text style={styles.shotTitle}>{this.props.shot.title}</Text>
              <Text style={styles.playerContent}>by <Text style={styles.player}>{player.name}</Text></Text>
            </View>
          </TouchableHighlight>
          <View style={styles.mainSection}>
            <View style={styles.shotDetailsRow}>
              <View style={styles.shotCounter}>
                <Icon name="heart-o" size={24} color="#333"/>
                <Text style={styles.shotCounterText}> {this.props.shot.likes_count} </Text>
              </View>
              <View style={styles.shotCounter}>
                <Icon name="comments-o" size={24} color="#333"/>
                <Text style={styles.shotCounterText}> {this.props.shot.comments_count} </Text>
              </View>
              <View style={styles.shotCounter}>
                <Icon name="eye" size={24} color="#333"/>
                <Text style={styles.shotCounterText}> {this.props.shot.views_count} </Text>
              </View>
            </View>
            <View style={styles.separator} />
            <Text>
              <HTML value={this.props.shot.description}
                    stylesheet={styles}/>
            </Text>
            <View>
              {this.state.dataSource.getRowCount() === 0 ?
                <Loading /> :
                this._renderCommentsList()}
            </View>
          </View>
        </View>
        <Modal isVisible={this.state.isModalOpen}
               onClose={this.closeModal}
               backdropType="blur"
               backdropBlur="dark"
               forceToFront={true}
               customShowHandler={this._showModalTransition}
               customHideHandler={this._hideModalTransition}
               onPressBackdrop={this.closeModal}>
          <Image source={getImage.shotImage(this.props.shot)}
                 style={styles.customModalImage}
                 resizeMode="contain"/>
        </Modal>
      </ParallaxView>
    );
  },

  _showModalTransition: function(transition) {
    transition("opacity", {
      duration: 200,
      begin: 0,
      end: 1
    });
    transition("height", {
      duration: 200,
      begin: - screen.height * 2,
      end: screen.height
    });
  },

  _hideModalTransition: function(transition) {
    transition("height", {
      duration: 200,
      begin: screen.height,
      end: screen.height * 2,
      reset: true
    });
    transition("opacity", {
      duration: 200,
      begin: 1,
      end: 0
    });
  },

  selectPlayer: function(player: Object) {
    this.props.navigator.push({
      component: Player,
      passProps: {player},
      title: player.name
    });
  },

  _renderCommentsList: function() {
    return <View style={styles.sectionSpacing}>
      <View style={styles.separator} />
      <Text style={styles.heading}>Comments</Text>
      <View style={styles.separator} />
      <ListView
        ref="commentsView"
        dataSource={this.state.dataSource}
        renderRow={this.renderRow}
        automaticallyAdjustContentInsets={false}
      />
    </View>
  },

  renderRow: function(comment: Object) {
    return <CommentItem
      onSelect={() => this.selectPlayer(comment.user)}
      comment={comment} />;
  },

  _renderLoading: function() {
    return <ActivityIndicatorIOS animating={this.state.isLoading}
                                 style={styles.spinner}/>;
  }
});

var styles = StyleSheet.create({
  spinner: {
    marginTop: 20,
    width: 50
  },
  a: {
    fontWeight: "300",
    color: "#ea4c89"
  },
  p: {
    marginBottom: 0,
    flexDirection: "row",
    marginTop: 0,
  },
  invisibleView: {
    flex: 1,
    position: "absolute",
    top: 0,
    left: 0,
    bottom: 0,
    right:0
  },
  customModalImage: {
    height: screen.height / 2
  },
  headerContent: {
    flex: 1,
    paddingBottom: 20,
    paddingTop: 40,
    alignItems: "center",
    width: screen.width,
    backgroundColor: "#fff"
  },
  shotTitle: {
    fontSize: 16,
    fontWeight: "400",
    color: "#ea4c89",
    lineHeight: 18
  },
  playerContent: {
    fontSize: 12
  },
  player: {
    fontWeight: "900",
    lineHeight: 18
  },
  playerAvatar: {
    borderRadius: 40,
    width: 80,
    height: 80,
    position: "absolute",
    bottom: 60,
    left: screen.width / 2 - 40,
    borderWidth: 2,
    borderColor: "#fff"
  },
  rightPane: {
    flex: 1,
    flexDirection: "column",
    alignItems: "center"
  },
  shotDetailsRow: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    backgroundColor: "white",
    flexDirection: "row"
  },
  shotCounter: {
    flex: 2,
    alignItems: "center",
    justifyContent: "space-between"
  },
  shotCounterText: {
    color: "#333"
  },
  mainSection: {
    flex: 1,
    alignItems: "stretch",
    padding: 10,
    backgroundColor: "white"
  },
  separator: {
    backgroundColor: "rgba(0, 0, 0, 0.1)",
    height: 1 / PixelRatio.get(),
    marginVertical: 10,
  },
  sectionSpacing: {
    marginTop: 20
  },
  heading: {
    fontWeight: "700",
    fontSize: 16
  }
});

module.exports = ShotDetails;
<|endoftext|>


<commit_before>import chooseDb from '../src/chooseDb'
import getConfig from '../src/getConfig'
import saveConfig from '../src/saveConfig'
import filterForFaelligeGeschaefte from '../src/filterForFaelligeGeschaefte'
import * as GeschaefteActions from './geschaefte'
import * as GeschaefteKontakteInternActions from './geschaefteKontakteIntern'
import * as GeschaefteKontakteExternActions from './geschaefteKontakteExtern'
import * as UserActions from './user'

const sqlite3 = require('sqlite3').verbose()

export const CONFIG_GET = 'CONFIG_GET'
export const configGet = () =>
  (dispatch) => {
    getConfig()
      .then((config) => {
        let newConfig = config
        if (!newConfig) {
          newConfig = {}
        }
        dispatch({
          type: CONFIG_GET,
          config: newConfig
        })
        const { dbPath } = newConfig
        if (!dbPath) {
          dispatch(dbGetAtStandardpathIfPossible())
        } else {
          const db = new sqlite3.Database(dbPath)
          dispatch(dbChooseSuccess(dbPath, db))
        }
      })
      .catch(error =>
        console.error(error)
      )
  }

export const configUiReset = () =>
  (dispatch, getState) => {
    const { config } = getState().app
    const newConfig = {}
    const dbPath = config.dbPath
    if (dbPath) {
      newConfig.dbPath = dbPath
    }
    saveConfig(newConfig)
    dispatch({
      type: CONFIG_SET,
      config: newConfig
    })
  }

export const CONFIG_SET_KEY = 'CONFIG_SET_KEY'
export const configSetKey = (key, value) =>
  (dispatch, getState) => {
    const { config } = getState().app
    if (value) {
      config[key] = value
    } else if (config[key]) {
      delete config[key]
    }
    saveConfig(config)
    dispatch({
      type: CONFIG_SET,
      config
    })
  }

export const CONFIG_SET = 'CONFIG_SET'
export const configSet = config => ({
  type: CONFIG_SET,
  config
})

export const TABLECOLUMN_SET = 'TABLECOLUMN_SET'
export const tableColumnSet = tableColumnWidth => ({
  type: TABLECOLUMN_SET,
  tableColumnWidth
})

export const MESSAGE_SHOW = 'MESSAGE_SHOW'
export const messageShow = (
  showMessageModal,
  messageTextLine1,
  messageTextLine2
) => ({
  type: MESSAGE_SHOW,
  showMessageModal,
  messageTextLine1,
  messageTextLine2
})

export const DB_CHOOSE = 'DB_CHOOSE'
const dbChoose = () => ({
  type: DB_CHOOSE
})

export const DB_CHOOSE_SUCCESS = 'DB_CHOOSE_SUCCESS'
const dbChooseSuccess = (dbPath, db) =>
  (dispatch) => {
    dispatch({
      type: DB_CHOOSE_SUCCESS,
      db,
      dbPath
    })
    // get data
    dispatch(UserActions.fetchUsername())
    dispatch(GeschaefteActions.getGeschaefte())
    dispatch(GeschaefteActions.getGeko())
    dispatch(GeschaefteActions.getLinks())
    dispatch(GeschaefteActions.rechtsmittelErledigungOptionsGet())
    dispatch(GeschaefteActions.parlVorstossTypOptionsGet())
    dispatch(GeschaefteActions.statusOptionsGet())
    dispatch(GeschaefteActions.geschaeftsartOptionsGet())
    dispatch(GeschaefteActions.aktenstandortOptionsGet())
    dispatch(GeschaefteActions.rechtsmittelInstanzOptionsGet())
    dispatch(GeschaefteActions.abteilungOptionsGet())
    dispatch(GeschaefteActions.interneOptionsGet())
    dispatch(GeschaefteActions.externeOptionsGet())
    dispatch(GeschaefteKontakteInternActions.getGeschaefteKontakteIntern())
    dispatch(GeschaefteKontakteExternActions.getGeschaefteKontakteExtern())
    // set filter to fällige
    dispatch(GeschaefteActions.geschaefteFilterByFields(filterForFaelligeGeschaefte(), 'fällige'))
    dispatch(GeschaefteActions.geschaefteSortByFields('fristMitarbeiter', 'DESCENDING'))
  }

export const DB_CHOOSE_ERROR = 'DB_CHOOSE_ERROR'
const dbChooseError = error => ({
  type: DB_CHOOSE_ERROR,
  error
})

export function dbGet() {
  return (dispatch) => {
    // let user choose db file
    dispatch(dbChoose())
    chooseDb()
      .then((dbPath) => {
        const db = new sqlite3.Database(dbPath)
        dispatch(dbChooseSuccess(dbPath, db))
        dispatch(configSetKey('dbPath', dbPath))
      })
      .catch(err => dispatch(dbChooseError(err)))
  }
}

export function dbGetAtStandardpathIfPossible() {
  return (dispatch) => {
    const standardDbPath = 'G:\\Recht\\2 Sekretariat\\Kapla\\kapla.db'
    // try to open db at standard path
    let db = new sqlite3.Database(standardDbPath, sqlite3.OPEN_READWRITE, (error) => {
      if (error) {
        // let user choose db file
        dispatch(dbChoose())
        chooseDb()
          .then((dbPath) => {
            db = new sqlite3.Database(dbPath)
            dispatch(dbChooseSuccess(dbPath, db))
            dispatch(configSetKey('dbPath', dbPath))
          })
          .catch(err => dispatch(dbChooseError(err)))
      } else {
        dispatch(dbChooseSuccess(standardDbPath, db))
        dispatch(configSetKey('dbPath', standardDbPath))
      }
    })
  }
}
<commit_msg>optimize<commit_after>import chooseDb from '../src/chooseDb'
import getConfig from '../src/getConfig'
import saveConfig from '../src/saveConfig'
import filterForFaelligeGeschaefte from '../src/filterForFaelligeGeschaefte'
import * as GeschaefteActions from './geschaefte'
import * as GeschaefteKontakteInternActions from './geschaefteKontakteIntern'
import * as GeschaefteKontakteExternActions from './geschaefteKontakteExtern'
import * as UserActions from './user'

const sqlite3 = require('sqlite3').verbose()

export const CONFIG_GET = 'CONFIG_GET'
export const configGet = () =>
  (dispatch) => {
    getConfig()
      .then((config) => {
        let newConfig = config
        if (!newConfig) {
          newConfig = {}
        }
        dispatch({
          type: CONFIG_GET,
          config: newConfig
        })
        const { dbPath } = newConfig
        if (!dbPath) {
          dispatch(dbGetAtStandardpathIfPossible())
        } else {
          const db = new sqlite3.Database(dbPath)
          dispatch(dbChooseSuccess(dbPath, db))
        }
      })
      .catch(error =>
        console.error(error)
      )
  }

export const configUiReset = () =>
  (dispatch, getState) => {
    const { config } = getState().app
    const newConfig = {}
    const dbPath = config.dbPath
    if (dbPath) {
      newConfig.dbPath = dbPath
    }
    saveConfig(newConfig)
    dispatch({
      type: CONFIG_SET,
      config: newConfig
    })
  }

export const CONFIG_SET_KEY = 'CONFIG_SET_KEY'
export const configSetKey = (key, value) =>
  (dispatch, getState) => {
    const { config } = getState().app
    if (value) {
      config[key] = value
    } else if (config[key]) {
      delete config[key]
    }
    saveConfig(config)
    dispatch({
      type: CONFIG_SET,
      config
    })
  }

export const CONFIG_SET = 'CONFIG_SET'
export const configSet = config => ({
  type: CONFIG_SET,
  config
})

export const TABLECOLUMN_SET = 'TABLECOLUMN_SET'
export const tableColumnSet = tableColumnWidth => ({
  type: TABLECOLUMN_SET,
  tableColumnWidth
})

export const MESSAGE_SHOW = 'MESSAGE_SHOW'
export const messageShow = (
  showMessageModal,
  messageTextLine1,
  messageTextLine2
) => ({
  type: MESSAGE_SHOW,
  showMessageModal,
  messageTextLine1,
  messageTextLine2
})

export const DB_CHOOSE = 'DB_CHOOSE'
const dbChoose = () => ({
  type: DB_CHOOSE
})

export const DB_CHOOSE_SUCCESS = 'DB_CHOOSE_SUCCESS'
const dbChooseSuccess = (dbPath, db) =>
  (dispatch) => {
    dispatch({
      type: DB_CHOOSE_SUCCESS,
      db,
      dbPath
    })
    // get data
    dispatch(GeschaefteActions.getGeschaefte())
    dispatch(GeschaefteActions.getGeko())
    dispatch(GeschaefteActions.getLinks())
    dispatch(GeschaefteActions.interneOptionsGet())
    dispatch(GeschaefteActions.externeOptionsGet())
    dispatch(GeschaefteKontakteInternActions.getGeschaefteKontakteIntern())
    dispatch(GeschaefteKontakteExternActions.getGeschaefteKontakteExtern())
    dispatch(GeschaefteActions.rechtsmittelErledigungOptionsGet())
    dispatch(GeschaefteActions.parlVorstossTypOptionsGet())
    dispatch(GeschaefteActions.statusOptionsGet())
    dispatch(GeschaefteActions.geschaeftsartOptionsGet())
    dispatch(GeschaefteActions.aktenstandortOptionsGet())
    dispatch(GeschaefteActions.rechtsmittelInstanzOptionsGet())
    dispatch(GeschaefteActions.abteilungOptionsGet())
    dispatch(UserActions.fetchUsername())
    // set filter to fällige
    dispatch(GeschaefteActions.geschaefteFilterByFields(filterForFaelligeGeschaefte(), 'fällige'))
    dispatch(GeschaefteActions.geschaefteSortByFields('fristMitarbeiter', 'DESCENDING'))
  }

export const DB_CHOOSE_ERROR = 'DB_CHOOSE_ERROR'
const dbChooseError = error => ({
  type: DB_CHOOSE_ERROR,
  error
})

export function dbGet() {
  return (dispatch) => {
    // let user choose db file
    dispatch(dbChoose())
    chooseDb()
      .then((dbPath) => {
        const db = new sqlite3.Database(dbPath)
        dispatch(dbChooseSuccess(dbPath, db))
        dispatch(configSetKey('dbPath', dbPath))
      })
      .catch(err => dispatch(dbChooseError(err)))
  }
}

export function dbGetAtStandardpathIfPossible() {
  return (dispatch) => {
    const standardDbPath = 'G:\\Recht\\2 Sekretariat\\Kapla\\kapla.db'
    // try to open db at standard path
    let db = new sqlite3.Database(standardDbPath, sqlite3.OPEN_READWRITE, (error) => {
      if (error) {
        // let user choose db file
        dispatch(dbChoose())
        chooseDb()
          .then((dbPath) => {
            db = new sqlite3.Database(dbPath)
            dispatch(dbChooseSuccess(dbPath, db))
            dispatch(configSetKey('dbPath', dbPath))
          })
          .catch(err => dispatch(dbChooseError(err)))
      } else {
        dispatch(dbChooseSuccess(standardDbPath, db))
        dispatch(configSetKey('dbPath', standardDbPath))
      }
    })
  }
}
<|endoftext|>


<commit_before>var oauth = require('oauth');
var querystring = require('querystring');
var path = require('path');
var debug = require('debug')('tradeking');
var util = require('util');
var EE = require('events').EventEmitter;

var Promise = require('bluebird').Promise;

module.exports = Tradeking;

function Tradeking(config) {
  EE.call(this);

  if (!config)
    throw new Error("OAuth 1.0a config not supplied");

  var consumer = new oauth.OAuth(
    config.requestUrl         || null,    // https://developers.tradeking.com/oauth/request_token
    config.accessUrl          || null,     // https://developers.tradeking.com/oauth/access_token
    config.consumerKey        || null,
    config.consumerSecret     || null,
    config.version            || "1.0",
    config.authorize_callback || null,
    config.signatureMethod    || "HMAC-SHA1",
    config.nonceSize          || null,
    config.customHeaders      || null
  );

  var self = this;
  self._config = config;
  self._consumer = consumer;

  Promise.promisifyAll(self);

  self.getMarketClockAsync()
      .get(0)
      .then(function(clockData) {

        /* TODO: ditch this manual timestamp crap and use moment.js */
        var serverTimestamp = parseInt(JSON.parse(clockData).response.unixtime);
        var unixtimeNow = Math.floor( (new Date()).getTime() / 1000 );
        var offset = unixtimeNow - serverTimestamp;

        self._consumer._getTimestamp = function() {
          var unixtime = Math.floor( (new Date()).getTime() / 1000 );

          debug("now: %s, serverTimestamp: %s, timeOffset: %s, curtime: %s, adjTime: %s",
                unixtimeNow, serverTimestamp, offset, unixtime, unixtime - offset);

          return unixtime - offset;
        }

        self.emit('ready');
      })
      .catch(function(error) {
        throw error;
      });
}
util.inherits(Tradeking, EE);

/* --- Order/Trade Calls ------------------------------------------------- */

// Tradeking.prototype.postAccountOrderPreview = function (accountId, fixml, cb) {
//   var uri = path.join('accounts/', accountId, '/orders/preview.json');
//   return this.post(uri, fixml, cb);
// }

/* --- Market Calls ------------------------------------------------- */

Tradeking.prototype.getMarketClock = function (cb) {
  return this.get('/market/clock.json', cb);
}

Tradeking.prototype.getMarketNewsSearch = function (searchOpts, cb) {
  // TODO: validate searchOpts against verbs permitted by TK API
  var uri = '/market/news/search.json?' + querystring.stringify(searchOpts);
  debug("getMarketNewsSearch() - uri: %s", uri);
  return this.get(uri, cb);
}

Tradeking.prototype.getMarketNewsStory = function (id, cb) {
  // TODO: validate id against the format expected by TK API
  var uri = path.join('/market/news/', id + '.json')
  debug("getMarketNewsSearch() - uri: %s", uri);
  return this.get(uri, cb);
}

Tradeking.prototype.getMarketToplists = function (toplist, cb) {
  var validToplists = ["toplosers", "toppctlosers", "topvolume",
                       "topactive", "topgainers", "toppctgainers",
                       "topactivegainersbydollarvalue"];

  if (0 > validToplists.indexOf(toplist))
    throw TypeError("'" + toplist + "' not found in: " + validToplists.toString());

  var uri = path.join('/market/toplists/', toplist + '.json');
  debug("getMarketToplists() - uri: %s", uri);
  return this.get(uri, cb);
}

/* --- Account Calls ------------------------------------------------- */

Tradeking.prototype.getAccounts = function (cb) {
  return this.get('/accounts.json', cb);
}

Tradeking.prototype.getAccountBalances = function (cb) {
  return this.get('/accounts/balances.json', cb);
}

/* --- Low-level Calls ------------------------------------------------- */

Tradeking.prototype.get = function (uri, cb) {
  return this._consumer.get(
           this._config.apiUrl + uri,
           this._config.accessToken,
           this._config.accessSecret,
           cb
         );
}

Tradeking.prototype.post = function (uri, fixmlData, cb) {
  return this._consumer.post(
           this._config.apiUrl + uri,
           this._config.accessToken,
           this._config.accessSecret,
           fixmlData, 'text/xml', cb
         );
}

<commit_msg>src/tradeking.js: make low-level POST method more generic<commit_after>var oauth = require('oauth');
var querystring = require('querystring');
var path = require('path');
var debug = require('debug')('tradeking');
var util = require('util');
var EE = require('events').EventEmitter;

var Promise = require('bluebird').Promise;

module.exports = Tradeking;

function Tradeking(config) {
  EE.call(this);

  if (!config)
    throw new Error("OAuth 1.0a config not supplied");

  var consumer = new oauth.OAuth(
    config.requestUrl         || null,    // https://developers.tradeking.com/oauth/request_token
    config.accessUrl          || null,     // https://developers.tradeking.com/oauth/access_token
    config.consumerKey        || null,
    config.consumerSecret     || null,
    config.version            || "1.0",
    config.authorize_callback || null,
    config.signatureMethod    || "HMAC-SHA1",
    config.nonceSize          || null,
    config.customHeaders      || null
  );

  var self = this;
  self._config = config;
  self._consumer = consumer;

  Promise.promisifyAll(self);

  self.getMarketClockAsync()
      .get(0)
      .then(function(clockData) {

        /* TODO: ditch this manual timestamp crap and use moment.js */
        var serverTimestamp = parseInt(JSON.parse(clockData).response.unixtime);
        var unixtimeNow = Math.floor( (new Date()).getTime() / 1000 );
        var offset = unixtimeNow - serverTimestamp;

        self._consumer._getTimestamp = function() {
          var unixtime = Math.floor( (new Date()).getTime() / 1000 );

          debug("now: %s, serverTimestamp: %s, timeOffset: %s, curtime: %s, adjTime: %s",
                unixtimeNow, serverTimestamp, offset, unixtime, unixtime - offset);

          return unixtime - offset;
        }

        self.emit('ready');
      })
      .catch(function(error) {
        throw error;
      });
}
util.inherits(Tradeking, EE);

/* --- Order/Trade Calls ------------------------------------------------- */

// Tradeking.prototype.postAccountOrderPreview = function (accountId, fixml, cb) {
//   var uri = path.join('accounts/', accountId, '/orders/preview.json');
//   return this.post(uri, fixml, cb);
// }

/* --- Market Calls ------------------------------------------------- */

Tradeking.prototype.getMarketClock = function (cb) {
  return this.get('/market/clock.json', cb);
}

Tradeking.prototype.getMarketNewsSearch = function (searchOpts, cb) {
  // TODO: validate searchOpts against verbs permitted by TK API
  var uri = '/market/news/search.json?' + querystring.stringify(searchOpts);
  debug("getMarketNewsSearch() - uri: %s", uri);
  return this.get(uri, cb);
}

Tradeking.prototype.getMarketNewsStory = function (id, cb) {
  // TODO: validate id against the format expected by TK API
  var uri = path.join('/market/news/', id + '.json')
  debug("getMarketNewsSearch() - uri: %s", uri);
  return this.get(uri, cb);
}

Tradeking.prototype.getMarketToplists = function (toplist, cb) {
  var validToplists = ["toplosers", "toppctlosers", "topvolume",
                       "topactive", "topgainers", "toppctgainers",
                       "topactivegainersbydollarvalue"];

  if (0 > validToplists.indexOf(toplist))
    throw TypeError("'" + toplist + "' not found in: " + validToplists.toString());

  var uri = path.join('/market/toplists/', toplist + '.json');
  debug("getMarketToplists() - uri: %s", uri);
  return this.get(uri, cb);
}

/* --- Account Calls ------------------------------------------------- */

Tradeking.prototype.getAccounts = function (cb) {
  return this.get('/accounts.json', cb);
}

Tradeking.prototype.getAccountBalances = function (cb) {
  return this.get('/accounts/balances.json', cb);
}

/* --- Low-level Calls ------------------------------------------------- */

Tradeking.prototype.get = function (uri, cb) {
  return this._consumer.get(
           this._config.apiUrl + uri,
           this._config.accessToken,
           this._config.accessSecret,
           cb
         );
}

Tradeking.prototype.post = function (uri, data, contentType, cb) {
  return this._consumer.post(
           this._config.apiUrl + uri,
           this._config.accessToken,
           this._config.accessSecret,
           data, contentType, cb
         );
}
<|endoftext|>


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

/**
 * @fileoverview Controllers for the exploration statistics tab in the
 * exploration editor.
 */

oppia.constant('IMPROVE_TYPE_INCOMPLETE', 'incomplete');

oppia.controller('StatisticsTab', [
  '$scope', '$http', '$uibModal', 'AlertsService', 'ExplorationStatesService',
  'ExplorationDataService', 'ComputeGraphService', 'DateTimeFormatService',
  'StatesObjectFactory', 'StateImprovementSuggestionService',
  'ReadOnlyExplorationBackendApiService', 'UrlInterpolationService',
  'RouterService', 'StateRulesStatsService', 'IMPROVE_TYPE_INCOMPLETE',
  function(
      $scope, $http, $uibModal, AlertsService, ExplorationStatesService,
      ExplorationDataService, ComputeGraphService, DateTimeFormatService,
      StatesObjectFactory, StateImprovementSuggestionService,
      ReadOnlyExplorationBackendApiService, UrlInterpolationService,
      RouterService, StateRulesStatsService, IMPROVE_TYPE_INCOMPLETE) {
    $scope.COMPLETION_RATE_CHART_OPTIONS = {
      chartAreaWidth: 300,
      colors: ['green', 'firebrick'],
      height: 100,
      legendPosition: 'right',
      width: 500
    };
    $scope.COMPLETION_RATE_PIE_CHART_OPTIONS = {
      title: '',
      left: 230,
      pieHole: 0.6,
      pieSliceTextStyleColor: 'black',
      pieSliceBorderColor: 'black',
      chartAreaWidth: 500,
      colors: ['#008808', '#d8d8d8'],
      height: 300,
      legendPosition: 'right',
      width: 600
    };
    var _EXPLORATION_STATS_VERSION_ALL = 'all';
    $scope.currentVersion = _EXPLORATION_STATS_VERSION_ALL;

    $scope.getLocaleAbbreviatedDatetimeString = function(millisSinceEpoch) {
      return DateTimeFormatService.getLocaleAbbreviatedDatetimeString(
        millisSinceEpoch);
    };

    $scope.hasTabLoaded = false;
    $scope.$on('refreshStatisticsTab', function() {
      $scope.refreshExplorationStatistics(_EXPLORATION_STATS_VERSION_ALL);
    });

    $scope.explorationHasBeenVisited = false;
    $scope.refreshExplorationStatistics = function(version) {
      $scope.explorationStatisticsUrl = (
        '/createhandler/statistics/' + ExplorationDataService.explorationId);

      $http.get($scope.explorationStatisticsUrl).then(function(statsResponse) {
        var data = statsResponse.data;
        var numStarts = data.num_starts;
        var numActualStarts = data.num_actual_starts;
        var numCompletions = data.num_completions;
        $scope.stateStats = data.state_stats_mapping;

        ReadOnlyExplorationBackendApiService.loadLatestExploration(
          ExplorationDataService.explorationId).then(function(response) {
          var statesDict = response.exploration.states;
          var states = StatesObjectFactory.createFromBackendDict(statesDict);
          var initStateName = response.exploration.init_state_name;

          $scope.statsGraphData = ComputeGraphService.compute(
            initStateName, states);
          var improvements = (
            StateImprovementSuggestionService.getStateImprovements(
              states, $scope.stateStats));
          $scope.highlightStates = {};
          improvements.forEach(function(impItem) {
            // TODO(bhenning): This is the feedback for improvement types
            // and should be included with the definitions of the
            // improvement types.
            if (impItem.type === IMPROVE_TYPE_INCOMPLETE) {
              $scope.highlightStates[impItem.stateName] = (
                'May be confusing');
            }
          });
        });

        if (numActualStarts > 0) {
          $scope.explorationHasBeenVisited = true;
        }

        $scope.numPassersby = numStarts - numActualStarts;
        $scope.pieChartData = [
          ['Type', 'Number'],
          ['Completions', numCompletions],
          ['Non-Completions', numActualStarts - numCompletions]
        ];
      });
    };

    $scope.onClickStateInStatsGraph = function(stateName) {
      $scope.showStateStatsModal(stateName, $scope.highlightStates[stateName]);
    };

    $scope.showStateStatsModal = function(stateName, improvementType) {
      AlertsService.clearWarnings();

      StateRulesStatsService.computeStateRulesStats(
        ExplorationStatesService.getState(stateName)
      ).then(function(stateRulesStats) {
        $uibModal.open({
          templateUrl: UrlInterpolationService.getDirectiveTemplateUrl(
            '/pages/exploration_editor/statistics_tab/' +
            'state_stats_modal_directive.html'),
          backdrop: true,
          resolve: {
            stateName: function() {
              return stateName;
            },
            stateStats: function() {
              return $scope.stateStats[stateName];
            },
            improvementType: function() {
              return improvementType;
            },
            visualizationsInfo: function() {
              return stateRulesStats.visualizations_info;
            }
          },
          controller: [
            '$scope', '$uibModalInstance', '$filter', '$injector', 'stateName',
            'stateStats', 'improvementType', 'visualizationsInfo',
            'HtmlEscaperService', 'AngularNameService',
            'AnswerClassificationService',
            function(
                $scope, $uibModalInstance, $filter, $injector, stateName,
                stateStats, improvementType, visualizationsInfo,
                HtmlEscaperService, AngularNameService,
                AnswerClassificationService) {
              var COMPLETION_RATE_PIE_CHART_OPTIONS = {
                left: 20,
                pieHole: 0.6,
                pieSliceTextStyleColor: 'black',
                pieSliceBorderColor: 'black',
                chartAreaWidth: 240,
                colors: ['#d8d8d8', '#008808', 'blue'],
                height: 270,
                legendPosition: 'right',
                width: 240
              };

              var title1 = 'Answer feedback statistics';
              $scope.COMPLETION_RATE_PIE_CHART_OPTIONS1 = angular.copy(
                COMPLETION_RATE_PIE_CHART_OPTIONS);
              $scope.COMPLETION_RATE_PIE_CHART_OPTIONS1.title = title1;

              var title2 = 'Solution usage statistics';
              $scope.COMPLETION_RATE_PIE_CHART_OPTIONS2 = angular.copy(
                COMPLETION_RATE_PIE_CHART_OPTIONS);
              $scope.COMPLETION_RATE_PIE_CHART_OPTIONS2.title = title2;

              $scope.stateName = stateName;
              $scope.stateStats = stateStats;
              $scope.improvementType = improvementType;

              var usefulFeedbackCount = (
                $scope.stateStats.useful_feedback_count);
              var totalAnswersCount = (
                $scope.stateStats.total_answers_count);
              if (totalAnswersCount > 0) {
                $scope.hasExplorationBeenAnswered = true;
              }
              $scope.pieChartData1 = [
                ['Type', 'Number'],
                ['Default feedback', totalAnswersCount - usefulFeedbackCount],
                ['Specific feedback', usefulFeedbackCount],
              ];

              var numTimesSolutionViewed = (
                $scope.stateStats.num_times_solution_viewed);
              $scope.pieChartData2 = [
                ['Type', 'Number'],
                ['Solutions used to answer', numTimesSolutionViewed],
                ['Solutions not used', totalAnswersCount - (
                  numTimesSolutionViewed)]
              ];

              var _getVisualizationsHtml = function() {
                var htmlSnippets = visualizationsInfo.map(function(vizInfo) {
                  var escapedData =
                    HtmlEscaperService.objToEscapedJson(vizInfo.data);
                  var escapedOptions =
                    HtmlEscaperService.objToEscapedJson(vizInfo.options);

                  var el = $(
                    '<oppia-visualization-' +
                    $filter('camelCaseToHyphens')(vizInfo.id) + '/>');
                  el.attr('escaped-data', escapedData);
                  el.attr('escaped-options', escapedOptions);
                  el.attr(
                    'addressed-info-is-supported',
                    vizInfo.addressed_info_is_supported);
                  return el.get(0).outerHTML;
                });

                return htmlSnippets.join('');
              };

              $scope.visualizationsHtml = _getVisualizationsHtml();

              $scope.cancel = function() {
                $uibModalInstance.dismiss('cancel');
                AlertsService.clearWarnings();
              };

              $scope.navigateToStateEditor = function() {
                $scope.cancel();
                RouterService.navigateToMainTab(stateName);
              };
            }
          ]
        });
      });
    };
  }
]);
<commit_msg>Fix #5274: Open modal window only once (#5346)<commit_after>// Copyright 2014 The Oppia Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Controllers for the exploration statistics tab in the
 * exploration editor.
 */

oppia.constant('IMPROVE_TYPE_INCOMPLETE', 'incomplete');

oppia.controller('StatisticsTab', [
  '$scope', '$http', '$uibModal', 'AlertsService', 'ExplorationStatesService',
  'ExplorationDataService', 'ComputeGraphService', 'DateTimeFormatService',
  'StatesObjectFactory', 'StateImprovementSuggestionService',
  'ReadOnlyExplorationBackendApiService', 'UrlInterpolationService',
  'RouterService', 'StateRulesStatsService', 'IMPROVE_TYPE_INCOMPLETE',
  function(
      $scope, $http, $uibModal, AlertsService, ExplorationStatesService,
      ExplorationDataService, ComputeGraphService, DateTimeFormatService,
      StatesObjectFactory, StateImprovementSuggestionService,
      ReadOnlyExplorationBackendApiService, UrlInterpolationService,
      RouterService, StateRulesStatsService, IMPROVE_TYPE_INCOMPLETE) {
    $scope.COMPLETION_RATE_CHART_OPTIONS = {
      chartAreaWidth: 300,
      colors: ['green', 'firebrick'],
      height: 100,
      legendPosition: 'right',
      width: 500
    };
    $scope.COMPLETION_RATE_PIE_CHART_OPTIONS = {
      title: '',
      left: 230,
      pieHole: 0.6,
      pieSliceTextStyleColor: 'black',
      pieSliceBorderColor: 'black',
      chartAreaWidth: 500,
      colors: ['#008808', '#d8d8d8'],
      height: 300,
      legendPosition: 'right',
      width: 600
    };
    var _EXPLORATION_STATS_VERSION_ALL = 'all';
    $scope.currentVersion = _EXPLORATION_STATS_VERSION_ALL;

    $scope.getLocaleAbbreviatedDatetimeString = function(millisSinceEpoch) {
      return DateTimeFormatService.getLocaleAbbreviatedDatetimeString(
        millisSinceEpoch);
    };

    $scope.hasTabLoaded = false;
    $scope.$on('refreshStatisticsTab', function() {
      $scope.refreshExplorationStatistics(_EXPLORATION_STATS_VERSION_ALL);
    });

    $scope.explorationHasBeenVisited = false;
    $scope.refreshExplorationStatistics = function(version) {
      $scope.explorationStatisticsUrl = (
        '/createhandler/statistics/' + ExplorationDataService.explorationId);

      $http.get($scope.explorationStatisticsUrl).then(function(statsResponse) {
        var data = statsResponse.data;
        var numStarts = data.num_starts;
        var numActualStarts = data.num_actual_starts;
        var numCompletions = data.num_completions;
        $scope.stateStats = data.state_stats_mapping;

        ReadOnlyExplorationBackendApiService.loadLatestExploration(
          ExplorationDataService.explorationId).then(function(response) {
          var statesDict = response.exploration.states;
          var states = StatesObjectFactory.createFromBackendDict(statesDict);
          var initStateName = response.exploration.init_state_name;

          $scope.statsGraphData = ComputeGraphService.compute(
            initStateName, states);
          var improvements = (
            StateImprovementSuggestionService.getStateImprovements(
              states, $scope.stateStats));
          $scope.highlightStates = {};
          improvements.forEach(function(impItem) {
            // TODO(bhenning): This is the feedback for improvement types
            // and should be included with the definitions of the
            // improvement types.
            if (impItem.type === IMPROVE_TYPE_INCOMPLETE) {
              $scope.highlightStates[impItem.stateName] = (
                'May be confusing');
            }
          });
        });

        if (numActualStarts > 0) {
          $scope.explorationHasBeenVisited = true;
        }

        $scope.numPassersby = numStarts - numActualStarts;
        $scope.pieChartData = [
          ['Type', 'Number'],
          ['Completions', numCompletions],
          ['Non-Completions', numActualStarts - numCompletions]
        ];
      });
    };

    var stateStatsModalIsOpen = false;
    $scope.onClickStateInStatsGraph = function(stateName) {
      if (!stateStatsModalIsOpen) {
        stateStatsModalIsOpen = true;
        $scope.showStateStatsModal(
          stateName, $scope.highlightStates[stateName]);
      }
    };

    $scope.showStateStatsModal = function(stateName, improvementType) {
      AlertsService.clearWarnings();

      StateRulesStatsService.computeStateRulesStats(
        ExplorationStatesService.getState(stateName)
      ).then(function(stateRulesStats) {
        $uibModal.open({
          templateUrl: UrlInterpolationService.getDirectiveTemplateUrl(
            '/pages/exploration_editor/statistics_tab/' +
            'state_stats_modal_directive.html'),
          backdrop: true,
          resolve: {
            stateName: function() {
              return stateName;
            },
            stateStats: function() {
              return $scope.stateStats[stateName];
            },
            improvementType: function() {
              return improvementType;
            },
            visualizationsInfo: function() {
              return stateRulesStats.visualizations_info;
            }
          },
          controller: [
            '$scope', '$uibModalInstance', '$filter', '$injector', 'stateName',
            'stateStats', 'improvementType', 'visualizationsInfo',
            'HtmlEscaperService', 'AngularNameService',
            'AnswerClassificationService',
            function(
                $scope, $uibModalInstance, $filter, $injector, stateName,
                stateStats, improvementType, visualizationsInfo,
                HtmlEscaperService, AngularNameService,
                AnswerClassificationService) {
              var COMPLETION_RATE_PIE_CHART_OPTIONS = {
                left: 20,
                pieHole: 0.6,
                pieSliceTextStyleColor: 'black',
                pieSliceBorderColor: 'black',
                chartAreaWidth: 240,
                colors: ['#d8d8d8', '#008808', 'blue'],
                height: 270,
                legendPosition: 'right',
                width: 240
              };

              var title1 = 'Answer feedback statistics';
              $scope.COMPLETION_RATE_PIE_CHART_OPTIONS1 = angular.copy(
                COMPLETION_RATE_PIE_CHART_OPTIONS);
              $scope.COMPLETION_RATE_PIE_CHART_OPTIONS1.title = title1;

              var title2 = 'Solution usage statistics';
              $scope.COMPLETION_RATE_PIE_CHART_OPTIONS2 = angular.copy(
                COMPLETION_RATE_PIE_CHART_OPTIONS);
              $scope.COMPLETION_RATE_PIE_CHART_OPTIONS2.title = title2;

              $scope.stateName = stateName;
              $scope.stateStats = stateStats;
              $scope.improvementType = improvementType;

              var usefulFeedbackCount = (
                $scope.stateStats.useful_feedback_count);
              var totalAnswersCount = (
                $scope.stateStats.total_answers_count);
              if (totalAnswersCount > 0) {
                $scope.hasExplorationBeenAnswered = true;
              }
              $scope.pieChartData1 = [
                ['Type', 'Number'],
                ['Default feedback', totalAnswersCount - usefulFeedbackCount],
                ['Specific feedback', usefulFeedbackCount],
              ];

              var numTimesSolutionViewed = (
                $scope.stateStats.num_times_solution_viewed);
              $scope.pieChartData2 = [
                ['Type', 'Number'],
                ['Solutions used to answer', numTimesSolutionViewed],
                ['Solutions not used', totalAnswersCount - (
                  numTimesSolutionViewed)]
              ];

              var _getVisualizationsHtml = function() {
                var htmlSnippets = visualizationsInfo.map(function(vizInfo) {
                  var escapedData =
                    HtmlEscaperService.objToEscapedJson(vizInfo.data);
                  var escapedOptions =
                    HtmlEscaperService.objToEscapedJson(vizInfo.options);

                  var el = $(
                    '<oppia-visualization-' +
                    $filter('camelCaseToHyphens')(vizInfo.id) + '/>');
                  el.attr('escaped-data', escapedData);
                  el.attr('escaped-options', escapedOptions);
                  el.attr(
                    'addressed-info-is-supported',
                    vizInfo.addressed_info_is_supported);
                  return el.get(0).outerHTML;
                });

                return htmlSnippets.join('');
              };

              $scope.visualizationsHtml = _getVisualizationsHtml();

              $scope.cancel = function() {
                $uibModalInstance.dismiss('cancel');
                AlertsService.clearWarnings();
              };

              $scope.$on('$destroy', function() {
                stateStatsModalIsOpen = false;
              });

              $scope.navigateToStateEditor = function() {
                $scope.cancel();
                RouterService.navigateToMainTab(stateName);
              };
            }
          ]
        });
      });
    };
  }
]);
<|endoftext|>


<commit_before>var _ = require('lodash');
var os = require('os');
var sf = require('sf');
var tar = require('tar-fs');
var green = '\u001b[42m \u001b[0m';
var red = '\u001b[41m \u001b[0m';

function createContainer(docker, fqn, options, next) {

    var optsCreate = {
        'name': options.service.name,
        'Image': fqn,
        'Hostname': '',
        'User': '',
        'AttachStdin': false,
        'AttachStdout': false,
        'AttachStderr': false,
        'Tty': false,
        'OpenStdin': false,
        'StdinOnce': false,
        'Env': null,
        'Volumes': null
    };

    if (options.service.docker && options.service.docker.Config) {
        // For example options look in Config in: docker inspect <container_name>
        optsCreate = _.extend(optsCreate, options.service.docker.Config);
    }

    // Process any variables
    processCmdVars(optsCreate, options.name, options.cwd);

    var doCreate = function(err) {
        if (err && err.statusCode !== 404) return next(err);
        docker.createContainer(optsCreate, next);
    };
    var container = docker.getContainer(optsCreate.name);
    if (container) return container.remove(doCreate);
    doCreate();
}

function processCmdVars(optsCreate, name, cwd) {

    // Allow simple variable substitution in Cmds
    var processedCommands = [], processedBinds = [],
        data = {
            HOST_IP: getHostIp(),
            PATH: cwd
        };

    if(optsCreate.Cmd) {
        optsCreate.Cmd.forEach(function(cmd) {
            processedCommands.push(sf(cmd, data));
        });
        optsCreate.Cmd = processedCommands;
    }

    if(optsCreate.Binds) {
        optsCreate.Binds.forEach(function(bind) {
            processedBinds.push(sf(bind, data));
        });
        optsCreate.Binds = processedBinds;
    }

}

function startContainer(bosco, docker, fqn, options, container, next) {
    // We need to get the SSH port?
    var optsStart = {
        'NetworkMode': 'bridge',
        'VolumesFrom': null
    };

    if (options.service.docker && options.service.docker.HostConfig) {
        // For example options look in HostConfig in: docker inspect <container_name>
        optsStart = _.extend(optsStart, options.service.docker.HostConfig);
    }

    // Process any variables
    processCmdVars(optsStart, options.name, options.cwd);

    bosco.log('Starting ' + options.name.green + ': ' + fqn.magenta + '...');

    container.start(optsStart, function(err) {

        if (err) {
            bosco.error('Failed to start Docker image: ' + err.message);
            return next(err);
        }
        var checkPort;
        _.forOwn(options.service.docker.HostConfig.PortBindings, function(value) {
            if(!checkPort && value[0].HostPort) checkPort = value[0].HostPort; // Check first port
        });

        if (!checkPort) {
            bosco.warn('Could not detect if ' + options.name.green + ' had started, no port specified');
            return next();
        }

        var checkTimeout = options.service.checkTimeout || 10000;
        var checkEnd = Date.now() + checkTimeout;

        function check() {
            checkRunning(checkPort, function(err, running) {
                if (!err && running) {
                    process.stdout.write('\n');
                    return next();
                }

                if (Date.now() > checkEnd) {
                    process.stdout.write('\n');
                    bosco.warn('Could not detect if ' + options.name.green + ' had started on port ' + ('' + checkPort).magenta + ' after ' + checkTimeout + 'ms');
                    return next();
                }

                process.stdout.write('.');
                setTimeout(check, 500);
            });
        }
        bosco.log('Waiting for ' + options.name.green + ' to respond on port ' + ('' + checkPort).magenta);
        check();

    });
}

/**
 * Check to see if the process is running by making a connection and
 * seeing if it is immediately closed or stays open long enough for us to close it.
 */
function checkRunning(port, next) {
    var net = require('net');
    var socket = net.createConnection(port);
    var start = new Date();
    var timer;
    socket.on('connect', function() {
        timer = setTimeout(function() { socket.end() }, 200);
    });
    socket.on('close', function(hadError) {
        if (hadError) return; // If we are closing due to an error ignore it

        clearTimeout(timer);
        var closed = new Date() - start;
        next(null, closed > 100 ? true : false);
    });
    socket.on('error', function() {
        next(new Error('Failed to connect'), false);
    });
}

function prepareImage(bosco, docker, fqn, options, next) {
    if (options.service.docker && options.service.docker.build) {
        return buildImage(bosco, docker, fqn, options, next);
    }

    if (options.service.alwaysPull) {
        return pullImage(bosco, docker, fqn, next);
    }

    locateImage(docker, fqn, function(err, image) {
        if (err || image) return next(err, image);

        // Image not available
        pullImage(bosco, docker, fqn, next);
    });
}

function buildImage(bosco, docker, fqn, options, next) {
    var path = sf(options.service.docker.build, {PATH: options.cwd});
    // TODO(geophree): obey .dockerignore
    var tarStream = tar.pack(path);
    tarStream.once('error', next);

    bosco.log('Building image for ' + options.service.name + ' ...');
    var lastStream = '';
    docker.buildImage(tarStream, {t: fqn}, function(err, stream) {
        if (err) next(err);

        stream.on('data', function(data) {
            var json = JSON.parse(data);
            if (json.error) {
                return bosco.error(json.error);
            } else if (json.progress) {
                return;
            } else if (json.stream) {
                lastStream = json.stream;
                bosco.log(lastStream.trim());
            }
        });
        stream.once('end', function() {
            var id = lastStream.match(/Successfully built ([a-f0-9]+)/);
            if (id && id[1]) {
                return next(null, docker.getImage(id[1]));
            }
            next(new Error('Id not found in final log line: ' . lastStream));
        });
        stream.once('error', next);
    });
}

function locateImage(docker, repoTag, callback) {
    docker.listImages(function(err, list) {
        if (err) return callback(err);

        for (var i = 0, len = list.length; i < len; i++) {
            if (list[i].RepoTags.indexOf(repoTag) !== -1) {
                return callback(null, docker.getImage(list[i].Id));
            }
        }

        return callback();
    });
}

function pullImage(bosco, docker, repoTag, next) {
    var prettyError;

    function handler() {
        locateImage(docker, repoTag, function(err, image) {
            if (err || prettyError) return next(prettyError || err);
            next(null, image);
        });
    }

    bosco.log('Pulling image ' + repoTag.green + ' ...');

    docker.pull(repoTag, function(err, stream) {
        var currentLayer;
        var progress;
        var previousTotal;

        if (err || prettyError) return next(prettyError || err);

        function newBar(id, total) {
            if (bosco.config.get('progress') == 'bar') {
                return new bosco.progress('Downloading ' + id + ' [:bar] :percent :etas', {
                    complete: green,
                    incomplete: red,
                    width: 50,
                    total: total
                });
            } else {
                var logged = false;
                return {
                    tick: function() {
                        if (!logged) {
                            bosco.log('Downloading layer ' + id + '...');
                            logged = true;
                        }
                    }
                }
            }
        }

        stream.on('data', function(data) {
            var json = JSON.parse(data);

            if (json.errorDetail) {
                prettyError = json.error;
            } else if (json.status === 'Downloading') {
                if (json.id !== currentLayer) {
                    progress = newBar(json.id, json.progressDetail.total);
                    currentLayer = json.id;
                    previousTotal = 0;
                }
                progress.tick(json.progressDetail.current - previousTotal);
                previousTotal = json.progressDetail.current;
            }
        })
        stream.once('end', handler);
    });

}

function getHostIp() {

    var ip = _.chain(os.networkInterfaces())
              .values()
              .flatten()
              .filter(function(val) {
                return (val.family === 'IPv4' && val.internal === false)
              })
              .pluck('address')
              .first()
              .value();

    return ip;

}

module.exports = {
    buildImage: buildImage,
    createContainer: createContainer,
    locateImage: locateImage,
    prepareImage: prepareImage,
    pullImage: pullImage,
    startContainer: startContainer
}
<commit_msg>Docker run: use optsStart to find ports, it won't be undefined.<commit_after>var _ = require('lodash');
var os = require('os');
var sf = require('sf');
var tar = require('tar-fs');
var green = '\u001b[42m \u001b[0m';
var red = '\u001b[41m \u001b[0m';

function createContainer(docker, fqn, options, next) {

    var optsCreate = {
        'name': options.service.name,
        'Image': fqn,
        'Hostname': '',
        'User': '',
        'AttachStdin': false,
        'AttachStdout': false,
        'AttachStderr': false,
        'Tty': false,
        'OpenStdin': false,
        'StdinOnce': false,
        'Env': null,
        'Volumes': null
    };

    if (options.service.docker && options.service.docker.Config) {
        // For example options look in Config in: docker inspect <container_name>
        optsCreate = _.extend(optsCreate, options.service.docker.Config);
    }

    // Process any variables
    processCmdVars(optsCreate, options.name, options.cwd);

    var doCreate = function(err) {
        if (err && err.statusCode !== 404) return next(err);
        docker.createContainer(optsCreate, next);
    };
    var container = docker.getContainer(optsCreate.name);
    if (container) return container.remove(doCreate);
    doCreate();
}

function processCmdVars(optsCreate, name, cwd) {

    // Allow simple variable substitution in Cmds
    var processedCommands = [], processedBinds = [],
        data = {
            HOST_IP: getHostIp(),
            PATH: cwd
        };

    if(optsCreate.Cmd) {
        optsCreate.Cmd.forEach(function(cmd) {
            processedCommands.push(sf(cmd, data));
        });
        optsCreate.Cmd = processedCommands;
    }

    if(optsCreate.Binds) {
        optsCreate.Binds.forEach(function(bind) {
            processedBinds.push(sf(bind, data));
        });
        optsCreate.Binds = processedBinds;
    }

}

function startContainer(bosco, docker, fqn, options, container, next) {
    // We need to get the SSH port?
    var optsStart = {
        'NetworkMode': 'bridge',
        'VolumesFrom': null
    };

    if (options.service.docker && options.service.docker.HostConfig) {
        // For example options look in HostConfig in: docker inspect <container_name>
        optsStart = _.extend(optsStart, options.service.docker.HostConfig);
    }

    // Process any variables
    processCmdVars(optsStart, options.name, options.cwd);

    bosco.log('Starting ' + options.name.green + ': ' + fqn.magenta + '...');

    container.start(optsStart, function(err) {

        if (err) {
            bosco.error('Failed to start Docker image: ' + err.message);
            return next(err);
        }

        var checkPort;
        _.forOwn(optsStart.PortBindings, function(value) {
            if(!checkPort && value[0].HostPort) checkPort = value[0].HostPort; // Check first port
        });

        if (!checkPort) {
            bosco.warn('Could not detect if ' + options.name.green + ' had started, no port specified');
            return next();
        }

        var checkTimeout = options.service.checkTimeout || 10000;
        var checkEnd = Date.now() + checkTimeout;

        function check() {
            checkRunning(checkPort, function(err, running) {
                if (!err && running) {
                    process.stdout.write('\n');
                    return next();
                }

                if (Date.now() > checkEnd) {
                    process.stdout.write('\n');
                    bosco.warn('Could not detect if ' + options.name.green + ' had started on port ' + ('' + checkPort).magenta + ' after ' + checkTimeout + 'ms');
                    return next();
                }

                process.stdout.write('.');
                setTimeout(check, 500);
            });
        }
        bosco.log('Waiting for ' + options.name.green + ' to respond on port ' + ('' + checkPort).magenta);
        check();

    });
}

/**
 * Check to see if the process is running by making a connection and
 * seeing if it is immediately closed or stays open long enough for us to close it.
 */
function checkRunning(port, next) {
    var net = require('net');
    var socket = net.createConnection(port);
    var start = new Date();
    var timer;
    socket.on('connect', function() {
        timer = setTimeout(function() { socket.end() }, 200);
    });
    socket.on('close', function(hadError) {
        if (hadError) return; // If we are closing due to an error ignore it

        clearTimeout(timer);
        var closed = new Date() - start;
        next(null, closed > 100 ? true : false);
    });
    socket.on('error', function() {
        next(new Error('Failed to connect'), false);
    });
}

function prepareImage(bosco, docker, fqn, options, next) {
    if (options.service.docker && options.service.docker.build) {
        return buildImage(bosco, docker, fqn, options, next);
    }

    if (options.service.alwaysPull) {
        return pullImage(bosco, docker, fqn, next);
    }

    locateImage(docker, fqn, function(err, image) {
        if (err || image) return next(err, image);

        // Image not available
        pullImage(bosco, docker, fqn, next);
    });
}

function buildImage(bosco, docker, fqn, options, next) {
    var path = sf(options.service.docker.build, {PATH: options.cwd});
    // TODO(geophree): obey .dockerignore
    var tarStream = tar.pack(path);
    tarStream.once('error', next);

    bosco.log('Building image for ' + options.service.name + ' ...');
    var lastStream = '';
    docker.buildImage(tarStream, {t: fqn}, function(err, stream) {
        if (err) next(err);

        stream.on('data', function(data) {
            var json = JSON.parse(data);
            if (json.error) {
                return bosco.error(json.error);
            } else if (json.progress) {
                return;
            } else if (json.stream) {
                lastStream = json.stream;
                bosco.log(lastStream.trim());
            }
        });
        stream.once('end', function() {
            var id = lastStream.match(/Successfully built ([a-f0-9]+)/);
            if (id && id[1]) {
                return next(null, docker.getImage(id[1]));
            }
            next(new Error('Id not found in final log line: ' . lastStream));
        });
        stream.once('error', next);
    });
}

function locateImage(docker, repoTag, callback) {
    docker.listImages(function(err, list) {
        if (err) return callback(err);

        for (var i = 0, len = list.length; i < len; i++) {
            if (list[i].RepoTags.indexOf(repoTag) !== -1) {
                return callback(null, docker.getImage(list[i].Id));
            }
        }

        return callback();
    });
}

function pullImage(bosco, docker, repoTag, next) {
    var prettyError;

    function handler() {
        locateImage(docker, repoTag, function(err, image) {
            if (err || prettyError) return next(prettyError || err);
            next(null, image);
        });
    }

    bosco.log('Pulling image ' + repoTag.green + ' ...');

    docker.pull(repoTag, function(err, stream) {
        var currentLayer;
        var progress;
        var previousTotal;

        if (err || prettyError) return next(prettyError || err);

        function newBar(id, total) {
            if (bosco.config.get('progress') == 'bar') {
                return new bosco.progress('Downloading ' + id + ' [:bar] :percent :etas', {
                    complete: green,
                    incomplete: red,
                    width: 50,
                    total: total
                });
            } else {
                var logged = false;
                return {
                    tick: function() {
                        if (!logged) {
                            bosco.log('Downloading layer ' + id + '...');
                            logged = true;
                        }
                    }
                }
            }
        }

        stream.on('data', function(data) {
            var json = JSON.parse(data);

            if (json.errorDetail) {
                prettyError = json.error;
            } else if (json.status === 'Downloading') {
                if (json.id !== currentLayer) {
                    progress = newBar(json.id, json.progressDetail.total);
                    currentLayer = json.id;
                    previousTotal = 0;
                }
                progress.tick(json.progressDetail.current - previousTotal);
                previousTotal = json.progressDetail.current;
            }
        })
        stream.once('end', handler);
    });

}

function getHostIp() {

    var ip = _.chain(os.networkInterfaces())
              .values()
              .flatten()
              .filter(function(val) {
                return (val.family === 'IPv4' && val.internal === false)
              })
              .pluck('address')
              .first()
              .value();

    return ip;

}

module.exports = {
    buildImage: buildImage,
    createContainer: createContainer,
    locateImage: locateImage,
    prepareImage: prepareImage,
    pullImage: pullImage,
    startContainer: startContainer
}
<|endoftext|>


<commit_before>'use strict';
window.initCSApp = function() {
  var Ticker = require('cs-ticker-api').BitcoinAverage
  var emitter = require('cs-emitter')
  var walletExists = require('cs-wallet-js').walletExists
  var fastclick = require('fastclick')
  var initFrame = require('cs-frame')
  var initAuth = require('cs-auth')
  var initGeoOverlay = require('cs-geo-overlay')
  var $ = require('browserify-zepto')
  var getNetwork = require('cs-network')
  var fadeIn = require('cs-transitions/fade.js').fadeIn
  var sync = require('cs-wallet-js').sync

  var CS = require('cs-wallet-js')
  var getNetwork = require('cs-network')
  var yaqrcode = require('yaqrcode')

  var appEl = document.getElementById('app')
  var frame = initFrame(appEl)
  var auth = null
  var _html = $('html')
  var _app = $(appEl)
  fastclick(document.body)

  initGeoOverlay(document.getElementById('geo-overlay'))

  if (window.buildPlatform === 'ios') {
    applewatch.init(onSuccessInitAppleWatch, onErrorInitAppleWatch, 'group.com.coinspace.wallet.dev')

    applewatch.addListener('requestCommandQueue', function(message) {

      console.log('receive command: ' + message)

      if (message === 'updateBalance') {
        console.log('receive request balanceUdpate');
        sync(function(err, txs) {
          if(err) return showError(err)
          emitter.emit('update-balance')
        })
      } else if (message === 'showQrCode') {
        console.log('receive request qr code')
        var address = CS.getWallet().getNextAddress()
        var qr = yaqrcode(getNetwork() + ':' + address)

        var response = {}
        response.command = 'qrMessage'
        response.qr = qr
        response.address = address

        applewatch.sendMessage(response, 'comandAnswerQueue')
      } else if (message == 'turnMectoOff') {
        console.log('turn off mecto')
        emitter.emit('turn-off-mecto-watch')
      } else if (message === 'turnMectoOn') {
        console.log('turn on mnecto')
        emitter.emit('turn-on-mecto-watch')
      } else if (message === 'getMectoStatus') {
        console.log('on getMectoStatus')
        emitter.emit('getMectoStatus')
      }
    });
  }

  walletExists(function(exists){
    auth = exists ? initAuth.pin(null, { userExists: true }) : initAuth.choose()
    var authContentEl = document.getElementById('auth_content')
    authContentEl.style.opacity = 0;
    fadeIn(authContentEl)
    auth.show()
  })

  emitter.on('open-overlay', function(){
    _app.addClass('is_hidden')
    _html.addClass('prevent_scroll')
  })

  emitter.on('close-overlay', function(){
    _app.removeClass('is_hidden')
    _html.removeClass('prevent_scroll')
  })

  emitter.on('balance-ready', function(){
    auth.hide()
    frame.show()
  })

  function updateExchangeRates(){
    var tickerUpdateInterval = 1000 * 60 * 2
    var ticker = new Ticker(getNetwork())

    ticker.getExchangeRates(function(err, rates){
      if (rates) {
        if (window.buildPlatform === 'ios') {
          var respone = {}
          respone.command = 'currencyMessage'
          respone.currency = rates;
          applewatch.sendMessage(respone, 'comandAnswerQueue');
        }
        emitter.emit('ticker', rates);
      }
      window.setTimeout(updateExchangeRates, tickerUpdateInterval)
    })
  }

  function onSuccessInitAppleWatch(appGroupId) {
    console.log('success init with ' + appGroupId);
  }

  function onErrorInitAppleWatch() {
    console.log('failed init apple watch module');
  }

  function onSuccessUserDefaults() {
    console.log('success on user defaults')
  }

  function onErrorUserDefaults() {
    console.log('error on user defaults')
  }

  updateExchangeRates()
}
<commit_msg>change app group<commit_after>'use strict';
window.initCSApp = function() {
  var Ticker = require('cs-ticker-api').BitcoinAverage
  var emitter = require('cs-emitter')
  var walletExists = require('cs-wallet-js').walletExists
  var fastclick = require('fastclick')
  var initFrame = require('cs-frame')
  var initAuth = require('cs-auth')
  var initGeoOverlay = require('cs-geo-overlay')
  var $ = require('browserify-zepto')
  var getNetwork = require('cs-network')
  var fadeIn = require('cs-transitions/fade.js').fadeIn
  var sync = require('cs-wallet-js').sync

  var CS = require('cs-wallet-js')
  var getNetwork = require('cs-network')
  var yaqrcode = require('yaqrcode')

  var appEl = document.getElementById('app')
  var frame = initFrame(appEl)
  var auth = null
  var _html = $('html')
  var _app = $(appEl)
  fastclick(document.body)

  initGeoOverlay(document.getElementById('geo-overlay'))

  if (window.buildPlatform === 'ios') {
    applewatch.init(onSuccessInitAppleWatch, onErrorInitAppleWatch, 'group.com.coinspace.wallet')

    applewatch.addListener('requestCommandQueue', function(message) {

      console.log('receive command: ' + message)

      if (message === 'updateBalance') {
        console.log('receive request balanceUdpate');
        sync(function(err, txs) {
          if(err) return showError(err)
          emitter.emit('update-balance')
        })
      } else if (message === 'showQrCode') {
        console.log('receive request qr code')
        var address = CS.getWallet().getNextAddress()
        var qr = yaqrcode(getNetwork() + ':' + address)

        var response = {}
        response.command = 'qrMessage'
        response.qr = qr
        response.address = address

        applewatch.sendMessage(response, 'comandAnswerQueue')
      } else if (message == 'turnMectoOff') {
        console.log('turn off mecto')
        emitter.emit('turn-off-mecto-watch')
      } else if (message === 'turnMectoOn') {
        console.log('turn on mnecto')
        emitter.emit('turn-on-mecto-watch')
      } else if (message === 'getMectoStatus') {
        console.log('on getMectoStatus')
        emitter.emit('getMectoStatus')
      }
    });
  }

  walletExists(function(exists){
    auth = exists ? initAuth.pin(null, { userExists: true }) : initAuth.choose()
    var authContentEl = document.getElementById('auth_content')
    authContentEl.style.opacity = 0;
    fadeIn(authContentEl)
    auth.show()
  })

  emitter.on('open-overlay', function(){
    _app.addClass('is_hidden')
    _html.addClass('prevent_scroll')
  })

  emitter.on('close-overlay', function(){
    _app.removeClass('is_hidden')
    _html.removeClass('prevent_scroll')
  })

  emitter.on('balance-ready', function(){
    auth.hide()
    frame.show()
  })

  function updateExchangeRates(){
    var tickerUpdateInterval = 1000 * 60 * 2
    var ticker = new Ticker(getNetwork())

    ticker.getExchangeRates(function(err, rates){
      if (rates) {
        if (window.buildPlatform === 'ios') {
          var respone = {}
          respone.command = 'currencyMessage'
          respone.currency = rates;
          applewatch.sendMessage(respone, 'comandAnswerQueue');
        }
        emitter.emit('ticker', rates);
      }
      window.setTimeout(updateExchangeRates, tickerUpdateInterval)
    })
  }

  function onSuccessInitAppleWatch(appGroupId) {
    console.log('success init with ' + appGroupId);
  }

  function onErrorInitAppleWatch() {
    console.log('failed init apple watch module');
  }

  function onSuccessUserDefaults() {
    console.log('success on user defaults')
  }

  function onErrorUserDefaults() {
    console.log('error on user defaults')
  }

  updateExchangeRates()
}
<|endoftext|>