<commit_before>var YAML = require('yamljs');
var q = require('q');
var fs = require('fs');

var DEFAULT_REGEXP = "(^|[^\\w_\\$])transl\\([\\\"\\\']([^\\\"\\\']+)[\\\"\\\']\\)";

var translate = function(content, dictionary) {
  if (!content) {
    return new Error('No content to transform (translate).');
  }

  var res = content.trim().split('.').reduce(function(dict, key) {
    if (!dict) {
      return null;
    }
    return dict[key];
  }, dictionary);

  if (!res) {
    return new Error('No such content (' + content + ') in locale file');
  } else {
    return '\"' + res + '\"';
  }
}

var getDictionary = function(path){
  var dictionary;

  if (path.match(/\.json$/)) {
    dictionary = JSON.parse(
      fs.readFileSync(path,{
        encoding: 'utf8'
      })
    );
  }
  else if (path.match(/\.yml$/)){
    dictionary = YAML.load(path);
  }
  if (!dictionary){
    try{
      dictionary = JSON.parse(
        fs.readFileSync(path+'.json',{
          encoding: 'utf8'
        })
      );
    }
    catch(e){
      dictionary = YAML.load(path + '.yml');
    }
  }
  return dictionary;
};

module.exports = (function() {
  var Translator = function(options){
    options = options || {};

    var regexp = DEFAULT_REGEXP;
    if (options.syntaxFunctionName) {
      this.syntaxFunctionName = options.syntaxFunctionName || 'transl';
      regexp = regexp.replace('transl', this.syntaxFunctionName);
    }

    this.syntaxPattern = new RegExp(regexp, 'g');

    if (typeof options.translate === 'function') {
        this.translate = option.translate;
    } else {
        return throw Error('options.translate not a function');
    }

    if (typeof options === 'string') {
      // A filepath to dictionary
      var lang = options.match(/([\.^\/]*)\.\w{0,4}$/);
      this.localeLang = lang && lang[0] || 'undefined';
      this.localeFilePath = options;
      this.localeDictionary = getDictionary(this.localeFilePath);
    } else if (typeof options === 'object' || options.localeDictionary) {
      // A dictionary
      this.localeDictionary = options.localeDictionary;
    } else {
      this.localeLang = options.localeLang;
      this.localeFilePath = options.localeFilePath;
      this.localeDirectory = options.localeDirectory;
      this.localeFileExt = options.localeFileExt;

      if (!this.localeFilePath){
        this.localeFilePath = this.localeDirectory + this.localeLang;
      }

      if (this.localeFileExt) {
        this.localeFilePath += this.localeFileExt;
      }
        this.localeDictionary = getDictionary(this.localeFilePath);
    }

    return this;
  };

  Translator.prototype.translate = function(content) {
    var resultPromise = q.defer();
    var self = this;

    resultPromise.resolve(content.replace(self.syntaxPattern, function (s, prefix, data) {
      var res = data;

      if (typeof self.translate === 'function') {
        res = self.translate(res, self.localeDictionary);
      } else {
        res = translate(res, self.localeDictionary);
      }

      if (res instanceof Error) {
        return resultPromise.reject(res);
      }

      return prefix + res;
    }, content));

    return resultPromise.promise;
  };

  return Translator;
}());
<commit_msg>Fix singular object option is not always dictionary<commit_after>var YAML = require('yamljs');
var q = require('q');
var fs = require('fs');

var DEFAULT_REGEXP = "(^|[^\\w_\\$])transl\\([\\\"\\\']([^\\\"\\\']+)[\\\"\\\']\\)";

var translate = function(content, dictionary) {
  if (!content) {
    return new Error('No content to transform (translate).');
  }

  var res = content.trim().split('.').reduce(function(dict, key) {
    if (!dict) {
      return null;
    }
    return dict[key];
  }, dictionary);

  if (!res) {
    return new Error('No such content (' + content + ') in locale file');
  } else {
    return '\"' + res + '\"';
  }
}

var getDictionary = function(path){
  var dictionary;

  if (path.match(/\.json$/)) {
    dictionary = JSON.parse(
      fs.readFileSync(path,{
        encoding: 'utf8'
      })
    );
  }
  else if (path.match(/\.yml$/)){
    dictionary = YAML.load(path);
  }
  if (!dictionary){
    try{
      dictionary = JSON.parse(
        fs.readFileSync(path+'.json',{
          encoding: 'utf8'
        })
      );
    }
    catch(e){
      dictionary = YAML.load(path + '.yml');
    }
  }
  return dictionary;
};

module.exports = (function() {
  var Translator = function(options){
    options = options || {};

    var regexp = DEFAULT_REGEXP;
    if (options.syntaxFunctionName) {
      this.syntaxFunctionName = options.syntaxFunctionName || 'transl';
      regexp = regexp.replace('transl', this.syntaxFunctionName);
    }

    this.syntaxPattern = new RegExp(regexp, 'g');

    if (typeof options.translate === 'function') {
        this.translate = option.translate;
    } else {
        return throw Error('options.translate not a function');
    }

    if (typeof options === 'string') {
      // A filepath to dictionary
      var lang = options.match(/([\.^\/]*)\.\w{0,4}$/);
      this.localeLang = lang && lang[0] || 'undefined';
      this.localeFilePath = options;
      this.localeDictionary = getDictionary(this.localeFilePath);
    } else if (options.localeDictionary) {
      // A dictionary
      this.localeDictionary = options.localeDictionary;
    } else {
      this.localeLang = options.localeLang;
      this.localeFilePath = options.localeFilePath;
      this.localeDirectory = options.localeDirectory;
      this.localeFileExt = options.localeFileExt;

      if (!this.localeFilePath){
        this.localeFilePath = this.localeDirectory + this.localeLang;
      }

      if (this.localeFileExt) {
        this.localeFilePath += this.localeFileExt;
      }
        this.localeDictionary = getDictionary(this.localeFilePath);
    }

    return this;
  };

  Translator.prototype.translate = function(content) {
    var resultPromise = q.defer();
    var self = this;

    resultPromise.resolve(content.replace(self.syntaxPattern, function (s, prefix, data) {
      var res = data;

      if (typeof self.translate === 'function') {
        res = self.translate(res, self.localeDictionary);
      } else {
        res = translate(res, self.localeDictionary);
      }

      if (res instanceof Error) {
        return resultPromise.reject(res);
      }

      return prefix + res;
    }, content));

    return resultPromise.promise;
  };

  return Translator;
}());
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from "react";
import {ActivityIndicator, View, Text, TouchableNativeFeedback, Platform} from "react-native";
import Ripple from './polyfill/Ripple';
import { TYPO, PRIMARY, THEME_NAME, PRIMARY_COLORS } from './config';
import { getColor, isCompatible } from './helpers';

export default class Button extends Component {

    static propTypes = {
        text: PropTypes.string.isRequired,
        theme: PropTypes.oneOf(THEME_NAME),
        primary: PropTypes.oneOf(PRIMARY_COLORS),
        overrides: PropTypes.shape({
            textColor: PropTypes.string,
            backgroundColor: PropTypes.string,
            rippleColor: PropTypes.string
        }),
        disabled: PropTypes.bool,
        loading: PropTypes.bool,
        activityIndicatorColor: PropTypes.string,
        raised: PropTypes.bool,
        styles: PropTypes.shape({
            button:   View.propTypes.style,
            disabled: View.propTypes.style,
            text:     Text.propTypes.style
        }),
        onPress: PropTypes.func,
        onLongPress: PropTypes.func,
    };

    static defaultProps = {
        theme: 'light',
        primary: PRIMARY,
        disabled: false,
        loading: false,
        raised: false,
        activityIndicatorColor: 'black',
        styles: {
            disabled: {opacity: 0.5}
        }
    };

    constructor(props) {
        super(props);
        this.state = {
            elevation: 2
        };
    }

    shouldComponentUpdate(nextProps, nextState) {
        return (
            nextProps.text     !== this.props.text ||
            nextProps.value    !== this.props.value ||
            nextProps.loading  !== this.props.loading ||
            nextProps.disabled !== this.props.disabled
        );
    }

    setElevation = () => {
        this.setState({
            elevation: 4
        });
    };

    removeElevation = () => {
        this.setState({
            elevation: 2
        });
    };

    render() {
        const { elevation } = this.state;
        const { text, value, theme, primary, overrides, disabled, loading, raised, styles, onPress, onLongPress } = this.props;

        const textStyleMap = {
            flat: {
                light: {
                    normal: {
                        color: getColor(primary)
                    },
                    disabled: {
                        color: 'rgba(0,0,0,.26)'
                    }
                },
                dark: {
                    normal: {
                        color: getColor(primary)
                    },
                    disabled: {
                        color: 'rgba(255,255,255,.3)'
                    }
                }
            },
            raised: {
                light: {
                    normal: {
                        color: getColor(primary)
                    },
                    disabled: {
                        color: 'rgba(0,0,0,.26)'
                    }
                },
                dark: {
                    normal: {
                        color: '#fff'
                    },
                    disabled: {
                        color: 'rgba(255,255,255,.3)'
                    }
                }
            }
        };

        const buttonStyleMap = {
            raised: {
                light: {
                    normal: {
                        backgroundColor: '#fff',
                        borderColor: 'rgba(0,0,0,.12)',
                    },
                    disabled: {
                        backgroundColor: 'rgba(0,0,0,.12)',
                        borderWidth: 1,
                        borderColor: 'rgba(0,0,0,.12)'
                    }
                },
                dark: {
                    normal: {
                        backgroundColor: getColor(primary),
                        borderWidth: 1,
                        borderColor: 'rgba(0,0,0,.12)'
                    },
                    disabled: {
                        backgroundColor: 'rgba(255,255,255,.12)',
                        borderWidth: 1,
                        borderColor: 'rgba(0,0,0,.12)'
                    }
                }
            }
        };

        const rippleColorMap = {
            flat: {
                light: {
                    normal: 'rgba(153,153,153,.4)',
                    disabled: 'rgba(0,0,0,0.06)'
                },
                dark: {
                    normal: 'rgba(204,204,204,.25)',
                    disabled: 'rgba(255,255,255,0.06)'
                }
            },
            raised: {
                light: {
                    normal: 'rgba(153,153,153,.4)',
                    disabled: 'rgba(0,0,0,.06)'
                },
                dark: {
                    normal: getColor(`${primary}700`),
                    disabled: 'rgba(255,255,255,.06)'
                }
            }
        };

        const type = disabled ? 'disabled' : 'normal';
        const shape = raised ? 'raised' : 'flat';

        const textStyle = (() => {
            if (disabled || !(overrides && overrides.textColor)) {
                return textStyleMap[shape][theme][type];
            }

            return { color: getColor(overrides.textColor) };
        })();

        const buttonStyle = (() => {
            if (raised) {
                if (disabled || !(overrides && overrides.backgroundColor)) {
                    return buttonStyleMap[shape][theme][type];
                }

                return Object.assign(buttonStyleMap[shape][theme][type], { backgroundColor: getColor(overrides.backgroundColor) });
            }

            return null;
        })();

        const rippleColor = (() => {
            if (disabled || !(overrides && overrides.rippleColor)) {
                return rippleColorMap[shape][theme][type];
            }

            return getColor(overrides.rippleColor)
        })();

        if (disabled || loading) {
            return (
                <View
                    style={[
                        componentStyles.button,
                        buttonStyle, {
                            backgroundColor: buttonStyle && buttonStyle.backgroundColor
                        },
                        styles.button,
                        styles.disabled
                    ]}
                >
                    {loading ?
                        <ActivityIndicator animating={this.props.loading} style={componentStyles.spinner} color={this.props.activityIndicatorColor} />
                        :
                        <Text style={[TYPO.paperFontButton, textStyle, componentStyles.text, styles.text]}>
                            {text || value}
                            {this.props.children}
                        </Text>
                    }
                </View>
            );
        }

        if (!isCompatible('TouchableNativeFeedback')) {
            return (
                <Ripple
                    elevation={raised ? [2, 4] : null}
                    rippleColor={rippleColor}
                    onPress={!disabled ? onPress : null}
                    onLongPress={!disabled ? onLongPress : null}
                    style={[
                        componentStyles.button,
                        buttonStyle, {
                            backgroundColor: buttonStyle && buttonStyle.backgroundColor,
                        }, raised && !isCompatible('elevation') && Platform.OS !== 'ios' && {
                            borderWidth: 1,
                            borderColor: 'rgba(0,0,0,.12)'
                        },
                        styles.button
                    ]}
                >
                    <Text style={[TYPO.paperFontButton, textStyle, componentStyles.text, styles.text]}>
                        {text || value}
                        {this.props.children}
                    </Text>
                </Ripple>
            )
        }

        return (
            <TouchableNativeFeedback
                background={TouchableNativeFeedback.Ripple(rippleColor)}
                onPress={!disabled ? onPress : null}
                onLongPress={!disabled ? onLongPress : null}
                onPressIn={raised ? this.setElevation : null}
                onPressOut={raised ? this.removeElevation : null}
            >
                <View style={[
                    componentStyles.button,
                    buttonStyle, {
                        backgroundColor: buttonStyle && buttonStyle.backgroundColor,
                        elevation: raised ? elevation : 0
                    },
                    styles.button
                  ]}
                >
                    <Text style={[TYPO.paperFontButton, textStyle, componentStyles.text, styles.text]}>
                        {text || value}
                    </Text>
                </View>
            </TouchableNativeFeedback>
        );
    };
}

const componentStyles = {
    button: {
        alignItems: 'center',
        justifyContent: 'center',
        paddingVertical: 6,
        paddingHorizontal: 16,
        margin: 6,
        borderRadius: 2
    },
    text: {
        position: 'relative',
        top: Platform.OS === 'android' ? 2 : -4
    },
    spinner: {
        alignSelf: 'center'
    }
};
<commit_msg>Update button text to center align, RN 0.35 seems to have modified this<commit_after>import React, {Component, PropTypes} from "react";
import {ActivityIndicator, View, Text, TouchableNativeFeedback, Platform} from "react-native";
import Ripple from './polyfill/Ripple';
import { TYPO, PRIMARY, THEME_NAME, PRIMARY_COLORS } from './config';
import { getColor, isCompatible } from './helpers';

export default class Button extends Component {

    static propTypes = {
        text: PropTypes.string.isRequired,
        theme: PropTypes.oneOf(THEME_NAME),
        primary: PropTypes.oneOf(PRIMARY_COLORS),
        overrides: PropTypes.shape({
            textColor: PropTypes.string,
            backgroundColor: PropTypes.string,
            rippleColor: PropTypes.string
        }),
        disabled: PropTypes.bool,
        loading: PropTypes.bool,
        activityIndicatorColor: PropTypes.string,
        raised: PropTypes.bool,
        styles: PropTypes.shape({
            button:   View.propTypes.style,
            disabled: View.propTypes.style,
            text:     Text.propTypes.style
        }),
        onPress: PropTypes.func,
        onLongPress: PropTypes.func,
    };

    static defaultProps = {
        theme: 'light',
        primary: PRIMARY,
        disabled: false,
        loading: false,
        raised: false,
        activityIndicatorColor: 'black',
        styles: {
            disabled: {opacity: 0.5}
        }
    };

    constructor(props) {
        super(props);
        this.state = {
            elevation: 2
        };
    }

    shouldComponentUpdate(nextProps, nextState) {
        return (
            nextProps.text     !== this.props.text ||
            nextProps.value    !== this.props.value ||
            nextProps.loading  !== this.props.loading ||
            nextProps.disabled !== this.props.disabled
        );
    }

    setElevation = () => {
        this.setState({
            elevation: 4
        });
    };

    removeElevation = () => {
        this.setState({
            elevation: 2
        });
    };

    render() {
        const { elevation } = this.state;
        const { text, value, theme, primary, overrides, disabled, loading, raised, styles, onPress, onLongPress } = this.props;

        const textStyleMap = {
            flat: {
                light: {
                    normal: {
                        color: getColor(primary)
                    },
                    disabled: {
                        color: 'rgba(0,0,0,.26)'
                    }
                },
                dark: {
                    normal: {
                        color: getColor(primary)
                    },
                    disabled: {
                        color: 'rgba(255,255,255,.3)'
                    }
                }
            },
            raised: {
                light: {
                    normal: {
                        color: getColor(primary)
                    },
                    disabled: {
                        color: 'rgba(0,0,0,.26)'
                    }
                },
                dark: {
                    normal: {
                        color: '#fff'
                    },
                    disabled: {
                        color: 'rgba(255,255,255,.3)'
                    }
                }
            }
        };

        const buttonStyleMap = {
            raised: {
                light: {
                    normal: {
                        backgroundColor: '#fff',
                        borderColor: 'rgba(0,0,0,.12)',
                    },
                    disabled: {
                        backgroundColor: 'rgba(0,0,0,.12)',
                        borderWidth: 1,
                        borderColor: 'rgba(0,0,0,.12)'
                    }
                },
                dark: {
                    normal: {
                        backgroundColor: getColor(primary),
                        borderWidth: 1,
                        borderColor: 'rgba(0,0,0,.12)'
                    },
                    disabled: {
                        backgroundColor: 'rgba(255,255,255,.12)',
                        borderWidth: 1,
                        borderColor: 'rgba(0,0,0,.12)'
                    }
                }
            }
        };

        const rippleColorMap = {
            flat: {
                light: {
                    normal: 'rgba(153,153,153,.4)',
                    disabled: 'rgba(0,0,0,0.06)'
                },
                dark: {
                    normal: 'rgba(204,204,204,.25)',
                    disabled: 'rgba(255,255,255,0.06)'
                }
            },
            raised: {
                light: {
                    normal: 'rgba(153,153,153,.4)',
                    disabled: 'rgba(0,0,0,.06)'
                },
                dark: {
                    normal: getColor(`${primary}700`),
                    disabled: 'rgba(255,255,255,.06)'
                }
            }
        };

        const type = disabled ? 'disabled' : 'normal';
        const shape = raised ? 'raised' : 'flat';

        const textStyle = (() => {
            if (disabled || !(overrides && overrides.textColor)) {
                return textStyleMap[shape][theme][type];
            }

            return { color: getColor(overrides.textColor) };
        })();

        const buttonStyle = (() => {
            if (raised) {
                if (disabled || !(overrides && overrides.backgroundColor)) {
                    return buttonStyleMap[shape][theme][type];
                }

                return Object.assign(buttonStyleMap[shape][theme][type], { backgroundColor: getColor(overrides.backgroundColor) });
            }

            return null;
        })();

        const rippleColor = (() => {
            if (disabled || !(overrides && overrides.rippleColor)) {
                return rippleColorMap[shape][theme][type];
            }

            return getColor(overrides.rippleColor)
        })();

        if (disabled || loading) {
            return (
                <View
                    style={[
                        componentStyles.button,
                        buttonStyle, {
                            backgroundColor: buttonStyle && buttonStyle.backgroundColor
                        },
                        styles.button,
                        styles.disabled
                    ]}
                >
                    {loading ?
                        <ActivityIndicator animating={this.props.loading} style={componentStyles.spinner} color={this.props.activityIndicatorColor} />
                        :
                        <Text style={[TYPO.paperFontButton, textStyle, componentStyles.text, styles.text]}>
                            {text || value}
                            {this.props.children}
                        </Text>
                    }
                </View>
            );
        }

        if (!isCompatible('TouchableNativeFeedback')) {
            return (
                <Ripple
                    elevation={raised ? [2, 4] : null}
                    rippleColor={rippleColor}
                    onPress={!disabled ? onPress : null}
                    onLongPress={!disabled ? onLongPress : null}
                    style={[
                        componentStyles.button,
                        buttonStyle, {
                            backgroundColor: buttonStyle && buttonStyle.backgroundColor,
                        }, raised && !isCompatible('elevation') && Platform.OS !== 'ios' && {
                            borderWidth: 1,
                            borderColor: 'rgba(0,0,0,.12)'
                        },
                        styles.button
                    ]}
                >
                    <Text style={[TYPO.paperFontButton, textStyle, componentStyles.text, styles.text]}>
                        {text || value}
                        {this.props.children}
                    </Text>
                </Ripple>
            )
        }

        return (
            <TouchableNativeFeedback
                background={TouchableNativeFeedback.Ripple(rippleColor)}
                onPress={!disabled ? onPress : null}
                onLongPress={!disabled ? onLongPress : null}
                onPressIn={raised ? this.setElevation : null}
                onPressOut={raised ? this.removeElevation : null}
            >
                <View style={[
                    componentStyles.button,
                    buttonStyle, {
                        backgroundColor: buttonStyle && buttonStyle.backgroundColor,
                        elevation: raised ? elevation : 0
                    },
                    styles.button
                  ]}
                >
                    <Text style={[TYPO.paperFontButton, textStyle, componentStyles.text, styles.text]}>
                        {text || value}
                    </Text>
                </View>
            </TouchableNativeFeedback>
        );
    };
}

const componentStyles = {
    button: {
        alignItems: 'center',
        justifyContent: 'center',
        paddingVertical: 6,
        paddingHorizontal: 16,
        margin: 6,
        borderRadius: 2
    },
    text: {
        lineHeight: 20
    },
    spinner: {
        alignSelf: 'center'
    }
};
<|endoftext|>


<commit_before>'use strict';

function storage (collection, storage, pushover) {

  function create (obj, fn) {

    //NOTE: the eventTime is sent by the client, but deleted, we only store created_at right now
    var created_at = new Date();
    var eventTime;

    if (obj.eventTime) {
      eventTime = new Date(obj.eventTime);
      created_at = eventTime;
    }

    obj.created_at = created_at.toISOString();

    var preBolusCarbs = '';
    if (obj.preBolus > 0 && obj.carbs) {
      preBolusCarbs = obj.carbs;
      delete obj.carbs;
    }

    // clean data
    delete obj.eventTime;
    if (!obj.carbs) delete obj.carbs;
    if (!obj.insulin) delete obj.insulin;
    if (!obj.notes) delete obj.notes;
    if (!obj.preBolus || obj.preBolus == 0) delete obj.preBolus;
    if (!obj.glucose) {
      delete obj.glucose;
      delete obj.glucoseType;
    }

    api( ).insert(obj, function (err, doc) {
      fn(null, doc);

      if (obj.preBolus > 0) {
        //create a new object to insert copying only the needed fields
        var pbTreat = {
          created_at: (new Date(created_at.getTime() + (obj.preBolus * 60000))).toISOString(),
          eventType: obj.eventType,
          carbs: preBolusCarbs
        };

        if (obj.notes) pbTreat.notes = obj.notes;

        api( ).insert(pbTreat, function() {
            //nothing to do here
        });
      }

      if (pushover) {

        //since we don't know the time zone on the device viewing the push messeage
        //we can only show the amount of adjustment
        var timeAdjustment = calcTimeAdjustment(eventTime);

        var text = (obj.glucose ? 'BG: ' + obj.glucose + ' (' + obj.glucoseType + ')' : '') +
          (obj.carbs ? '\nCarbs: ' + obj.carbs : '') +
          (preBolusCarbs ? '\nCarbs: ' + preBolusCarbs + ' (in ' + obj.preBolus + ' minutes)' : '')+
          (obj.insulin ? '\nInsulin: ' + obj.insulin : '')+
          (obj.enteredBy ? '\nEntered By: ' + obj.enteredBy : '') +
          (timeAdjustment ? '\nEvent Time: ' + timeAdjustment : '') +
          (obj.notes ? '\nNotes: ' + obj.notes : '');

        var msg = {
          expire: 14400, // 4 hours
          message: text,
          title: obj.eventType,
          sound: 'gamelan',
          timestamp: new Date( ),
          priority: (obj.eventType == 'Note' ? -1 : 0),
          retry: 30
        };

        pushover.send( msg, function( err, result ) {
          console.log(result);
        });
      }

    });
  }

  function calcTimeAdjustment(eventTime) {

    if (!eventTime) return null;

    var now = (new Date()).getTime(),
      other = eventTime.getTime(),
      past = other < now,
      offset = Math.abs(now - other);

    var MINUTE = 60 * 1000,
      HOUR = 3600 * 1000;

    var parts = {};

    if (offset <= MINUTE)
      return 'now';
    else if (offset < (HOUR * 2))
      parts = { value: Math.round(Math.abs(offset / MINUTE)), label: 'mins' };
    else
      parts = { value: Math.round(Math.abs(offset / HOUR)), label: 'hrs' };

    if (past)
      return parts.value + ' ' + parts.label + ' ago';
    else
      return 'in ' + parts.value + ' ' + parts.label;
  }

  function list (opts, fn) {
    function find ( ) {
      var q = opts && opts.find ? opts.find : { };
      return q;
    }

    return api( ).find(find()).sort({created_at: -1}).toArray(fn);
  }

  function api ( ) {
    return storage.pool.db.collection(collection);
  }

  api.list = list;
  api.create = create;
  return api;
}

function ensureIndexes(name, storage) {
  storage.with_collection(name)(function (err, collection) {
    if (err) {
      console.error("ensureIndexes, unable to get collection for: " + name + " - " + err);
    } else {
      storage.ensureIndexes(collection, ['created_at', 'eventType']);
    }
  });
}

module.exports = {
  storage: storage,
  ensureIndexes: ensureIndexes
};
<commit_msg>if there isn't a glucose value, don't store the units<commit_after>'use strict';

function storage (collection, storage, pushover) {

  function create (obj, fn) {

    //NOTE: the eventTime is sent by the client, but deleted, we only store created_at right now
    var created_at = new Date();
    var eventTime;

    if (obj.eventTime) {
      eventTime = new Date(obj.eventTime);
      created_at = eventTime;
    }

    obj.created_at = created_at.toISOString();

    var preBolusCarbs = '';
    if (obj.preBolus > 0 && obj.carbs) {
      preBolusCarbs = obj.carbs;
      delete obj.carbs;
    }

    // clean data
    delete obj.eventTime;
    if (!obj.carbs) delete obj.carbs;
    if (!obj.insulin) delete obj.insulin;
    if (!obj.notes) delete obj.notes;
    if (!obj.preBolus || obj.preBolus == 0) delete obj.preBolus;
    if (!obj.glucose) {
      delete obj.glucose;
      delete obj.glucoseType;
      delete obj.units;
    }

    api( ).insert(obj, function (err, doc) {
      fn(null, doc);

      if (obj.preBolus > 0) {
        //create a new object to insert copying only the needed fields
        var pbTreat = {
          created_at: (new Date(created_at.getTime() + (obj.preBolus * 60000))).toISOString(),
          eventType: obj.eventType,
          carbs: preBolusCarbs
        };

        if (obj.notes) pbTreat.notes = obj.notes;

        api( ).insert(pbTreat, function() {
            //nothing to do here
        });
      }

      if (pushover) {

        //since we don't know the time zone on the device viewing the push messeage
        //we can only show the amount of adjustment
        var timeAdjustment = calcTimeAdjustment(eventTime);

        var text = (obj.glucose ? 'BG: ' + obj.glucose + ' (' + obj.glucoseType + ')' : '') +
          (obj.carbs ? '\nCarbs: ' + obj.carbs : '') +
          (preBolusCarbs ? '\nCarbs: ' + preBolusCarbs + ' (in ' + obj.preBolus + ' minutes)' : '')+
          (obj.insulin ? '\nInsulin: ' + obj.insulin : '')+
          (obj.enteredBy ? '\nEntered By: ' + obj.enteredBy : '') +
          (timeAdjustment ? '\nEvent Time: ' + timeAdjustment : '') +
          (obj.notes ? '\nNotes: ' + obj.notes : '');

        var msg = {
          expire: 14400, // 4 hours
          message: text,
          title: obj.eventType,
          sound: 'gamelan',
          timestamp: new Date( ),
          priority: (obj.eventType == 'Note' ? -1 : 0),
          retry: 30
        };

        pushover.send( msg, function( err, result ) {
          console.log(result);
        });
      }

    });
  }

  function calcTimeAdjustment(eventTime) {

    if (!eventTime) return null;

    var now = (new Date()).getTime(),
      other = eventTime.getTime(),
      past = other < now,
      offset = Math.abs(now - other);

    var MINUTE = 60 * 1000,
      HOUR = 3600 * 1000;

    var parts = {};

    if (offset <= MINUTE)
      return 'now';
    else if (offset < (HOUR * 2))
      parts = { value: Math.round(Math.abs(offset / MINUTE)), label: 'mins' };
    else
      parts = { value: Math.round(Math.abs(offset / HOUR)), label: 'hrs' };

    if (past)
      return parts.value + ' ' + parts.label + ' ago';
    else
      return 'in ' + parts.value + ' ' + parts.label;
  }

  function list (opts, fn) {
    function find ( ) {
      var q = opts && opts.find ? opts.find : { };
      return q;
    }

    return api( ).find(find()).sort({created_at: -1}).toArray(fn);
  }

  function api ( ) {
    return storage.pool.db.collection(collection);
  }

  api.list = list;
  api.create = create;
  return api;
}

function ensureIndexes(name, storage) {
  storage.with_collection(name)(function (err, collection) {
    if (err) {
      console.error("ensureIndexes, unable to get collection for: " + name + " - " + err);
    } else {
      storage.ensureIndexes(collection, ['created_at', 'eventType']);
    }
  });
}

module.exports = {
  storage: storage,
  ensureIndexes: ensureIndexes
};
<|endoftext|>


<commit_before>;
(function () {
  if (typeof HyperbolicCanvas === "undefined") {
    window.HyperbolicCanvas = {};
  }

  var Point = window.HyperbolicCanvas.Point;
  var Line = window.HyperbolicCanvas.Line;
  var Circle = window.HyperbolicCanvas.Circle;
  var Polygon = window.HyperbolicCanvas.Polygon;

  var Canvas = window.HyperbolicCanvas.Canvas = function (el) {
    this.el = el;
    this.appendChildren();
    this.resize();

    this.ctx = this.canvas.getContext('2d');

    this.filledPolygons = [];
    // if (this.hasClass('hyperbolic-canvas-autoresize')) {
    //   this.sensor = new ResizeSensor(this.el, this.resize.bind(this));
    // }

    HyperbolicCanvas.canvases.push(this);
  };

  Canvas.prototype.appendChildren = function () {
    this.viewport = document.createElement('div');
    this.canvas = document.createElement('canvas');

    this.viewport.className = 'viewport';
    this.viewport.style.height = this.viewport.style.width = '100%';
    this.canvas.style.display = 'block';
    this.canvas.width = this.canvas.height = 0;

    this.el.appendChild(this.viewport);
    this.viewport.appendChild(this.canvas);
  };

  // Canvas.prototype.hasClass = function (className) {
  //   if (this.el.classList) {
  //     return this.el.classList.contains(className);
  //   } else {
  //     return new RegExp('(^| )' + className + '( |$)', 'gi').test(this.el.className);
  //   }
  // };

  Canvas.prototype.resize = function () {
    this.viewport.style["max-width"] = this.viewport.style["max-height"] = null;

    var w = this.viewport.clientWidth;
    var h = this.viewport.clientHeight;
    this.diameter = w > h ? h : w;
    this.radius =  this.diameter / 2;

    this.canvas.width = this.canvas.height = this.diameter;
    this.viewport.style["max-width"] = this.viewport.style["max-height"] = "" + (this.diameter) + "px";
    this.canvas.style["border-radius"] = "" + Math.floor(this.radius) + "px";
  };

  Canvas.prototype.setFillStyle = function (style) {
    this.ctx.fillStyle = style;
  };

  Canvas.prototype.setStrokeStyle = function (style) {
    this.ctx.strokeStyle = style;
  };

  Canvas.prototype.at = function (loc) {
    if (loc.__proto__ === Point.prototype) {
      // scale up
      var x = (loc.x + 1) * this.radius;
      var y = (loc.y + 1) * this.radius;
      return [x, this.diameter - y];
    } else if (loc.__proto__ === Array.prototype) {
      // scale down
      return new Point({ x: loc[0] / this.radius - 1, y: (this.diameter - loc[1]) / this.radius - 1 });
    }
  };

  Canvas.prototype.polarAt = function () {

  };

  Canvas.prototype.strokeLineThroughIdealPoints = function (t0, t1) {

  };

  Canvas.prototype.clear = function () {
    this.ctx.clearRect(0, 0, this.diameter, this.diameter);
  };

  Canvas.prototype.strokeGrid = function (n) {
    this.ctx.beginPath();

    for (var i = 1; i < n; i++) {
      // x axis
      this.ctx.moveTo(this.diameter * i / n, 0);
      this.ctx.lineTo(this.diameter * i / n, this.diameter);
      // y axis
      this.ctx.moveTo(0, this.diameter * i / n);
      this.ctx.lineTo(this.diameter, this.diameter * i / n);
    }
    this.ctx.stroke();
    this.ctx.closePath();
  };

  Canvas.prototype.drawCircle = function (c, options) {
    var center = this.at(c.center);
    this.ctx.beginPath();
    this.ctx.arc(center[0], center[1], c.radius * this.radius, 0, Math.TAU);
    this.ctx.closePath();
    if (options.fill) {
      this.ctx.fill();
    }
    if (options.stroke) {
      this.ctx.stroke();
    }
  };

  Canvas.prototype.fillAndStrokeCircle = function (c) {
    this.drawCircle(c, { fill: true, stroke: true });
  };

  Canvas.prototype.fillCircle = function (c) {
    this.drawCircle(c, { fill: true });
  };

  Canvas.prototype.strokeCircle = function (c) {
    this.drawCircle(c, { stroke: true });
  };

  Canvas.prototype.strokeLine = function (l, infinite) {
    // TODO deal with points not in unit circle ?
    // TODO breaks on line from q4 to center
    var c = l.arcCircle();

    if (!c) {
      var p0 = this.at(l.p0);
      var p1 = this.at(l.p1);
      this.ctx.beginPath();
      this.ctx.moveTo(p0[0], p0[1]);
      this.ctx.lineTo(p1[0], p1[1]);
      this.ctx.stroke();
      this.ctx.closePath();
      return true;
    }
    var centerScaled = this.at(c.center);
    var a0, a1;

    if (infinite) {
      a0 = 0;
      a1 = Math.TAU;
    } else {
      a0 = -1 * c.angleAt(l.p0);
      a1 = -1 * c.angleAt(l.p1);
    }
    this.ctx.beginPath();
    this.ctx.arc(centerScaled[0], centerScaled[1], c.radius * this.radius, a0 < a1 ? a0 : a1, a0 < a1 ? a1 : a0, Math.abs(a0 - a1) > Math.PI);
    this.ctx.stroke();
    this.ctx.closePath();

    return true;
  };

  Canvas.prototype.drawPath = function (l) {
    var c = l.arcCircle();

    var p0 = this.at(l.p0);
    var p1 = this.at(l.p1);

    // TODO why is this lineTo necessary?
    this.ctx.lineTo(p0[0], p0[1]);

    if (c) {
      var centerScaled = this.at(c.center);
      var a0 = -1 * c.angleAt(l.p0);
      var a1 = -1 * c.angleAt(l.p1);

      var control = this.at(Line.intersect(c.tangentAtPoint(l.p0), c.tangentAtPoint(l.p1)));
      if (control) {
        this.ctx.arcTo(control[0], control[1], p1[0], p1[1], c.radius * this.radius);
      } else {
        // TODO need this?
        this.ctx.lineTo(p1[0], p1[1]);
      }
    } else {
      this.ctx.lineTo(p1[0], p1[1]);
    }
  };

  Canvas.prototype.drawPolygon = function (p, options) {
    var vertices = p.vertices;
    var n = vertices.length;
    this.ctx.beginPath();
    var start = this.at(vertices[0])
    this.ctx.moveTo(start[0], start[1]);
    for (var i = 0; i < n; i++) {
      this.drawPath(Line.fromTwoPoints(vertices[i], vertices[(i + 1) % n]));
    }
    this.ctx.closePath();
    if (options.fill) {
      this.ctx.fill();
      this.filledPolygons.push(p);
    }
    if (options.stroke) {
      this.ctx.stroke();
    }
    return true;
  };

  Canvas.prototype.fillAndStrokePolygon = function (p) {
    this.drawPolygon(p, { fill: true, stroke: true });
  };

  Canvas.prototype.fillPolygon = function (p) {
    this.drawPolygon(p, { fill: true });
  };

  Canvas.prototype.strokePolygon = function (p) {
    this.drawPolygon(p, { stroke: true });
  };

  Canvas.prototype.strokePolygonBoundaries = function (p) {
    var vertices = p.vertices;
    var n = vertices.length;
    if (n < 3){
      return false;
    }
    for (var i = 0; i < n; i++) {
      this.strokeLine(Line.fromTwoPoints(vertices[i], vertices[(i + 1) % n]), true);
    }
    return true;
  };

  Canvas.prototype.tesselate = function (order, n, center, radius, rotation, inverse) {
    // TODO tesselate the entire plane with n-gons
  };
})();
<commit_msg>fix offscreen bug, (the browser was giving an error when the polygon went off the screen)<commit_after>;
(function () {
  if (typeof HyperbolicCanvas === "undefined") {
    window.HyperbolicCanvas = {};
  }

  var Point = window.HyperbolicCanvas.Point;
  var Line = window.HyperbolicCanvas.Line;
  var Circle = window.HyperbolicCanvas.Circle;
  var Polygon = window.HyperbolicCanvas.Polygon;

  var Canvas = window.HyperbolicCanvas.Canvas = function (el) {
    this.el = el;
    this.appendChildren();
    this.resize();

    this.ctx = this.canvas.getContext('2d');

    this.filledPolygons = [];
    // if (this.hasClass('hyperbolic-canvas-autoresize')) {
    //   this.sensor = new ResizeSensor(this.el, this.resize.bind(this));
    // }

    HyperbolicCanvas.canvases.push(this);
  };

  Canvas.prototype.appendChildren = function () {
    this.viewport = document.createElement('div');
    this.canvas = document.createElement('canvas');

    this.viewport.className = 'viewport';
    this.viewport.style.height = this.viewport.style.width = '100%';
    this.canvas.style.display = 'block';
    this.canvas.width = this.canvas.height = 0;

    this.el.appendChild(this.viewport);
    this.viewport.appendChild(this.canvas);
  };

  // Canvas.prototype.hasClass = function (className) {
  //   if (this.el.classList) {
  //     return this.el.classList.contains(className);
  //   } else {
  //     return new RegExp('(^| )' + className + '( |$)', 'gi').test(this.el.className);
  //   }
  // };

  Canvas.prototype.resize = function () {
    this.viewport.style["max-width"] = this.viewport.style["max-height"] = null;

    var w = this.viewport.clientWidth;
    var h = this.viewport.clientHeight;
    this.diameter = w > h ? h : w;
    this.radius =  this.diameter / 2;

    this.canvas.width = this.canvas.height = this.diameter;
    this.viewport.style["max-width"] = this.viewport.style["max-height"] = "" + (this.diameter) + "px";
    this.canvas.style["border-radius"] = "" + Math.floor(this.radius) + "px";
  };

  Canvas.prototype.setFillStyle = function (style) {
    this.ctx.fillStyle = style;
  };

  Canvas.prototype.setStrokeStyle = function (style) {
    this.ctx.strokeStyle = style;
  };

  Canvas.prototype.at = function (loc) {
    if (loc.__proto__ === Point.prototype) {
      // scale up
      var x = (loc.x + 1) * this.radius;
      var y = (loc.y + 1) * this.radius;
      return [x, this.diameter - y];
    } else if (loc.__proto__ === Array.prototype) {
      // scale down
      return new Point({ x: loc[0] / this.radius - 1, y: (this.diameter - loc[1]) / this.radius - 1 });
    }
  };

  Canvas.prototype.polarAt = function () {

  };

  Canvas.prototype.strokeLineThroughIdealPoints = function (t0, t1) {

  };

  Canvas.prototype.clear = function () {
    this.ctx.clearRect(0, 0, this.diameter, this.diameter);
  };

  Canvas.prototype.strokeGrid = function (n) {
    this.ctx.beginPath();

    for (var i = 1; i < n; i++) {
      // x axis
      this.ctx.moveTo(this.diameter * i / n, 0);
      this.ctx.lineTo(this.diameter * i / n, this.diameter);
      // y axis
      this.ctx.moveTo(0, this.diameter * i / n);
      this.ctx.lineTo(this.diameter, this.diameter * i / n);
    }
    this.ctx.stroke();
    this.ctx.closePath();
  };

  Canvas.prototype.drawCircle = function (c, options) {
    var center = this.at(c.center);
    this.ctx.beginPath();
    this.ctx.arc(center[0], center[1], c.radius * this.radius, 0, Math.TAU);
    this.ctx.closePath();
    if (options.fill) {
      this.ctx.fill();
    }
    if (options.stroke) {
      this.ctx.stroke();
    }
  };

  Canvas.prototype.fillAndStrokeCircle = function (c) {
    this.drawCircle(c, { fill: true, stroke: true });
  };

  Canvas.prototype.fillCircle = function (c) {
    this.drawCircle(c, { fill: true });
  };

  Canvas.prototype.strokeCircle = function (c) {
    this.drawCircle(c, { stroke: true });
  };

  Canvas.prototype.strokeLine = function (l, infinite) {
    // TODO deal with points not in unit circle ?
    // TODO breaks on line from q4 to center
    var c = l.arcCircle();

    if (!c) {
      var p0 = this.at(l.p0);
      var p1 = this.at(l.p1);
      this.ctx.beginPath();
      this.ctx.moveTo(p0[0], p0[1]);
      this.ctx.lineTo(p1[0], p1[1]);
      this.ctx.stroke();
      this.ctx.closePath();
      return true;
    }
    var centerScaled = this.at(c.center);
    var a0, a1;

    if (infinite) {
      a0 = 0;
      a1 = Math.TAU;
    } else {
      a0 = -1 * c.angleAt(l.p0);
      a1 = -1 * c.angleAt(l.p1);
    }
    this.ctx.beginPath();
    this.ctx.arc(centerScaled[0], centerScaled[1], c.radius * this.radius, a0 < a1 ? a0 : a1, a0 < a1 ? a1 : a0, Math.abs(a0 - a1) > Math.PI);
    this.ctx.stroke();
    this.ctx.closePath();

    return true;
  };

  Canvas.prototype.drawPath = function (l) {
    var c = l.arcCircle();

    var p0 = this.at(l.p0);
    var p1 = this.at(l.p1);

    // TODO why is this lineTo necessary?
    this.ctx.lineTo(p0[0], p0[1]);

    if (c) {
      var centerScaled = this.at(c.center);
      var a0 = -1 * c.angleAt(l.p0);
      var a1 = -1 * c.angleAt(l.p1);

      var control = this.at(Line.intersect(c.tangentAtPoint(l.p0), c.tangentAtPoint(l.p1)));
      if (control) {
        this.ctx.arcTo(control[0], control[1], p1[0], p1[1], c.radius * this.radius);
      } else {
        // TODO need this?
        this.ctx.lineTo(p1[0], p1[1]);
      }
    } else {
      this.ctx.lineTo(p1[0], p1[1]);
    }
  };

  Canvas.prototype.drawPolygon = function (p, options) {
    var vertices = p.vertices;
    if(vertices==undefined)return false;//fix, so the browser doesn't give an error when the polygon off the screen
    var n = vertices.length;
    this.ctx.beginPath();
    var start = this.at(vertices[0])
    this.ctx.moveTo(start[0], start[1]);
    for (var i = 0; i < n; i++) {
      this.drawPath(Line.fromTwoPoints(vertices[i], vertices[(i + 1) % n]));
    }
    this.ctx.closePath();
    if (options.fill) {
      this.ctx.fill();
      this.filledPolygons.push(p);
    }
    if (options.stroke) {
      this.ctx.stroke();
    }
    return true;
  };

  Canvas.prototype.fillAndStrokePolygon = function (p) {
    this.drawPolygon(p, { fill: true, stroke: true });
  };

  Canvas.prototype.fillPolygon = function (p) {
    this.drawPolygon(p, { fill: true });
  };

  Canvas.prototype.strokePolygon = function (p) {
    this.drawPolygon(p, { stroke: true });
  };

  Canvas.prototype.strokePolygonBoundaries = function (p) {
    var vertices = p.vertices;
    var n = vertices.length;
    if (n < 3){
      return false;
    }
    for (var i = 0; i < n; i++) {
      this.strokeLine(Line.fromTwoPoints(vertices[i], vertices[(i + 1) % n]), true);
    }
    return true;
  };

  Canvas.prototype.tesselate = function (order, n, center, radius, rotation, inverse) {
    // TODO tesselate the entire plane with n-gons
  };
})();
<|endoftext|>


<commit_before>/**
 * Base functionality for relationships.
 * @module relationships
 */

var InternalSiestaError = require('./error').InternalSiestaError,
    Store = require('./store'),
    Operation = require('./operation/operation').Operation,
    util = require('./util'),
    defineSubProperty = util.defineSubProperty,
    _ = util._,
    Query = require('./query'),
    log = require('./operation/log'),
    notifications = require('./notifications'),
    wrapArrayForAttributes = notifications.wrapArray,
    ArrayObserver = require('../vendor/observe-js/src/observe').ArrayObserver,
    coreChanges = require('./changes'),
    ChangeType = coreChanges.ChangeType;

/**
 * @class  [Fault description]
 * @param {RelationshipProxy} proxy
 * @constructor
 */
function Fault(proxy) {
    var self = this;
    this.proxy = proxy;
    Object.defineProperty(this, 'isFault', {
        get: function () {
            return self.proxy.isFault;
        },
        enumerable: true,
        configurable: true
    });
}

_.extend(Fault.prototype, {
    get: function () {
        this.proxy.get.apply(this.proxy, arguments);
    },
    set: function () {
        this.proxy.set.apply(this.proxy, arguments);
    }
});

/**
 * @class  [RelationshipProxy description]
 * @param {Object} opts
 * @constructor
 */
function RelationshipProxy(opts) {
    this._opts = opts;
    if (!this) return new RelationshipProxy(opts);
    var self = this;
    this.fault = new Fault(this);
    this.object = null;
    this._id = undefined;
    this.related = null;
    Object.defineProperty(this, 'isFault', {
        get: function () {
            if (self._id) {
                return !self.related;
            } else if (self._id === null) {
                return false;
            }
            return true;
        },
        set: function (v) {
            if (v) {
                self._id = undefined;
                self.related = null;
            } else {
                if (!self._id) {
                    self._id = null;
                }
            }
        },
        enumerable: true,
        configurable: true
    });
    defineSubProperty.call(this, 'reverseModel', this._opts);
    defineSubProperty.call(this, 'forwardModel', this._opts);
    defineSubProperty.call(this, 'forwardName', this._opts);
    defineSubProperty.call(this, 'reverseName', this._opts);
    defineSubProperty.call(this, 'isReverse', this._opts);
    Object.defineProperty(this, 'isForward', {
        get: function () {
            return !self.isReverse;
        },
        set: function (v) {
            self.isReverse = !v;
        },
        enumerable: true,
        configurable: true
    });
    if (this._opts.isReverse === undefined && this._opts.isForward !== undefined) {
        this.isReverse = !this._opts.isForward;
    }
    else if (this._opts.isReverse === undefined && this._opts.isForward === undefined) {
        throw new InternalSiestaError('Must specify either isReverse or isForward when configuring relationship proxy.');
    }
}

_.extend(RelationshipProxy.prototype, {
    _dump: function (asJson) {
        var dumped = {};
    },
    install: function (obj) {
        if (obj) {
            if (!this.object) {
                this.object = obj;
                var self = this;
                var name = getForwardName.call(this);
                Object.defineProperty(obj, name, {
                    get: function () {
                        if (self.isFault) {
                            return self.fault;
                        } else {
                            return self.related;
                        }
                    },
                    set: function (v) {
                        self.set(v);
                    },
                    configurable: true,
                    enumerable: true
                });
                if (!obj.__proxies) obj.__proxies = {};
                obj.__proxies[name] = this;
                if (!obj._proxies) {
                    obj._proxies = [];
                }
                obj._proxies.push(this);
            } else {
                throw new InternalSiestaError('Already installed.');
            }
        } else {
            throw new InternalSiestaError('No object passed to relationship install');
        }
    },
    set: function () {
        throw new InternalSiestaError('Must subclass RelationshipProxy');
    },
    get: function () {
        throw new InternalSiestaError('Must subclass RelationshipProxy');
    }
});

// TODO: Share code between getReverseProxyForObject and getForwardProxyForObject

function getReverseProxyForObject(obj) {
    var reverseName = getReverseName.call(this);
    var reverseModel = this.reverseModel;
    // This should never happen. Should g   et caught in the mapping operation?
    if (util.isArray(obj)) {
        return _.map(obj, function (o) {
            return o.__proxies[reverseName];
        })
    } else {
        var proxy = obj.__proxies[reverseName];
        if (!proxy) {
            var err = 'No proxy with name "' + reverseName + '" on mapping ' + reverseModel.name;
            throw new InternalSiestaError(err);
        }
        return proxy;
    }
}

function getForwardProxyForObject(obj) {
    var forwardName = getForwardName.call(this);
    var forwardModel = this.forwardModel;
    if (util.isArray(obj)) {
        return _.map(obj, function (o) {
            return o.__proxies[forwardName];
        })
    } else {
        var proxy = obj.__proxies[forwardName];
        if (!proxy) {
            var err = 'No proxy with name "' + forwardName + '" on mapping ' + forwardModel.name;
            throw new InternalSiestaError(err);
        }
        return proxy;
    }
}

function getReverseName() {
    return this.isForward ? this.reverseName : this.forwardName;
}

function getForwardName() {
    return this.isForward ? this.forwardName : this.reverseName;
}

function getReverseModel() {
    return this.isForward ? this.reverseModel : this.forwardModel;
}

function getForwardModel() {
    return this.isForward ? this.forwardModel : this.reverseModel;
}

function checkInstalled() {
    if (!this.object) {
        throw new InternalSiestaError('Proxy must be installed on an object before can use it.');
    }
}

/**
 * Configure _id and related with the new related object.
 * @param obj
 * @param {object} [opts]
 * @param {boolean} [opts.disableNotifications]
 * @returns {String|undefined} - Error message or undefined
 */
function set(obj, opts) {
    opts = opts || {};
    if (!opts.disableNotifications) {
        registerSetChange.call(this, obj);
    }
    if (obj) {
        if (util.isArray(obj)) {
            this._id = _.pluck(obj, '_id');
            this.related = obj;
        } else {
            this._id = obj._id;
            this.related = obj;
        }
    } else {
        this._id = null;
        this.related = null;
    }
}

function spliceFactory(opts) {
    opts = opts || {};
    return function (idx, numRemove) {
        opts = opts || {};
        if (!opts.disableNotifications) {
            registerSpliceChange.apply(this, arguments);
        }
        var add = Array.prototype.slice.call(arguments, 2);
        var returnValue = _.partial(this._id.splice, idx, numRemove).apply(this._id, _.pluck(add, '_id'));
        if (this.related) {
            _.partial(this.related.splice, idx, numRemove).apply(this.related, add);
        }
        return returnValue;
    }
}

var splice = spliceFactory({});

//function splice(idx, numRemove) {
//    registerSpliceChange.apply(this, arguments);
//    var add = Array.prototype.slice.call(arguments, 2);
//    var returnValue = _.partial(this._id.splice, idx, numRemove).apply(this._id, _.pluck(add, '_id'));
//    if (this.related) {
//        _.partial(this.related.splice, idx, numRemove).apply(this.related, add);
//    }
//    return returnValue;
//}

function objAsString(obj) {
    function _objAsString(obj) {
        if (obj) {
            var model = obj.model;
            var modelName = model.name;
            var ident = obj._id;
            if (typeof ident == 'string') {
                ident = '"' + ident + '"';
            }
            return modelName + '[_id=' + ident + ']';
        } else if (obj === undefined) {
            return 'undefined';
        } else if (obj === null) {
            return 'null';
        }
    }

    if (util.isArray(obj)) return _.map(_objAsString, obj).join(', ');
    return _objAsString(obj);
}

function clearReverseRelated(opts) {
    opts = opts || {};
    var self = this;
    if (!self.isFault) {
        if (this.related) {
            var reverseProxy = getReverseProxyForObject.call(this, this.related);
            var reverseProxies = util.isArray(reverseProxy) ? reverseProxy : [reverseProxy];
            _.each(reverseProxies, function (p) {
                if (util.isArray(p._id)) {
                    var idx = p._id.indexOf(self.object._id);
                    makeChangesToRelatedWithoutObservations.call(p, function () {
                        spliceFactory(opts).call(p, idx, 1);
                    });
                } else {
                    set.call(p, null, opts);
                }
            });
        }
    } else {
        if (self._id) {
            var reverseName = getReverseName.call(this);
            var reverseModel = getReverseModel.call(this);
            var identifiers = util.isArray(self._id) ? self._id : [self._id];
            if (this._reverseIsArray) {
                if (!opts.disableNotifications) {
                    _.each(identifiers, function (_id) {
                        coreChanges.registerChange({
                            collection: reverseModel.collectionName,
                            model: reverseModel.name,
                            _id: _id,
                            field: reverseName,
                            removedId: [self.object._id],
                            removed: [self.object],
                            type: ChangeType.Delete,
                            obj: self.object
                        });
                    });
                }
            } else {
                if (!opts.disableNotifications) {
                    _.each(identifiers, function (_id) {
                        coreChanges.registerChange({
                            collection: reverseModel.collectionName,
                            model: reverseModel.name,
                            _id: _id,
                            field: reverseName,
                            new: null,
                            newId: null,
                            oldId: self.object._id,
                            old: self.object,
                            type: ChangeType.Set,
                            obj: self.object
                        });
                    });
                }
            }

        } else {
            throw new Error(getForwardName.call(this) + ' has no _id');
        }
    }
}

function makeChangesToRelatedWithoutObservations(f) {
    if (this.related) {
        this.related.oneToManyObserver.close();
        this.related.oneToManyObserver = null;
        f();
        wrapArray.call(this, this.related);
    } else {
        // If there's a fault we can make changes anyway.
        f();
    }
}

function setReverse(obj, opts) {
    var self = this;
    var reverseProxy = getReverseProxyForObject.call(this, obj);
    var reverseProxies = util.isArray(reverseProxy) ? reverseProxy : [reverseProxy];
    _.each(reverseProxies, function (p) {
        if (util.isArray(p._id)) {
            makeChangesToRelatedWithoutObservations.call(p, function () {
                spliceFactory(opts).call(p, p._id.length, 0, self.object);
            });
        } else {
            clearReverseRelated.call(p, opts);
            set.call(p, self.object, opts);
        }
    });
}

function registerSetChange(obj) {
    var proxyObject = this.object;
    if (!proxyObject) throw new InternalSiestaError('Proxy must have an object associated');
    var model = proxyObject.model.name;
    var collectionName = proxyObject.collectionName;
    var newId;
    if (util.isArray(obj)) {
        newId = _.pluck(obj, '_id');
    } else {
        newId = obj ? obj._id : obj;
    }
    // We take [] == null == undefined in the case of relationships.
    var oldId = this._id;
    if (util.isArray(oldId) && !oldId.length) {
        oldId = null;
    }
    var old = this.related;
    if (util.isArray(old) && !old.length) {
        old = null;
    }
    coreChanges.registerChange({
        collection: collectionName,
        model: model,
        _id: proxyObject._id,
        field: getForwardName.call(this),
        newId: newId,
        oldId: oldId,
        old: old,
        new: obj,
        type: ChangeType.Set,
        obj: proxyObject
    });
}

function registerSpliceChange(idx, numRemove) {
    var add = Array.prototype.slice.call(arguments, 2);
    var model = this.object.model.name;
    var coll = this.object.collectionName;
    coreChanges.registerChange({
        collection: coll,
        model: model,
        _id: this.object._id,
        field: getForwardName.call(this),
        index: idx,
        removedId: this._id.slice(idx, idx + numRemove),
        removed: this.related ? this.related.slice(idx, idx + numRemove) : null,
        addedId: add.length ? _.pluck(add, '_id') : [],
        added: add.length ? add : [],
        type: ChangeType.Splice,
        obj: this.object
    });
}

function wrapArray(arr) {
    var self = this;
    wrapArrayForAttributes(arr, this.reverseName, this.object);
    if (!arr.oneToManyObserver) {
        arr.oneToManyObserver = new ArrayObserver(arr);
        var observerFunction = function (splices) {
            splices.forEach(function (splice) {
                var added = splice.addedCount ? arr.slice(splice.index, splice.index + splice.addedCount) : [];
                var model = getForwardModel.call(self);
                coreChanges.registerChange({
                    collection: model.collectionName,
                    model: model.name,
                    _id: self.object._id,
                    field: getForwardName.call(self),
                    removed: splice.removed,
                    added: added,
                    removedId: _.pluck(splice.removed, '_id'),
                    addedId: _.pluck(splice.added, '_id'),
                    type: ChangeType.Splice,
                    obj: self.object
                });
            });
        };
        arr.oneToManyObserver.open(observerFunction);
    }
}

module.exports = {
    RelationshipProxy: RelationshipProxy,
    Fault: Fault,
    getReverseProxyForObject: getReverseProxyForObject,
    getForwardProxyForObject: getForwardProxyForObject,
    getReverseName: getReverseName,
    getForwardName: getForwardName,
    getReverseModel: getReverseModel,
    getForwardModel: getForwardModel,
    checkInstalled: checkInstalled,
    set: set,
    registerSetChange: registerSetChange,
    splice:splice,
    spliceFactory: spliceFactory,
    clearReverseRelated:  clearReverseRelated,
    setReverse:  setReverse,
    objAsString:  objAsString,
    wrapArray:  wrapArray,
    registerSpliceChange:  registerSpliceChange,
    makeChangesToRelatedWithoutObservations:  makeChangesToRelatedWithoutObservations
};

<commit_msg>some proxy refactoring<commit_after>/**
 * Base functionality for relationships.
 * @module relationships
 */

var InternalSiestaError = require('./error').InternalSiestaError,
    Store = require('./store'),
    Operation = require('./operation/operation').Operation,
    util = require('./util'),
    defineSubProperty = util.defineSubProperty,
    _ = util._,
    Query = require('./query'),
    log = require('./operation/log'),
    notifications = require('./notifications'),
    wrapArrayForAttributes = notifications.wrapArray,
    ArrayObserver = require('../vendor/observe-js/src/observe').ArrayObserver,
    coreChanges = require('./changes'),
    ChangeType = coreChanges.ChangeType;

/**
 * A fault occurs when we try to access a property that has not been loaded from disk
 * @param {RelationshipProxy} proxy
 * @constructor
 */
function Fault(proxy) {
    var self = this;
    this.proxy = proxy;
    Object.defineProperty(this, 'isFault', {
        get: function () {
            return self.proxy.isFault;
        },
        enumerable: true,
        configurable: true
    });
}

_.extend(Fault.prototype, {
    get: function () {
        this.proxy.get.apply(this.proxy, arguments);
    },
    set: function () {
        this.proxy.set.apply(this.proxy, arguments);
    }
});

/**
 * @class  [RelationshipProxy description]
 * @param {Object} opts
 * @constructor
 */
function RelationshipProxy(opts) {
    this._opts = opts;
    var self = this;

    _.extend(this, {
        fault: new Fault(this),
        object: null,
        _id: undefined,
        related: null
    });

    Object.defineProperties(this, {
        isFault: {
            get: function () {
                if (self._id) {
                    return !self.related;
                } else if (self._id === null) {
                    return false;
                }
                return true;
            },
            set: function (v) {
                if (v) {
                    self._id = undefined;
                    self.related = null;
                } else {
                    if (!self._id) {
                        self._id = null;
                    }
                }
            },
            enumerable: true,
            configurable: true
        },
        isForward: {
            get: function () {
                return !self.isReverse;
            },
            set: function (v) {
                self.isReverse = !v;
            },
            enumerable: true
        }
    });

    defineSubProperty.call(this, 'reverseModel', this._opts);
    defineSubProperty.call(this, 'forwardModel', this._opts);
    defineSubProperty.call(this, 'forwardName', this._opts);
    defineSubProperty.call(this, 'reverseName', this._opts);
    defineSubProperty.call(this, 'isReverse', this._opts);

    if (this._opts.isReverse === undefined && this._opts.isForward !== undefined) {
        this.isReverse = !this._opts.isForward;
    }
    else if (this._opts.isReverse === undefined && this._opts.isForward === undefined) {
        throw new InternalSiestaError('Must specify either isReverse or isForward when configuring relationship proxy.');
    }
}

_.extend(RelationshipProxy.prototype, {
    _dump: function (asJson) {
        var dumped = {};
    },
    install: function (obj) {
        if (obj) {
            if (!this.object) {
                this.object = obj;
                var self = this;
                var name = getForwardName.call(this);
                Object.defineProperty(obj, name, {
                    get: function () {
                        if (self.isFault) {
                            return self.fault;
                        } else {
                            return self.related;
                        }
                    },
                    set: function (v) {
                        self.set(v);
                    },
                    configurable: true,
                    enumerable: true
                });
                if (!obj.__proxies) obj.__proxies = {};
                obj.__proxies[name] = this;
                if (!obj._proxies) {
                    obj._proxies = [];
                }
                obj._proxies.push(this);
            } else {
                throw new InternalSiestaError('Already installed.');
            }
        } else {
            throw new InternalSiestaError('No object passed to relationship install');
        }
    },
    set: function () {
        throw new InternalSiestaError('Must subclass RelationshipProxy');
    },
    get: function () {
        throw new InternalSiestaError('Must subclass RelationshipProxy');
    }
});

// TODO: Share code between getReverseProxyForObject and getForwardProxyForObject

function getReverseProxyForObject(obj) {
    var reverseName = getReverseName.call(this);
    var reverseModel = this.reverseModel;
    // This should never happen. Should g   et caught in the mapping operation?
    if (util.isArray(obj)) {
        return _.map(obj, function (o) {
            return o.__proxies[reverseName];
        })
    } else {
        var proxy = obj.__proxies[reverseName];
        if (!proxy) {
            var err = 'No proxy with name "' + reverseName + '" on mapping ' + reverseModel.name;
            throw new InternalSiestaError(err);
        }
        return proxy;
    }
}

function getForwardProxyForObject(obj) {
    var forwardName = getForwardName.call(this);
    var forwardModel = this.forwardModel;
    if (util.isArray(obj)) {
        return _.map(obj, function (o) {
            return o.__proxies[forwardName];
        })
    } else {
        var proxy = obj.__proxies[forwardName];
        if (!proxy) {
            var err = 'No proxy with name "' + forwardName + '" on mapping ' + forwardModel.name;
            throw new InternalSiestaError(err);
        }
        return proxy;
    }
}

function getReverseName() {
    return this.isForward ? this.reverseName : this.forwardName;
}

function getForwardName() {
    return this.isForward ? this.forwardName : this.reverseName;
}

function getReverseModel() {
    return this.isForward ? this.reverseModel : this.forwardModel;
}

function getForwardModel() {
    return this.isForward ? this.forwardModel : this.reverseModel;
}

function checkInstalled() {
    if (!this.object) {
        throw new InternalSiestaError('Proxy must be installed on an object before can use it.');
    }
}

/**
 * Configure _id and related with the new related object.
 * @param obj
 * @param {object} [opts]
 * @param {boolean} [opts.disableNotifications]
 * @returns {String|undefined} - Error message or undefined
 */
function set(obj, opts) {
    opts = opts || {};
    if (!opts.disableNotifications) {
        registerSetChange.call(this, obj);
    }
    if (obj) {
        if (util.isArray(obj)) {
            this._id = _.pluck(obj, '_id');
            this.related = obj;
        } else {
            this._id = obj._id;
            this.related = obj;
        }
    } else {
        this._id = null;
        this.related = null;
    }
}

function spliceFactory(opts) {
    opts = opts || {};
    return function (idx, numRemove) {
        opts = opts || {};
        if (!opts.disableNotifications) {
            registerSpliceChange.apply(this, arguments);
        }
        var add = Array.prototype.slice.call(arguments, 2);
        var returnValue = _.partial(this._id.splice, idx, numRemove).apply(this._id, _.pluck(add, '_id'));
        if (this.related) {
            _.partial(this.related.splice, idx, numRemove).apply(this.related, add);
        }
        return returnValue;
    }
}

var splice = spliceFactory({});

//function splice(idx, numRemove) {
//    registerSpliceChange.apply(this, arguments);
//    var add = Array.prototype.slice.call(arguments, 2);
//    var returnValue = _.partial(this._id.splice, idx, numRemove).apply(this._id, _.pluck(add, '_id'));
//    if (this.related) {
//        _.partial(this.related.splice, idx, numRemove).apply(this.related, add);
//    }
//    return returnValue;
//}

function objAsString(obj) {
    function _objAsString(obj) {
        if (obj) {
            var model = obj.model;
            var modelName = model.name;
            var ident = obj._id;
            if (typeof ident == 'string') {
                ident = '"' + ident + '"';
            }
            return modelName + '[_id=' + ident + ']';
        } else if (obj === undefined) {
            return 'undefined';
        } else if (obj === null) {
            return 'null';
        }
    }

    if (util.isArray(obj)) return _.map(_objAsString, obj).join(', ');
    return _objAsString(obj);
}

function clearReverseRelated(opts) {
    opts = opts || {};
    var self = this;
    if (!self.isFault) {
        if (this.related) {
            var reverseProxy = getReverseProxyForObject.call(this, this.related);
            var reverseProxies = util.isArray(reverseProxy) ? reverseProxy : [reverseProxy];
            _.each(reverseProxies, function (p) {
                if (util.isArray(p._id)) {
                    var idx = p._id.indexOf(self.object._id);
                    makeChangesToRelatedWithoutObservations.call(p, function () {
                        spliceFactory(opts).call(p, idx, 1);
                    });
                } else {
                    set.call(p, null, opts);
                }
            });
        }
    } else {
        if (self._id) {
            var reverseName = getReverseName.call(this);
            var reverseModel = getReverseModel.call(this);
            var identifiers = util.isArray(self._id) ? self._id : [self._id];
            if (this._reverseIsArray) {
                if (!opts.disableNotifications) {
                    _.each(identifiers, function (_id) {
                        coreChanges.registerChange({
                            collection: reverseModel.collectionName,
                            model: reverseModel.name,
                            _id: _id,
                            field: reverseName,
                            removedId: [self.object._id],
                            removed: [self.object],
                            type: ChangeType.Delete,
                            obj: self.object
                        });
                    });
                }
            } else {
                if (!opts.disableNotifications) {
                    _.each(identifiers, function (_id) {
                        coreChanges.registerChange({
                            collection: reverseModel.collectionName,
                            model: reverseModel.name,
                            _id: _id,
                            field: reverseName,
                            new: null,
                            newId: null,
                            oldId: self.object._id,
                            old: self.object,
                            type: ChangeType.Set,
                            obj: self.object
                        });
                    });
                }
            }

        } else {
            throw new Error(getForwardName.call(this) + ' has no _id');
        }
    }
}

function makeChangesToRelatedWithoutObservations(f) {
    if (this.related) {
        this.related.oneToManyObserver.close();
        this.related.oneToManyObserver = null;
        f();
        wrapArray.call(this, this.related);
    } else {
        // If there's a fault we can make changes anyway.
        f();
    }
}

function setReverse(obj, opts) {
    var self = this;
    var reverseProxy = getReverseProxyForObject.call(this, obj);
    var reverseProxies = util.isArray(reverseProxy) ? reverseProxy : [reverseProxy];
    _.each(reverseProxies, function (p) {
        if (util.isArray(p._id)) {
            makeChangesToRelatedWithoutObservations.call(p, function () {
                spliceFactory(opts).call(p, p._id.length, 0, self.object);
            });
        } else {
            clearReverseRelated.call(p, opts);
            set.call(p, self.object, opts);
        }
    });
}

function registerSetChange(obj) {
    var proxyObject = this.object;
    if (!proxyObject) throw new InternalSiestaError('Proxy must have an object associated');
    var model = proxyObject.model.name;
    var collectionName = proxyObject.collectionName;
    var newId;
    if (util.isArray(obj)) {
        newId = _.pluck(obj, '_id');
    } else {
        newId = obj ? obj._id : obj;
    }
    // We take [] == null == undefined in the case of relationships.
    var oldId = this._id;
    if (util.isArray(oldId) && !oldId.length) {
        oldId = null;
    }
    var old = this.related;
    if (util.isArray(old) && !old.length) {
        old = null;
    }
    coreChanges.registerChange({
        collection: collectionName,
        model: model,
        _id: proxyObject._id,
        field: getForwardName.call(this),
        newId: newId,
        oldId: oldId,
        old: old,
        new: obj,
        type: ChangeType.Set,
        obj: proxyObject
    });
}

function registerSpliceChange(idx, numRemove) {
    var add = Array.prototype.slice.call(arguments, 2);
    var model = this.object.model.name;
    var coll = this.object.collectionName;
    coreChanges.registerChange({
        collection: coll,
        model: model,
        _id: this.object._id,
        field: getForwardName.call(this),
        index: idx,
        removedId: this._id.slice(idx, idx + numRemove),
        removed: this.related ? this.related.slice(idx, idx + numRemove) : null,
        addedId: add.length ? _.pluck(add, '_id') : [],
        added: add.length ? add : [],
        type: ChangeType.Splice,
        obj: this.object
    });
}

function wrapArray(arr) {
    var self = this;
    wrapArrayForAttributes(arr, this.reverseName, this.object);
    if (!arr.oneToManyObserver) {
        arr.oneToManyObserver = new ArrayObserver(arr);
        var observerFunction = function (splices) {
            splices.forEach(function (splice) {
                var added = splice.addedCount ? arr.slice(splice.index, splice.index + splice.addedCount) : [];
                var model = getForwardModel.call(self);
                coreChanges.registerChange({
                    collection: model.collectionName,
                    model: model.name,
                    _id: self.object._id,
                    field: getForwardName.call(self),
                    removed: splice.removed,
                    added: added,
                    removedId: _.pluck(splice.removed, '_id'),
                    addedId: _.pluck(splice.added, '_id'),
                    type: ChangeType.Splice,
                    obj: self.object
                });
            });
        };
        arr.oneToManyObserver.open(observerFunction);
    }
}

module.exports = {
    RelationshipProxy: RelationshipProxy,
    Fault: Fault,
    getReverseProxyForObject: getReverseProxyForObject,
    getForwardProxyForObject: getForwardProxyForObject,
    getReverseName: getReverseName,
    getForwardName: getForwardName,
    getReverseModel: getReverseModel,
    getForwardModel: getForwardModel,
    checkInstalled: checkInstalled,
    set: set,
    registerSetChange: registerSetChange,
    splice:splice,
    spliceFactory: spliceFactory,
    clearReverseRelated:  clearReverseRelated,
    setReverse:  setReverse,
    objAsString:  objAsString,
    wrapArray:  wrapArray,
    registerSpliceChange:  registerSpliceChange,
    makeChangesToRelatedWithoutObservations:  makeChangesToRelatedWithoutObservations
};

<|endoftext|>