<commit_before>// SensorTag object
var sensortag;

// Various variables
var tossMode = true;

var pressed = false;    // Button press indicator (default false)
var caught = true;      // Device caught indicator (default true)
var peaked = false;     // Apogee reached indicator (default false)

var prevAccel;          // Previous z-axis acceleration measurement

var acc_rate = 100; 	// Accelerometer sample rate in ms (1-1000)
var throw_level = 8;    // Lower bound for velocity change indicating a throw (large)
var catch_level = 2;    // Lower bound for velocity change indicating a catch (medium)
var peak_level = 0.05;  // Upper bound for velocity change indicating apogee reached (small)

var spinAcc = {x: [0], y: [0], z: [0]};      // An array containing each instance of acceleration data

var startTime = 0;      // Start time variable
var peakTime = 0;       // Apogee time variable
var caughtTime = 0;     // Catch time variable

// Score limits
var maxThrow = 1895;    // The highest throw
var minThrow = 184;     // The lowest throw

var maxSpin = 80;    // The highest spin
var minSpin = 1;      // the lowest spin

var maxScore = 1000; 	// The highest attainable score

// SensorTag characteristic id
var characteristicId = '00002a24-0000-1000-8000-00805f9b34fb';

// SensorTag connection variables
var attemptToConnect = false;   // Whether or not the user is currently attempting to connect
var connected = false;          // Whether or not a SensorTag is currently connected
var connecting = false;         // Whether or not the app is actively trying to connect
var readTimeout = null;         // Timeout for reading characteristics

function getTossMode() {
    return tossMode;
}

function setTossMode(value) {
    tossMode = value;
}

function getPressed() {
    return pressed;
}

function setPressed(value) {
    pressed = value;
}

function initializeSensorTag() {
    sensortag = evothings.tisensortag.createInstance(evothings.tisensortag.CC2650_BLUETOOTH_SMART);

    sensortag.statusCallback(statusHandler);
    sensortag.errorCallback(errorHandler);
    sensortag.accelerometerCallback(accelerometerHandler, acc_rate);
    sensortag.gyroscopeCallback(gyroscopeHandler, acc_rate);
    sensortag.keypressCallback(function(){}, 1000);
    sensortag.temperatureCallback(function(){}, 1000);
    sensortag.barometerCallback(function(){}, 1000);
    sensortag.magnetometerCallback(function(){}, 1000);
    sensortag.humidityCallback(function(){}, 1000);
    sensortag.luxometerCallback(function(){}, 1000);
}

function checkConnection(attemptToConnect) {
    if (sensortag && sensortag.device) {
        readTimeout = setTimeout(
            function() {
                connected = false;
            },
            400
        );

        try {
            sensortag.device.readCharacteristic(
                characteristicId,
                function() {
                    if (readTimeout != null) {
                        clearTimeout(readTimeout);
                    }
                    connected = true;
                },
                function() {
                    connected = false;
                }
            );
        } catch(e) {
          connected=false;
        }
    } else {
        connected = false;
    }

    if (attemptToConnect) {
        if (connected) {
            // set bluetooth-connected.png
            console.log('checkConnection(): bluetooth-connected');
        } else {
            // set bluetooth-connecting.png
            console.log('checkConnection(): bluetooth-connecting');
            if (!connecting)
            {
                connecting = true;
                setTimeout(
                    function() {
                        connecting = false
                    },
                    5000
                );
                if (sensortag != null) {
                    sensortag.connectToNearestDevice();
                }
            }
        }
    } else {
        // set bluetooth-off.png
        console.log('checkConnection(): bluetooth-off');
        if (sensortag != null) {
            sensortag.disconnectDevice();
        }
    }
}

function statusHandler(status)
{
    console.log(status);

    if ('CONNECTED' == status)
    {
        if (readTimeout != null) {
            clearTimeout(readTimeout);
        }
        connected = true;
    }
    if ('EASYBLE_ERROR_DISCONNECTED' == status)
    {
        connected = false;
    }
}

function errorHandler(error) {
    console.log('Error: ' + error);
}

// Capture the acceleration data
function accelerometerHandler(data) {
    if (tossMode) {
        var values = sensortag.getAccelerometerValues(data);
        var x = values.x;
        var y = values.y;
        var z = values.z;

        // Get the total magnitude of acceleration on the device
        var total = Math.sqrt(Math.pow(x,2)+Math.pow(y,2)+Math.pow(z,2));

        if (pressed) {
            // Calculate and store change in velocity
            var velocityChange = Math.abs(total-prevAccel)/(acc_rate/1000);

            // When thrown
            if (caught && (velocityChange >= throw_level)) {
                startTime = (new Date()).getTime();
                caught = false;
                peaked = false;
                displayValue("scoreField", "Thrown");

                // When apogee reached
            } else if (!caught && !peaked && (velocityChange <= peak_level)) {
                peakTime = (new Date()).getTime() - startTime;
                peaked = true;
                displayValue("scoreField", "Peaked");

                // When caught (and apogee detected)
            } else if (!caught && peaked && (velocityChange >= catch_level)) {
                caughtTime = (new Date()).getTime() - startTime;
                caught = true;
                stopButtonPress(); // Don't wait for a button press
            } else if(!caught && peaked) {
              var time = ((new Date()).getTime() - startTime - peakTime);

              var heightScore = calculateScore(time, maxThrow, minThrow);
              displayValue('scoreField', "" + heightScore);
            }
        }

        // Update last accelerometer value
        prevAccel = total;
    }
}

function gyroscopeHandler(data) {
    if (!tossMode) {
        // Calculate the gyroscope values from raw sensor data.
        var values = sensortag.getGyroscopeValues(data);
        var x = values.x;
        var y = values.y;
        var z = values.z;

        if (pressed) {
            var spinScore = determineSpinScore(x, y, z);
            displayValue('scoreField', Math.abs(spinScore));
        } else {
          spinScore = 0;
        }
    }
}

// Ends tracking, displays values & resets data
function stopTracking() {
    if (tossMode) {
        var time = (caughtTime - peakTime);

        var heightScore = calculateScore(time, maxThrow, minThrow);
        displayValue('scoreField', "" + heightScore);

        caught = true;
        peaked = false;
        startTime = 0;
        peakTime = 0;
        caughtTime = 0;
    } else {
        // Calculate score of spin data
        var spinScore = determineSpinScore(0, 0, 0);
        displayValue('scoreField', Math.abs(spinScore));
        spinAcc = {x: [0], y: [0], z: [0]} ;
        HighSpin = 0;
    }
}

function countHighAccelerations(arr){
    var count = 1;
    for (var i=0; i<arr.length; i++) {
        if (Math.abs(arr[i])>250) {
            count+=1;
        }
    }
    return count;
}

// Decides what the highest spin axis is and returns its score
function determineSpinScore(x, y, z) {
    // Start with the x axis
    spinAcc['x'].push(x);
    var spinScore = calculateScore(countHighAccelerations(spinAcc['x']), maxSpin, minSpin);

    // Compare to x axis
    spinAcc['y'].push(y);
    var temp = calculateScore(countHighAccelerations(spinAcc['y']), maxSpin, minSpin);
    spinScore = temp > spinScore ? temp : spinScore;

    // Compare to z axis
    spinAcc['z'].push(z);
    temp = calculateScore(countHighAccelerations(spinAcc['z']), maxSpin, minSpin);
    spinScore = temp > spinScore ? temp : spinScore;

    return spinScore;
}

// Takes a variable representing time of throw in ms, returns a score from 0-10 which ranks the throw relative to the maximum and minimum throws possible
function calculateScore(time, max, min) {
    var score = Math.round(((time-min)/(max-min))*maxScore);

    // Don't return negative scores (use zero instead) or scores greater than the maximum score
    return (score > maxScore) ? maxScore : (score < 0) ? 0 : score;
}

function median(values) {
    values.sort( function(a,b) {return a - b;} );

    var half = Math.floor(values.length/2);

    if (values.length % 2) {
        return values[half];
    } else {
        return (values[half-1] + values[half]) / 2.0;
    }
}

document.addEventListener(
    'deviceready',
    function() {
        evothings.scriptsLoaded(initializeSensorTag);
    },
    false
);

setInterval(
    function() {
        checkConnection(document.getElementById('connectCheckbox').checked);
    },
    500
);
<commit_msg>made spin tracking end when 250 stops appearing<commit_after>// SensorTag object
var sensortag;

// Various variables
var tossMode = true;

var pressed = false;    // Button press indicator (default false)
var caught = true;      // Device caught indicator (default true)
var peaked = false;     // Apogee reached indicator (default false)

var prevAccel;          // Previous z-axis acceleration measurement

var acc_rate = 100; 	// Accelerometer sample rate in ms (1-1000)
var throw_level = 8;    // Lower bound for velocity change indicating a throw (large)
var catch_level = 2;    // Lower bound for velocity change indicating a catch (medium)
var peak_level = 0.05;  // Upper bound for velocity change indicating apogee reached (small)

var spinAcc = {x: [0], y: [0], z: [0]};      // An array containing each instance of acceleration data

var startTime = 0;      // Start time variable
var peakTime = 0;       // Apogee time variable
var caughtTime = 0;     // Catch time variable

// Score limits
var maxThrow = 1895;    // The highest throw
var minThrow = 184;     // The lowest throw

var maxSpin = 80;    // The highest spin
var minSpin = 1;      // the lowest spin

var maxScore = 1000; 	// The highest attainable score

// SensorTag characteristic id
var characteristicId = '00002a24-0000-1000-8000-00805f9b34fb';

// SensorTag connection variables
var attemptToConnect = false;   // Whether or not the user is currently attempting to connect
var connected = false;          // Whether or not a SensorTag is currently connected
var connecting = false;         // Whether or not the app is actively trying to connect
var readTimeout = null;         // Timeout for reading characteristics

function getTossMode() {
    return tossMode;
}

function setTossMode(value) {
    tossMode = value;
}

function getPressed() {
    return pressed;
}

function setPressed(value) {
    pressed = value;
}

function initializeSensorTag() {
    sensortag = evothings.tisensortag.createInstance(evothings.tisensortag.CC2650_BLUETOOTH_SMART);

    sensortag.statusCallback(statusHandler);
    sensortag.errorCallback(errorHandler);
    sensortag.accelerometerCallback(accelerometerHandler, acc_rate);
    sensortag.gyroscopeCallback(gyroscopeHandler, acc_rate);
    sensortag.keypressCallback(function(){}, 1000);
    sensortag.temperatureCallback(function(){}, 1000);
    sensortag.barometerCallback(function(){}, 1000);
    sensortag.magnetometerCallback(function(){}, 1000);
    sensortag.humidityCallback(function(){}, 1000);
    sensortag.luxometerCallback(function(){}, 1000);
}

function checkConnection(attemptToConnect) {
    if (sensortag && sensortag.device) {
        readTimeout = setTimeout(
            function() {
                connected = false;
            },
            400
        );

        try {
            sensortag.device.readCharacteristic(
                characteristicId,
                function() {
                    if (readTimeout != null) {
                        clearTimeout(readTimeout);
                    }
                    connected = true;
                },
                function() {
                    connected = false;
                }
            );
        } catch(e) {
          connected=false;
        }
    } else {
        connected = false;
    }

    if (attemptToConnect) {
        if (connected) {
            // set bluetooth-connected.png
            console.log('checkConnection(): bluetooth-connected');
        } else {
            // set bluetooth-connecting.png
            console.log('checkConnection(): bluetooth-connecting');
            if (!connecting)
            {
                connecting = true;
                setTimeout(
                    function() {
                        connecting = false
                    },
                    5000
                );
                if (sensortag != null) {
                    sensortag.connectToNearestDevice();
                }
            }
        }
    } else {
        // set bluetooth-off.png
        console.log('checkConnection(): bluetooth-off');
        if (sensortag != null) {
            sensortag.disconnectDevice();
        }
    }
}

function statusHandler(status)
{
    console.log(status);

    if ('CONNECTED' == status)
    {
        if (readTimeout != null) {
            clearTimeout(readTimeout);
        }
        connected = true;
    }
    if ('EASYBLE_ERROR_DISCONNECTED' == status)
    {
        connected = false;
    }
}

function errorHandler(error) {
    console.log('Error: ' + error);
}

// Capture the acceleration data
function accelerometerHandler(data) {
    if (tossMode) {
        var values = sensortag.getAccelerometerValues(data);
        var x = values.x;
        var y = values.y;
        var z = values.z;

        // Get the total magnitude of acceleration on the device
        var total = Math.sqrt(Math.pow(x,2)+Math.pow(y,2)+Math.pow(z,2));

        if (pressed) {
            // Calculate and store change in velocity
            var velocityChange = Math.abs(total-prevAccel)/(acc_rate/1000);

            // When thrown
            if (caught && (velocityChange >= throw_level)) {
                startTime = (new Date()).getTime();
                caught = false;
                peaked = false;
                displayValue("scoreField", "Thrown");

                // When apogee reached
            } else if (!caught && !peaked && (velocityChange <= peak_level)) {
                peakTime = (new Date()).getTime() - startTime;
                peaked = true;
                displayValue("scoreField", "Peaked");

                // When caught (and apogee detected)
            } else if (!caught && peaked && (velocityChange >= catch_level)) {
                caughtTime = (new Date()).getTime() - startTime;
                caught = true;
                stopButtonPress(); // Don't wait for a button press
            } else if(!caught && peaked) {
              var time = ((new Date()).getTime() - startTime - peakTime);

              var heightScore = calculateScore(time, maxThrow, minThrow);
              displayValue('scoreField', "" + heightScore);
            }
        }

        // Update last accelerometer value
        prevAccel = total;
    }
}

function gyroscopeHandler(data) {
    if (!tossMode) {
        // Calculate the gyroscope values from raw sensor data.
        var values = sensortag.getGyroscopeValues(data);
        var x = values.x;
        var y = values.y;
        var z = values.z;

        if (pressed) {
            var spinScore = determineSpinScore(x, y, z);
            displayValue('scoreField', Math.abs(spinScore));
        } else {
          spinScore = 0;
        }
    }
}

// Ends tracking, displays values & resets data
function stopTracking() {
    if (tossMode) {
        var time = (caughtTime - peakTime);

        var heightScore = calculateScore(time, maxThrow, minThrow);
        displayValue('scoreField', "" + heightScore);

        caught = true;
        peaked = false;
        startTime = 0;
        peakTime = 0;
        caughtTime = 0;
    } else {
        // Calculate score of spin data
        spinAcc = {x: [0], y: [0], z: [0]} ;
        HighSpin = 0;
    }
}

function countHighAccelerations(arr){
    var count = 1;
    for (var i=0; i<arr.length; i++) {
        if (Math.abs(arr[i])>250) {
            count+=1;
        }
    }
    return count;
}

// Decides what the highest spin axis is and returns its score
function determineSpinScore(x, y, z) {
    // Start with the x axis
    spinAcc['x'].push(x);
    var spinScore = calculateScore(countHighAccelerations(spinAcc['x']), maxSpin, minSpin);
    var val = 'x';

    // Compare to x axis
    spinAcc['y'].push(y);
    var temp = calculateScore(countHighAccelerations(spinAcc['y']), maxSpin, minSpin);
    if(temp>spinScore) {
      spinScore = temp;
      val = 'y';
    }

    // Compare to z axis
    spinAcc['z'].push(z);
    temp = calculateScore(countHighAccelerations(spinAcc['z']), maxSpin, minSpin);
    if(temp>spinScore) {
      spinScore = temp;
      val = 'z';
    }

    if(spinAcc[val][spinAcc[val].length-1]<250) {
      stopButtonPress();
    }

    return spinScore;
}

// Takes a variable representing time of throw in ms, returns a score from 0-10 which ranks the throw relative to the maximum and minimum throws possible
function calculateScore(time, max, min) {
    var score = Math.round(((time-min)/(max-min))*maxScore);

    // Don't return negative scores (use zero instead) or scores greater than the maximum score
    return (score > maxScore) ? maxScore : (score < 0) ? 0 : score;
}

function median(values) {
    values.sort( function(a,b) {return a - b;} );

    var half = Math.floor(values.length/2);

    if (values.length % 2) {
        return values[half];
    } else {
        return (values[half-1] + values[half]) / 2.0;
    }
}

document.addEventListener(
    'deviceready',
    function() {
        evothings.scriptsLoaded(initializeSensorTag);
    },
    false
);

setInterval(
    function() {
        checkConnection(document.getElementById('connectCheckbox').checked);
    },
    500
);
<|endoftext|>


<commit_before>const INPUT_FOCUSED = 'INPUT_FOCUSED';
const INPUT_BLURRED = 'INPUT_BLURRED';
const INPUT_CHANGED = 'INPUT_CHANGED';
const UPDATE_FOCUSED_SUGGESTION = 'UPDATE_FOCUSED_SUGGESTION';
const RESET_FOCUSED_SUGGESTION = 'RESET_FOCUSED_SUGGESTION';
const REVEAL_SUGGESTIONS = 'REVEAL_SUGGESTIONS';
const CLOSE_SUGGESTIONS = 'CLOSE_SUGGESTIONS';

const inputFocused = shouldRenderSuggestions => ({
  type: INPUT_FOCUSED,
  shouldRenderSuggestions
});

const inputBlurred = shouldRenderSuggestions => ({
  type: INPUT_BLURRED,
  shouldRenderSuggestions
});

const inputChanged = shouldRenderSuggestions => ({
  type: INPUT_CHANGED,
  shouldRenderSuggestions
});

const updateFocusedSuggestion = (sectionIndex, suggestionIndex, value) => ({
  type: UPDATE_FOCUSED_SUGGESTION,
  sectionIndex,
  suggestionIndex,
  value
});

const resetFocusedSuggestion = (shouldResetValueBeforeUpDown = true) => ({
  type: RESET_FOCUSED_SUGGESTION,
  shouldResetValueBeforeUpDown
});

const revealSuggestions = () => ({
  type: REVEAL_SUGGESTIONS
});

const closeSuggestions = () => ({
  type: CLOSE_SUGGESTIONS
});

export const actionCreators = {
  inputFocused,
  inputBlurred,
  inputChanged,
  updateFocusedSuggestion,
  resetFocusedSuggestion,
  revealSuggestions,
  closeSuggestions
};

const reducer = (state, action) => {
  switch (action.type) {
    case INPUT_FOCUSED:
      return {
        ...state,
        isFocused: true,
        isCollapsed: !action.shouldRenderSuggestions
      };

    case INPUT_BLURRED:
      return {
        ...state,
        isFocused: false,
        focusedSectionIndex: null,
        focusedSuggestionIndex: null,
        valueBeforeUpDown: null,
        isCollapsed: !action.shouldRenderSuggestions
      };

    case INPUT_CHANGED:
      return {
        ...state,
        focusedSectionIndex: null,
        focusedSuggestionIndex: null,
        valueBeforeUpDown: null,
        isCollapsed: !action.shouldRenderSuggestions
      };

    case UPDATE_FOCUSED_SUGGESTION: {
      const { sectionIndex, suggestionIndex, value } = action;
      let { valueBeforeUpDown } = state;

      if (suggestionIndex === null) {
        valueBeforeUpDown = null;
      } else if (valueBeforeUpDown === null && typeof value !== 'undefined') {
        valueBeforeUpDown = value;
      }

      return {
        ...state,
        focusedSectionIndex: sectionIndex,
        focusedSuggestionIndex: suggestionIndex,
        valueBeforeUpDown
      };
    }

    case RESET_FOCUSED_SUGGESTION: {
      return {
        ...state,
        focusedSectionIndex: null,
        focusedSuggestionIndex: null,
        valueBeforeUpDown: action.shouldResetValueBeforeUpDown ? null : state.valueBeforeUpDown
      };
    }

    case REVEAL_SUGGESTIONS:
      return {
        ...state,
        isCollapsed: false
      };

    case CLOSE_SUGGESTIONS:
      return {
        ...state,
        focusedSectionIndex: null,
        focusedSuggestionIndex: null,
        valueBeforeUpDown: null,
        isCollapsed: true
      };

    default:
      return state;
  }
};

export default reducer;
<commit_msg>Change value to prevValue for clarity<commit_after>const INPUT_FOCUSED = 'INPUT_FOCUSED';
const INPUT_BLURRED = 'INPUT_BLURRED';
const INPUT_CHANGED = 'INPUT_CHANGED';
const UPDATE_FOCUSED_SUGGESTION = 'UPDATE_FOCUSED_SUGGESTION';
const RESET_FOCUSED_SUGGESTION = 'RESET_FOCUSED_SUGGESTION';
const REVEAL_SUGGESTIONS = 'REVEAL_SUGGESTIONS';
const CLOSE_SUGGESTIONS = 'CLOSE_SUGGESTIONS';

const inputFocused = shouldRenderSuggestions => ({
  type: INPUT_FOCUSED,
  shouldRenderSuggestions
});

const inputBlurred = shouldRenderSuggestions => ({
  type: INPUT_BLURRED,
  shouldRenderSuggestions
});

const inputChanged = shouldRenderSuggestions => ({
  type: INPUT_CHANGED,
  shouldRenderSuggestions
});

const updateFocusedSuggestion = (sectionIndex, suggestionIndex, prevValue) => ({
  type: UPDATE_FOCUSED_SUGGESTION,
  sectionIndex,
  suggestionIndex,
  prevValue
});

const resetFocusedSuggestion = (shouldResetValueBeforeUpDown = true) => ({
  type: RESET_FOCUSED_SUGGESTION,
  shouldResetValueBeforeUpDown
});

const revealSuggestions = () => ({
  type: REVEAL_SUGGESTIONS
});

const closeSuggestions = () => ({
  type: CLOSE_SUGGESTIONS
});

export const actionCreators = {
  inputFocused,
  inputBlurred,
  inputChanged,
  updateFocusedSuggestion,
  resetFocusedSuggestion,
  revealSuggestions,
  closeSuggestions
};

const reducer = (state, action) => {
  switch (action.type) {
    case INPUT_FOCUSED:
      return {
        ...state,
        isFocused: true,
        isCollapsed: !action.shouldRenderSuggestions
      };

    case INPUT_BLURRED:
      return {
        ...state,
        isFocused: false,
        focusedSectionIndex: null,
        focusedSuggestionIndex: null,
        valueBeforeUpDown: null,
        isCollapsed: !action.shouldRenderSuggestions
      };

    case INPUT_CHANGED:
      return {
        ...state,
        focusedSectionIndex: null,
        focusedSuggestionIndex: null,
        valueBeforeUpDown: null,
        isCollapsed: !action.shouldRenderSuggestions
      };

    case UPDATE_FOCUSED_SUGGESTION: {
      const { sectionIndex, suggestionIndex, prevValue } = action;
      let { valueBeforeUpDown } = state;

      if (suggestionIndex === null) {
        valueBeforeUpDown = null;
      } else if (valueBeforeUpDown === null && typeof prevValue !== 'undefined') {
        valueBeforeUpDown = prevValue;
      }

      return {
        ...state,
        focusedSectionIndex: sectionIndex,
        focusedSuggestionIndex: suggestionIndex,
        valueBeforeUpDown
      };
    }

    case RESET_FOCUSED_SUGGESTION: {
      return {
        ...state,
        focusedSectionIndex: null,
        focusedSuggestionIndex: null,
        valueBeforeUpDown: action.shouldResetValueBeforeUpDown ? null : state.valueBeforeUpDown
      };
    }

    case REVEAL_SUGGESTIONS:
      return {
        ...state,
        isCollapsed: false
      };

    case CLOSE_SUGGESTIONS:
      return {
        ...state,
        focusedSectionIndex: null,
        focusedSuggestionIndex: null,
        valueBeforeUpDown: null,
        isCollapsed: true
      };

    default:
      return state;
  }
};

export default reducer;
<|endoftext|>


<commit_before>import {
  fromGlobalId,
  connectionFromArray,
  nodeDefinitions,
  connectionDefinitions,
  connectionArgs
} from 'graphql-relay';

import {
  GraphQLList
} from 'graphql';

import {
  base64,
  unbase64,
} from './base64.js';

import _ from 'lodash';
import simplifyAST from './simplifyAST';

import {Model} from 'sequelize';

function getModelOfInstance(instance) {
  return instance instanceof Model ? instance.constructor : instance.Model;
}

export class NodeTypeMapper {
  constructor() {
    this.map = { };
  }

  mapTypes(types) {
    Object.keys(types).forEach((k) => {
      let v = types[k];
      this.map[k] = v.type
        ? v
        : { type: v };
    });
  }

  item(type) {
    return this.map[type];
  }
}

export function idFetcher(sequelize, nodeTypeMapper) {
  return async (globalId, context, info) => {
    const {type, id} = fromGlobalId(globalId);

    const nodeType = nodeTypeMapper.item(type);
    if (nodeType && typeof nodeType.resolve === 'function') {
      const res = await Promise.resolve(nodeType.resolve(globalId, context, info));
      if (res) res.__graphqlType__ = type;
      return res;
    }

    const model = Object.keys(sequelize.models).find(model => model === type);
    if (model) {
      return sequelize.models[model].findById(id);
    }

    if (nodeType) {
      return typeof nodeType.type === 'string' ? info.schema.getType(nodeType.type) : nodeType.type;
    }

    return null;
  };
}

export function typeResolver(nodeTypeMapper) {
  return (obj, context, info) => {
    var type = obj.__graphqlType__
               || (obj.Model
                 ? obj.Model.options.name.singular
                 : obj._modelOptions
                 ? obj._modelOptions.name.singular
                 : obj.name);

    if (!type) {
      throw new Error(`Unable to determine type of ${ typeof obj }. ` +
        `Either specify a resolve function in 'NodeTypeMapper' object, or specify '__graphqlType__' property on object.`);
    }

    const nodeType = nodeTypeMapper.item(type);
    if (nodeType) {
      return typeof nodeType.type === 'string' ? info.schema.getType(nodeType.type) : nodeType.type;
    }

    return null;
  };
}

export function isConnection(type) {
  return typeof type.name !== 'undefined' && type.name.endsWith('Connection');
}

export function handleConnection(values, args) {
  return connectionFromArray(values, args);
}

export function createNodeInterface(sequelize) {
  let nodeTypeMapper = new NodeTypeMapper();
  const nodeObjects = nodeDefinitions(
    idFetcher(sequelize, nodeTypeMapper),
    typeResolver(nodeTypeMapper)
  );

  return {
    nodeTypeMapper,
    ...nodeObjects
  };
}

export {createNodeInterface as sequelizeNodeInterface};

export function nodeType(connectionType) {
  return connectionType._fields.edges.type.ofType._fields.node.type;
}

export function createConnectionResolver({
  target: targetMaybeThunk,
  before,
  after,
  where,
  orderBy: orderByEnum,
  ignoreArgs
}) {
  before = before || ((options) => options);
  after = after || ((result) => result);

  let orderByAttribute = function (orderAttr, {source, args, context, info}) {
    return typeof orderAttr === 'function' ? orderAttr(source, args, context, info) : orderAttr;
  };

  let orderByDirection = function (orderDirection, args) {
    if (args.last) {
      return orderDirection.indexOf('ASC') >= 0
              ? orderDirection.replace('ASC', 'DESC')
              : orderDirection.replace('DESC', 'ASC');
    }
    return orderDirection;
  };

  /**
   * Creates a cursor given a item returned from the Database
   * @param  {Object}   item   sequelize row
   * @param  {Integer}  index  the index of this item within the results, 0 indexed
   * @return {String}          The Base64 encoded cursor string
   */
  let toCursor = function (item, index) {
    const model = getModelOfInstance(item);
    const id = model ?
               typeof model.primaryKeyAttribute === 'string' ? item[model.primaryKeyAttribute] : null :
               item[Object.keys(item)[0]];
    return base64(JSON.stringify([id, index]));
  };

  /**
   * Decode a cursor into its component parts
   * @param  {String} cursor Base64 encoded cursor
   * @return {Object}        Object containing ID and index
   */
  let fromCursor = function (cursor) {
    let [id, index] = JSON.parse(unbase64(cursor));

    return {
      id,
      index
    };
  };

  let argsToWhere = function (args) {
    let result = {};

    if (where === undefined) return result;

    _.each(args, (value, key) => {
      if (ignoreArgs && key in ignoreArgs) return;
      Object.assign(result, where(key, value, result));
    });

    return result;
  };

  let resolveEdge = function (item, index, queriedCursor, sourceArgs = {}, source) {
    let startIndex = null;
    if (queriedCursor) startIndex = Number(queriedCursor.index);
    if (startIndex !== null) {
      startIndex++;
    } else {
      startIndex = 0;
    }

    return {
      cursor: toCursor(item, index + startIndex),
      node: item,
      source: source,
      sourceArgs
    };
  };

  let $resolver = require('./resolver')(targetMaybeThunk, {
    handleConnection: false,
    list: true,
    before: function (options, args, context, info) {
      const target = info.target;
      const model = target.target ? target.target : target;

      if (args.first || args.last) {
        options.limit = parseInt(args.first || args.last, 10);
      }

      // Grab enum type by name if it's a string
      orderByEnum = typeof orderByEnum === 'string' ? info.schema.getType(orderByEnum) : orderByEnum;

      let orderBy = args.orderBy ? args.orderBy :
                    orderByEnum ? [orderByEnum._values[0].value] :
                    [[model.primaryKeyAttribute, 'ASC']];

      if (orderByEnum && typeof orderBy === 'string') {
        orderBy = [orderByEnum._nameLookup[args.orderBy].value];
      }

      let orderAttribute = orderByAttribute(orderBy[0][0], {
        source: info.source,
        args,
        context,
        info
      });
      let orderDirection = orderByDirection(orderBy[0][1], args);

      options.order = [
        [orderAttribute, orderDirection]
      ];

      if (orderAttribute !== model.primaryKeyAttribute) {
        options.order.push([model.primaryKeyAttribute, orderByDirection('ASC', args)]);
      }

      if (typeof orderAttribute === 'string') {
        options.attributes.push(orderAttribute);
      }

      if (options.limit && !options.attributes.some(attribute => attribute.length === 2 && attribute[1] === 'full_count')) {
        if (model.sequelize.dialect.name === 'postgres') {
          options.attributes.push([
            model.sequelize.literal('COUNT(*) OVER()'),
            'full_count'
          ]);
        } else if (model.sequelize.dialect.name === 'mssql') {
          options.attributes.push([
            model.sequelize.literal('COUNT(1) OVER()'),
            'full_count'
          ]);
        }
      }

      options.where = argsToWhere(args);

      if (args.after || args.before) {
        let cursor = fromCursor(args.after || args.before);
        let startIndex = Number(cursor.index);

        if (startIndex >= 0) options.offset = startIndex + 1;
      }

      options.attributes.unshift(model.primaryKeyAttribute); // Ensure the primary key is always the first selected attribute
      options.attributes = _.uniq(options.attributes);
      return before(options, args, context, info);
    },
    after: async function (values, args, context, info) {
      const {
        source,
        target
      } = info;

      var cursor = null;

      if (args.after || args.before) {
        cursor = fromCursor(args.after || args.before);
      }

      let edges = values.map((value, idx) => {
        return resolveEdge(value, idx, cursor, args, source);
      });

      let firstEdge = edges[0];
      let lastEdge = edges[edges.length - 1];
      let fullCount = values[0] &&
                      (values[0].dataValues || values[0]).full_count &&
                      parseInt((values[0].dataValues || values[0]).full_count, 10);

      if (!values[0]) {
        fullCount = 0;
      }

      if ((args.first || args.last) && (fullCount === null || fullCount === undefined)) {
        // In case of `OVER()` is not available, we need to get the full count from a second query.
        const options = await Promise.resolve(before({
          where: argsToWhere(args)
        }, args, context, info));

        if (target.count) {
          if (target.associationType) {
            fullCount = await target.count(source, options);
          } else {
            fullCount = await target.count(options);
          }
        } else {
          fullCount = await target.manyFromSource.count(source, options);
        }
      }

      let hasNextPage = false;
      let hasPreviousPage = false;
      if (args.first || args.last) {
        const count = parseInt(args.first || args.last, 10);
        let index = cursor ? Number(cursor.index) : null;
        if (index !== null) {
          index++;
        } else {
          index = 0;
        }

        hasNextPage = index + 1 + count <= fullCount;
        hasPreviousPage = index - count >= 0;

        if (args.last) {
          [hasNextPage, hasPreviousPage] = [hasPreviousPage, hasNextPage];
        }
      }

      return after({
        source,
        args,
        where: argsToWhere(args),
        edges,
        pageInfo: {
          startCursor: firstEdge ? firstEdge.cursor : null,
          endCursor: lastEdge ? lastEdge.cursor : null,
          hasNextPage: hasNextPage,
          hasPreviousPage: hasPreviousPage
        },
        fullCount
      }, args, context, info);
    }
  });

  let resolveConnection = (source, args, context, info) => {
    var fieldNodes = info.fieldASTs || info.fieldNodes;
    if (simplifyAST(fieldNodes[0], info).fields.edges) {
      return $resolver(source, args, context, info);
    }

    return after({
      source,
      args,
      where: argsToWhere(args)
    }, args, context, info);
  };

  return {
    resolveEdge,
    resolveConnection
  };
}

export function createConnection({
  name,
  nodeType,
  target: targetMaybeThunk,
  orderBy: orderByEnum,
  before,
  after,
  connectionFields,
  edgeFields,
  where
}) {
  const {
    edgeType,
    connectionType
  } = connectionDefinitions({
    name,
    nodeType,
    connectionFields,
    edgeFields
  });

  let $connectionArgs = {
    ...connectionArgs
  };

  if (orderByEnum) {
    $connectionArgs.orderBy = {
      type: new GraphQLList(orderByEnum)
    };
  }

  const {
    resolveEdge,
    resolveConnection
  } = createConnectionResolver({
    orderBy: orderByEnum,
    target: targetMaybeThunk,
    before,
    after,
    where,
    ignoreArgs: $connectionArgs
  });

  return {
    connectionType,
    edgeType,
    nodeType,
    resolveEdge,
    resolveConnection,
    connectionArgs: $connectionArgs,
    resolve: resolveConnection
  };
}

export {createConnection as sequelizeConnection};
<commit_msg>adding sql lite for the counts (#657)<commit_after>import {
  fromGlobalId,
  connectionFromArray,
  nodeDefinitions,
  connectionDefinitions,
  connectionArgs
} from 'graphql-relay';

import {
  GraphQLList
} from 'graphql';

import {
  base64,
  unbase64,
} from './base64.js';

import _ from 'lodash';
import simplifyAST from './simplifyAST';

import {Model} from 'sequelize';

function getModelOfInstance(instance) {
  return instance instanceof Model ? instance.constructor : instance.Model;
}

export class NodeTypeMapper {
  constructor() {
    this.map = { };
  }

  mapTypes(types) {
    Object.keys(types).forEach((k) => {
      let v = types[k];
      this.map[k] = v.type
        ? v
        : { type: v };
    });
  }

  item(type) {
    return this.map[type];
  }
}

export function idFetcher(sequelize, nodeTypeMapper) {
  return async (globalId, context, info) => {
    const {type, id} = fromGlobalId(globalId);

    const nodeType = nodeTypeMapper.item(type);
    if (nodeType && typeof nodeType.resolve === 'function') {
      const res = await Promise.resolve(nodeType.resolve(globalId, context, info));
      if (res) res.__graphqlType__ = type;
      return res;
    }

    const model = Object.keys(sequelize.models).find(model => model === type);
    if (model) {
      return sequelize.models[model].findById(id);
    }

    if (nodeType) {
      return typeof nodeType.type === 'string' ? info.schema.getType(nodeType.type) : nodeType.type;
    }

    return null;
  };
}

export function typeResolver(nodeTypeMapper) {
  return (obj, context, info) => {
    var type = obj.__graphqlType__
               || (obj.Model
                 ? obj.Model.options.name.singular
                 : obj._modelOptions
                 ? obj._modelOptions.name.singular
                 : obj.name);

    if (!type) {
      throw new Error(`Unable to determine type of ${ typeof obj }. ` +
        `Either specify a resolve function in 'NodeTypeMapper' object, or specify '__graphqlType__' property on object.`);
    }

    const nodeType = nodeTypeMapper.item(type);
    if (nodeType) {
      return typeof nodeType.type === 'string' ? info.schema.getType(nodeType.type) : nodeType.type;
    }

    return null;
  };
}

export function isConnection(type) {
  return typeof type.name !== 'undefined' && type.name.endsWith('Connection');
}

export function handleConnection(values, args) {
  return connectionFromArray(values, args);
}

export function createNodeInterface(sequelize) {
  let nodeTypeMapper = new NodeTypeMapper();
  const nodeObjects = nodeDefinitions(
    idFetcher(sequelize, nodeTypeMapper),
    typeResolver(nodeTypeMapper)
  );

  return {
    nodeTypeMapper,
    ...nodeObjects
  };
}

export {createNodeInterface as sequelizeNodeInterface};

export function nodeType(connectionType) {
  return connectionType._fields.edges.type.ofType._fields.node.type;
}

export function createConnectionResolver({
  target: targetMaybeThunk,
  before,
  after,
  where,
  orderBy: orderByEnum,
  ignoreArgs
}) {
  before = before || ((options) => options);
  after = after || ((result) => result);

  let orderByAttribute = function (orderAttr, {source, args, context, info}) {
    return typeof orderAttr === 'function' ? orderAttr(source, args, context, info) : orderAttr;
  };

  let orderByDirection = function (orderDirection, args) {
    if (args.last) {
      return orderDirection.indexOf('ASC') >= 0
              ? orderDirection.replace('ASC', 'DESC')
              : orderDirection.replace('DESC', 'ASC');
    }
    return orderDirection;
  };

  /**
   * Creates a cursor given a item returned from the Database
   * @param  {Object}   item   sequelize row
   * @param  {Integer}  index  the index of this item within the results, 0 indexed
   * @return {String}          The Base64 encoded cursor string
   */
  let toCursor = function (item, index) {
    const model = getModelOfInstance(item);
    const id = model ?
               typeof model.primaryKeyAttribute === 'string' ? item[model.primaryKeyAttribute] : null :
               item[Object.keys(item)[0]];
    return base64(JSON.stringify([id, index]));
  };

  /**
   * Decode a cursor into its component parts
   * @param  {String} cursor Base64 encoded cursor
   * @return {Object}        Object containing ID and index
   */
  let fromCursor = function (cursor) {
    let [id, index] = JSON.parse(unbase64(cursor));

    return {
      id,
      index
    };
  };

  let argsToWhere = function (args) {
    let result = {};

    if (where === undefined) return result;

    _.each(args, (value, key) => {
      if (ignoreArgs && key in ignoreArgs) return;
      Object.assign(result, where(key, value, result));
    });

    return result;
  };

  let resolveEdge = function (item, index, queriedCursor, sourceArgs = {}, source) {
    let startIndex = null;
    if (queriedCursor) startIndex = Number(queriedCursor.index);
    if (startIndex !== null) {
      startIndex++;
    } else {
      startIndex = 0;
    }

    return {
      cursor: toCursor(item, index + startIndex),
      node: item,
      source: source,
      sourceArgs
    };
  };

  let $resolver = require('./resolver')(targetMaybeThunk, {
    handleConnection: false,
    list: true,
    before: function (options, args, context, info) {
      const target = info.target;
      const model = target.target ? target.target : target;

      if (args.first || args.last) {
        options.limit = parseInt(args.first || args.last, 10);
      }

      // Grab enum type by name if it's a string
      orderByEnum = typeof orderByEnum === 'string' ? info.schema.getType(orderByEnum) : orderByEnum;

      let orderBy = args.orderBy ? args.orderBy :
                    orderByEnum ? [orderByEnum._values[0].value] :
                    [[model.primaryKeyAttribute, 'ASC']];

      if (orderByEnum && typeof orderBy === 'string') {
        orderBy = [orderByEnum._nameLookup[args.orderBy].value];
      }

      let orderAttribute = orderByAttribute(orderBy[0][0], {
        source: info.source,
        args,
        context,
        info
      });
      let orderDirection = orderByDirection(orderBy[0][1], args);

      options.order = [
        [orderAttribute, orderDirection]
      ];

      if (orderAttribute !== model.primaryKeyAttribute) {
        options.order.push([model.primaryKeyAttribute, orderByDirection('ASC', args)]);
      }

      if (typeof orderAttribute === 'string') {
        options.attributes.push(orderAttribute);
      }

      if (options.limit && !options.attributes.some(attribute => attribute.length === 2 && attribute[1] === 'full_count')) {
        if (model.sequelize.dialect.name === 'postgres') {
          options.attributes.push([
            model.sequelize.literal('COUNT(*) OVER()'),
            'full_count'
          ]);
        } else if (model.sequelize.dialect.name === 'mssql' || model.sequelize.dialect.name === 'sqlite') {
          options.attributes.push([
            model.sequelize.literal('COUNT(1) OVER()'),
            'full_count'
          ]);
        }
      }

      options.where = argsToWhere(args);

      if (args.after || args.before) {
        let cursor = fromCursor(args.after || args.before);
        let startIndex = Number(cursor.index);

        if (startIndex >= 0) options.offset = startIndex + 1;
      }

      options.attributes.unshift(model.primaryKeyAttribute); // Ensure the primary key is always the first selected attribute
      options.attributes = _.uniq(options.attributes);
      return before(options, args, context, info);
    },
    after: async function (values, args, context, info) {
      const {
        source,
        target
      } = info;

      var cursor = null;

      if (args.after || args.before) {
        cursor = fromCursor(args.after || args.before);
      }

      let edges = values.map((value, idx) => {
        return resolveEdge(value, idx, cursor, args, source);
      });

      let firstEdge = edges[0];
      let lastEdge = edges[edges.length - 1];
      let fullCount = values[0] &&
                      (values[0].dataValues || values[0]).full_count &&
                      parseInt((values[0].dataValues || values[0]).full_count, 10);

      if (!values[0]) {
        fullCount = 0;
      }

      if ((args.first || args.last) && (fullCount === null || fullCount === undefined)) {
        // In case of `OVER()` is not available, we need to get the full count from a second query.
        const options = await Promise.resolve(before({
          where: argsToWhere(args)
        }, args, context, info));

        if (target.count) {
          if (target.associationType) {
            fullCount = await target.count(source, options);
          } else {
            fullCount = await target.count(options);
          }
        } else {
          fullCount = await target.manyFromSource.count(source, options);
        }
      }

      let hasNextPage = false;
      let hasPreviousPage = false;
      if (args.first || args.last) {
        const count = parseInt(args.first || args.last, 10);
        let index = cursor ? Number(cursor.index) : null;
        if (index !== null) {
          index++;
        } else {
          index = 0;
        }

        hasNextPage = index + 1 + count <= fullCount;
        hasPreviousPage = index - count >= 0;

        if (args.last) {
          [hasNextPage, hasPreviousPage] = [hasPreviousPage, hasNextPage];
        }
      }

      return after({
        source,
        args,
        where: argsToWhere(args),
        edges,
        pageInfo: {
          startCursor: firstEdge ? firstEdge.cursor : null,
          endCursor: lastEdge ? lastEdge.cursor : null,
          hasNextPage: hasNextPage,
          hasPreviousPage: hasPreviousPage
        },
        fullCount
      }, args, context, info);
    }
  });

  let resolveConnection = (source, args, context, info) => {
    var fieldNodes = info.fieldASTs || info.fieldNodes;
    if (simplifyAST(fieldNodes[0], info).fields.edges) {
      return $resolver(source, args, context, info);
    }

    return after({
      source,
      args,
      where: argsToWhere(args)
    }, args, context, info);
  };

  return {
    resolveEdge,
    resolveConnection
  };
}

export function createConnection({
  name,
  nodeType,
  target: targetMaybeThunk,
  orderBy: orderByEnum,
  before,
  after,
  connectionFields,
  edgeFields,
  where
}) {
  const {
    edgeType,
    connectionType
  } = connectionDefinitions({
    name,
    nodeType,
    connectionFields,
    edgeFields
  });

  let $connectionArgs = {
    ...connectionArgs
  };

  if (orderByEnum) {
    $connectionArgs.orderBy = {
      type: new GraphQLList(orderByEnum)
    };
  }

  const {
    resolveEdge,
    resolveConnection
  } = createConnectionResolver({
    orderBy: orderByEnum,
    target: targetMaybeThunk,
    before,
    after,
    where,
    ignoreArgs: $connectionArgs
  });

  return {
    connectionType,
    edgeType,
    nodeType,
    resolveEdge,
    resolveConnection,
    connectionArgs: $connectionArgs,
    resolve: resolveConnection
  };
}

export {createConnection as sequelizeConnection};
<|endoftext|>


<commit_before>import _ from 'underscore'
import Radio from 'backbone.radio'
import Marionette from 'backbone.marionette'
import RouteContext from './routecontext'
import {getMnRoutes, routerChannel} from './cherrytree-adapter'

export default Marionette.Object.extend(
  {
    constructor (options) {
      this.mergeOptions(options, ['viewClass', 'viewOptions'])
      Marionette.Object.call(this, options);
      this._bindContext()
    },

    activate() {

    },

    deactivate() {

    },

    renderView(region, transition) {
      //todo: move renderView out of Route class??
      if (!this.viewClass) {
        throw new Error('render: viewClass not defined')
      }
      if (this.view && this.updateView(transition)) return ;
      this.view = new this.viewClass(_.result(this, 'viewOptions', {}))
      this.listenToOnce(this.view, 'destroy', function () {
        this.view = void 0
      })
      region.show(this.view)
      routerChannel.trigger('route:render', this)
      if (this.viewEvents) {
        Marionette.bindEvents(this, this.view, this.viewEvents)
      }
    },

    updateView() {

    },

    getContext() {
      //todo: cache context??
      let state = this.$router.state
      let mnRoutes = (state.activeTransition || state).mnRoutes
      if (!mnRoutes) {
        mnRoutes = getMnRoutes(state.routes)
      }
      return new RouteContext(mnRoutes, this)
    },

    _bindContext() {
      let channel, requests = _.result(this, 'contextRequests'),
        events = _.result(this, 'contextEvents')
      if (!requests && !events) {
        return
      }

      this._contextChannel = channel = new Radio.Channel('__routeContext_' + this.cid)

      this.bindRequests(channel, requests)
      this.bindEvents(channel, events)
    },

    $router: null
  }
)
<commit_msg>Define cidPrefix in Route class<commit_after>import _ from 'underscore'
import Radio from 'backbone.radio'
import Marionette from 'backbone.marionette'
import RouteContext from './routecontext'
import {getMnRoutes, routerChannel} from './cherrytree-adapter'

export default Marionette.Object.extend(
  {
    constructor (options) {
      this.mergeOptions(options, ['viewClass', 'viewOptions'])
      Marionette.Object.call(this, options);
      this._bindContext()
    },

    cidPrefix: 'rou',

    activate() {

    },

    deactivate() {

    },

    renderView(region, transition) {
      //todo: move renderView out of Route class??
      if (!this.viewClass) {
        throw new Error('render: viewClass not defined')
      }
      if (this.view && this.updateView(transition)) return ;
      this.view = new this.viewClass(_.result(this, 'viewOptions', {}))
      this.listenToOnce(this.view, 'destroy', function () {
        this.view = void 0
      })
      region.show(this.view)
      routerChannel.trigger('route:render', this)
      if (this.viewEvents) {
        Marionette.bindEvents(this, this.view, this.viewEvents)
      }
    },

    updateView() {

    },

    getContext() {
      //todo: cache context??
      let state = this.$router.state
      let mnRoutes = (state.activeTransition || state).mnRoutes
      if (!mnRoutes) {
        mnRoutes = getMnRoutes(state.routes)
      }
      return new RouteContext(mnRoutes, this)
    },

    _bindContext() {
      let channel, requests = _.result(this, 'contextRequests'),
        events = _.result(this, 'contextEvents')
      if (!requests && !events) {
        return
      }

      this._contextChannel = channel = new Radio.Channel('__routeContext_' + this.cid)

      this.bindRequests(channel, requests)
      this.bindEvents(channel, events)
    },

    $router: null
  }
)
<|endoftext|>


<commit_before>import { dispatch, getState } from "./Store";
import Actions from "./Actions";
import parseUrl from "./util/parseUrl";
import V        from "./constants/PanelView";
import mapConst from "./constants/Map";
import RoutingUsecases from "./constants/RoutingUsecases";

export default (event) => {
  console.log(`URL CHANGE FROM BROWSER: "${getState().url.hash}" --> "${window.location.hash}"`);
  dispatch(Actions.updateStateFromURL(window.location.hash));
  const actions = createActionsFromState(getState());
  for (let a of actions) {
    dispatch(a);
  }
};

const createActionsFromState = (state) => {
  const { server, map, url } = state;
  const { entries } = server;
  const { hash, routingUsecases } = url;
  const { params } = parseUrl(hash);
  const { entry, zoom, center, search, tags, view, confirmEmail, left} = params;
  let [lat, lng] = center ? center.split(',') : [null, null];
  const zoomValue = Number(zoom)

  const actions = [];

  for (let usecase of routingUsecases){
    switch(usecase){
      case RoutingUsecases.CHANGE_SIDEBAR_VISIBILITY:
        console.log("route: left visibility:", left);
        if (left && left == "hide"){
          actions.push(Actions.hideLeftPanel());
        } else {
          actions.push(Actions.showLeftPanel());
        }
        break;
      case RoutingUsecases.NO_ROUTING:
        console.log("route: nothing");
        if(getState().view.left != V.SUBSCRIBE_TO_BBOX){
          actions.push(Actions.showSearchResults());
        }
        break;
      case RoutingUsecases.SHOW_ENTRY:
        console.log("route: entry");
        actions.push(Actions.showMap());
        actions.push(Actions.getEntries([entry]));
        if(entries[entry] != null){
          actions.push(Actions.setCurrentEntry(
            entry, {lat: entries[entry].lat, lng: entries[entry].lng}));
        } else {
          actions.push(Actions.setCurrentEntry(entry, null));
          actions.push(Actions.setCenter(entry));
        }
        if(!zoom) {
          actions.push(Actions.setZoom(mapConst.ENTRY_DEFAULT_ZOOM));
        }
        break;
      case RoutingUsecases.CHANGE_CENTER:
        lat = parseFloat(lat);
        lng = parseFloat(lng);

        if (!(isNaN(lat) || isNaN(lng))
          && ((lat.toFixed(4) != map.center.lat.toFixed(4))
          || (lng.toFixed(4) != map.center.lng.toFixed(4)))) {
          console.log("route center: ", lat, lng);
          actions.push(Actions.showSearchResults());
          actions.push(Actions.setCenter({lat, lng}));
        }
        break;
      case RoutingUsecases.CHANGE_ZOOM:
        if(!isNaN(zoomValue) && zoomValue != map.zoom){
          console.log("route: zoom", zoomValue, map.zoom);
          actions.push(Actions.setZoom(zoomValue));
        }
        break;
      case RoutingUsecases.CHANGE_SEARCH:
        console.log(`route: search=${search}, tags=${tags}`);
        actions.push(Actions.search());
        break;
      case RoutingUsecases.CONFIRM_EMAIL:
        console.log("route: confirmEmail");
        actions.push(Actions.confirmEmail(confirmEmail));
        break;
    }
  }

  return actions;
};
<commit_msg>fix(Accounts): fix bug with confirmEmail routing<commit_after>import { dispatch, getState } from "./Store";
import Actions from "./Actions";
import parseUrl from "./util/parseUrl";
import V        from "./constants/PanelView";
import mapConst from "./constants/Map";
import RoutingUsecases from "./constants/RoutingUsecases";

export default (event) => {
  console.log(`URL CHANGE FROM BROWSER: "${getState().url.hash}" --> "${window.location.hash}"`);
  dispatch(Actions.updateStateFromURL(window.location.hash));
  const actions = createActionsFromState(getState());
  for (let a of actions) {
    dispatch(a);
  }
};

const createActionsFromState = (state) => {
  const { server, map, url } = state;
  const { entries } = server;
  const { hash, routingUsecases } = url;
  const { params } = parseUrl(hash);
  const { entry, zoom, center, search, tags, view, left} = params;
  const confirmEmail = params.confirm_email;
  let [lat, lng] = center ? center.split(',') : [null, null];
  const zoomValue = Number(zoom)

  const actions = [];

  for (let usecase of routingUsecases){
    switch(usecase){
      case RoutingUsecases.CHANGE_SIDEBAR_VISIBILITY:
        console.log("route: left visibility:", left);
        if (left && left == "hide"){
          actions.push(Actions.hideLeftPanel());
        } else {
          actions.push(Actions.showLeftPanel());
        }
        break;
      case RoutingUsecases.NO_ROUTING:
        console.log("route: nothing");
        if(getState().view.left != V.SUBSCRIBE_TO_BBOX){
          actions.push(Actions.showSearchResults());
        }
        break;
      case RoutingUsecases.SHOW_ENTRY:
        console.log("route: entry");
        actions.push(Actions.showMap());
        actions.push(Actions.getEntries([entry]));
        if(entries[entry] != null){
          actions.push(Actions.setCurrentEntry(
            entry, {lat: entries[entry].lat, lng: entries[entry].lng}));
        } else {
          actions.push(Actions.setCurrentEntry(entry, null));
          actions.push(Actions.setCenter(entry));
        }
        if(!zoom) {
          actions.push(Actions.setZoom(mapConst.ENTRY_DEFAULT_ZOOM));
        }
        break;
      case RoutingUsecases.CHANGE_CENTER:
        lat = parseFloat(lat);
        lng = parseFloat(lng);

        if (!(isNaN(lat) || isNaN(lng))
          && ((lat.toFixed(4) != map.center.lat.toFixed(4))
          || (lng.toFixed(4) != map.center.lng.toFixed(4)))) {
          console.log("route center: ", lat, lng);
          actions.push(Actions.showSearchResults());
          actions.push(Actions.setCenter({lat, lng}));
        }
        break;
      case RoutingUsecases.CHANGE_ZOOM:
        if(!isNaN(zoomValue) && zoomValue != map.zoom){
          console.log("route: zoom", zoomValue, map.zoom);
          actions.push(Actions.setZoom(zoomValue));
        }
        break;
      case RoutingUsecases.CHANGE_SEARCH:
        console.log(`route: search=${search}, tags=${tags}`);
        actions.push(Actions.search());
        break;
      case RoutingUsecases.CONFIRM_EMAIL:
        console.log("route: confirmEmail");
        actions.push(Actions.confirmEmail(confirmEmail));
        break;
    }
  }

  return actions;
};
<|endoftext|>


<commit_before>import _ from 'underscore'
import Radio from 'backbone.radio'
import Marionette from 'backbone.marionette'
import RouteContext from './routecontext'
import {getMnRoutes, routerChannel} from './cherrytree-adapter'

export default Marionette.Object.extend(
  {
    constructor (options) {
      this.mergeOptions(options, ['viewClass', 'viewOptions'])
      Marionette.Object.call(this, options);
      this._bindContext()
    },
    activate() {

    },

    deactivate() {

    },

    renderView(region) {
      //todo: move renderView out of Route class??
      if (!this.viewClass) {
        throw new Error('render: viewClass not defined')
      }
      this.view = new this.viewClass(_.result(this, 'viewOptions', {}))
      region.show(this.view)
      routerChannel.trigger('route:render', this)
      if (this.viewEvents) {
        Marionette.bindEvents(this, this.view, this.viewEvents)
      }
    },

    getContext() {
      //todo: cache context??
      let state = this.$router.state
      let mnRoutes = (state.activeTransition || state).mnRoutes
      if (!mnRoutes) {
        mnRoutes = getMnRoutes(state.routes)
      }
      return new RouteContext(mnRoutes, this)
    },

    _bindContext() {
      let channel, requests = _.result(this, 'contextRequests'),
        events = _.result(this, 'contextEvents')
      if (!requests && !events) {
        return
      }

      this._contextChannel = channel = new Radio.Channel('__routeContext_' + this.cid)

      this.bindRequests(channel, requests)
      this.bindEvents(channel, events)
    }
  }
)
<commit_msg>Initialize Route.$router property at class definition<commit_after>import _ from 'underscore'
import Radio from 'backbone.radio'
import Marionette from 'backbone.marionette'
import RouteContext from './routecontext'
import {getMnRoutes, routerChannel} from './cherrytree-adapter'

export default Marionette.Object.extend(
  {
    constructor (options) {
      this.mergeOptions(options, ['viewClass', 'viewOptions'])
      Marionette.Object.call(this, options);
      this._bindContext()
    },

    activate() {

    },

    deactivate() {

    },

    renderView(region) {
      //todo: move renderView out of Route class??
      if (!this.viewClass) {
        throw new Error('render: viewClass not defined')
      }
      this.view = new this.viewClass(_.result(this, 'viewOptions', {}))
      region.show(this.view)
      routerChannel.trigger('route:render', this)
      if (this.viewEvents) {
        Marionette.bindEvents(this, this.view, this.viewEvents)
      }
    },

    getContext() {
      //todo: cache context??
      let state = this.$router.state
      let mnRoutes = (state.activeTransition || state).mnRoutes
      if (!mnRoutes) {
        mnRoutes = getMnRoutes(state.routes)
      }
      return new RouteContext(mnRoutes, this)
    },

    _bindContext() {
      let channel, requests = _.result(this, 'contextRequests'),
        events = _.result(this, 'contextEvents')
      if (!requests && !events) {
        return
      }

      this._contextChannel = channel = new Radio.Channel('__routeContext_' + this.cid)

      this.bindRequests(channel, requests)
      this.bindEvents(channel, events)
    },

    $router: null
  }
)
<|endoftext|>


<commit_before>/* ============================================================
 * node.bittrex.api
 * https://github.com/n0mad01/node.bittrex.api
 *
 * ============================================================
 * Copyright 2014-2015, Adrian Soluch - http://soluch.us/
 * Released under the MIT License
 * ============================================================ */
var NodeBittrexApi = function() {

    'use strict';

    var request = require('request'),
        hmac_sha512 = require('./hmac-sha512.js'),
        JSONStream = require('JSONStream'),
        es = require('event-stream');

    var start,
        request_options = {
            method: 'GET',
            agent: false,
            headers: {
                "User-Agent": "Mozilla/4.0 (compatible; Node Bittrex API)",
                "Content-type": "application/x-www-form-urlencoded"
            }
        };

    var opts = {
        baseUrl: 'https://bittrex.com/api/v1.1',
        apikey: 'APIKEY',
        apisecret: 'APISECRET',
        verbose: false,
        cleartext: false,
        stream: false
    };

    var getNonce = function() {
        return Math.floor(new Date().getTime() / 1000);
    };

    var extractOptions = function(options) {

        var o = Object.keys(options),
            i;
        for (i = 0; i < o.length; i++) {
            opts[o[i]] = options[o[i]];
        }
    };

    var apiCredentials = function(uri) {

        var options = {
            apikey: opts.apikey,
            nonce: getNonce()
        };

        return setRequestUriGetParams(uri, options);
    };

    var setRequestUriGetParams = function(uri, options) {
        var op;
        if (typeof(uri) === 'object') {
            op = uri;
            uri = op.uri;
        } else {
            op = request_options;
        }

        var o = Object.keys(options),
            i;
        for (i = 0; i < o.length; i++) {
            uri = updateQueryStringParameter(uri, o[i], options[o[i]]);
        }

        op.headers.apisign = hmac_sha512.HmacSHA512(uri, opts.apisecret); // setting the HMAC hash `apisign` http header
        op.uri = uri;

        return op;
    };

    var updateQueryStringParameter = function(uri, key, value) {

        var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
        var separator = uri.indexOf('?') !== -1 ? "&" : "?";

        if (uri.match(re)) {
            uri = uri.replace(re, '$1' + key + "=" + value + '$2');
        } else {
            uri = uri + separator + key + "=" + value;
        }

        return uri;
    };

    var sendRequestCallback = function(callback, op) {
        start = Date.now();

        switch (opts.stream) {

            case true:
                request(op)
                    .pipe(JSONStream.parse())
                    .pipe(es.mapSync(function(data) {
                        callback(data);
                        ((opts.verbose) ? console.log("streamed from " + op.uri + " in: %ds", (Date.now() - start) / 1000) : '');
                    }));
                break;
            case false:
                request(op, function(error, result, body) {
                    if (!body || !result || result.statusCode != 200) {
                        console.error(error);
                    } else {
                        callback(((opts.cleartext) ? body : JSON.parse(body)));
                        ((opts.verbose) ? console.log("requested from " + result.request.href + " in: %ds", (Date.now() - start) / 1000) : '');

                    }
                });
                break;
        }
    };

    return {
        options: function(options) {
            extractOptions(options);
        },
        sendCustomRequest: function(request_string, callback, credentials) {
            var op;

            if (credentials === true) {
                op = apiCredentials(request_string);
            } else {
                op = request_options;
                op.uri = request_string;
            }
            sendRequestCallback(callback, op);
        },
        getmarkets: function(callback) {
            var op = request_options;
            op.uri = opts.baseUrl + '/public/getmarkets';
            sendRequestCallback(callback, op);
        },
        getcurrencies: function(callback) {
            var op = request_options;
            op.uri = opts.baseUrl + '/public/getcurrencies';
            sendRequestCallback(callback, op);
        },
        getticker: function(options, callback) {
            var op = setRequestUriGetParams(opts.baseUrl + '/public/getticker', options);
            sendRequestCallback(callback, op);
        },
        getmarketsummaries: function(callback) {
            var op = request_options;
            op.uri = opts.baseUrl + '/public/getmarketsummaries';
            sendRequestCallback(callback, op);
        },
        getmarketsummary: function(options, callback) {
            var op = setRequestUriGetParams(opts.baseUrl + '/public/getmarketsummary', options);
            sendRequestCallback(callback, op);
        },
        getorderbook: function(options, callback) {
            var op = setRequestUriGetParams(opts.baseUrl + '/public/getorderbook', options);
            sendRequestCallback(callback, op);
        },
        getmarkethistory: function(options, callback) {
            var op = setRequestUriGetParams(opts.baseUrl + '/public/getmarkethistory', options);
            sendRequestCallback(callback, op);
        },
        buylimit: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/buylimit'), options);
            sendRequestCallback(callback, op);
        },

        buymarket: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/buymarket'), options);
            sendRequestCallback(callback, op);
        },
        selllimit: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/selllimit'), options);
            sendRequestCallback(callback, op);
        },
        sellmarket: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/sellmarket'), options);
            sendRequestCallback(callback, op);
        },
        cancel: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/cancel'), options);
            sendRequestCallback(callback, op);
        },
        getopenorders: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/getopenorders'), options);
            sendRequestCallback(callback, op);
        },
        getbalances: function(callback) {
            var op = apiCredentials(opts.baseUrl + '/account/getbalances');
            sendRequestCallback(callback, op);
        },
        getbalance: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getbalance'), options);
            sendRequestCallback(callback, op);
        },
        getwithdrawalhistory: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getwithdrawalhistory'), options);
            sendRequestCallback(callback, op);
        },
        getdepositaddress: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getdepositaddress'), options);
            sendRequestCallback(callback, op);
        },
        getdeposithistory: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getdeposithistory'), options);
            sendRequestCallback(callback, op);
        },
        getorderhistory: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getorderhistory'), options);
            sendRequestCallback(callback, op);
        },
        getorder: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getorder'), options);
            sendRequestCallback(callback, op);
        },
        withdraw: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/withdraw'), options);
            sendRequestCallback(callback, op);
        }

    };

}();

module.exports = NodeBittrexApi;
<commit_msg>JSONStream : parse anything<commit_after>/* ============================================================
 * node.bittrex.api
 * https://github.com/n0mad01/node.bittrex.api
 *
 * ============================================================
 * Copyright 2014-2015, Adrian Soluch - http://soluch.us/
 * Released under the MIT License
 * ============================================================ */
var NodeBittrexApi = function() {

    'use strict';

    var request = require('request'),
        hmac_sha512 = require('./hmac-sha512.js'),
        JSONStream = require('JSONStream'),
        es = require('event-stream');

    var start,
        request_options = {
            method: 'GET',
            agent: false,
            headers: {
                "User-Agent": "Mozilla/4.0 (compatible; Node Bittrex API)",
                "Content-type": "application/x-www-form-urlencoded"
            }
        };

    var opts = {
        baseUrl: 'https://bittrex.com/api/v1.1',
        apikey: 'APIKEY',
        apisecret: 'APISECRET',
        verbose: false,
        cleartext: false,
        stream: false
    };

    var getNonce = function() {
        return Math.floor(new Date().getTime() / 1000);
    };

    var extractOptions = function(options) {

        var o = Object.keys(options),
            i;
        for (i = 0; i < o.length; i++) {
            opts[o[i]] = options[o[i]];
        }
    };

    var apiCredentials = function(uri) {

        var options = {
            apikey: opts.apikey,
            nonce: getNonce()
        };

        return setRequestUriGetParams(uri, options);
    };

    var setRequestUriGetParams = function(uri, options) {
        var op;
        if (typeof(uri) === 'object') {
            op = uri;
            uri = op.uri;
        } else {
            op = request_options;
        }

        var o = Object.keys(options),
            i;
        for (i = 0; i < o.length; i++) {
            uri = updateQueryStringParameter(uri, o[i], options[o[i]]);
        }

        op.headers.apisign = hmac_sha512.HmacSHA512(uri, opts.apisecret); // setting the HMAC hash `apisign` http header
        op.uri = uri;

        return op;
    };

    var updateQueryStringParameter = function(uri, key, value) {

        var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
        var separator = uri.indexOf('?') !== -1 ? "&" : "?";

        if (uri.match(re)) {
            uri = uri.replace(re, '$1' + key + "=" + value + '$2');
        } else {
            uri = uri + separator + key + "=" + value;
        }

        return uri;
    };

    var sendRequestCallback = function(callback, op) {
        start = Date.now();

        switch (opts.stream) {

            case true:
                request(op)
                    .pipe(JSONStream.parse('*'))
                    .pipe(es.mapSync(function(data) {
                        callback(data);
                        ((opts.verbose) ? console.log("streamed from " + op.uri + " in: %ds", (Date.now() - start) / 1000) : '');
                    }));
                break;
            case false:
                request(op, function(error, result, body) {
                    if (!body || !result || result.statusCode != 200) {
                        console.error(error);
                    } else {
                        callback(((opts.cleartext) ? body : JSON.parse(body)));
                        ((opts.verbose) ? console.log("requested from " + result.request.href + " in: %ds", (Date.now() - start) / 1000) : '');

                    }
                });
                break;
        }
    };

    return {
        options: function(options) {
            extractOptions(options);
        },
        sendCustomRequest: function(request_string, callback, credentials) {
            var op;

            if (credentials === true) {
                op = apiCredentials(request_string);
            } else {
                op = request_options;
                op.uri = request_string;
            }
            sendRequestCallback(callback, op);
        },
        getmarkets: function(callback) {
            var op = request_options;
            op.uri = opts.baseUrl + '/public/getmarkets';
            sendRequestCallback(callback, op);
        },
        getcurrencies: function(callback) {
            var op = request_options;
            op.uri = opts.baseUrl + '/public/getcurrencies';
            sendRequestCallback(callback, op);
        },
        getticker: function(options, callback) {
            var op = setRequestUriGetParams(opts.baseUrl + '/public/getticker', options);
            sendRequestCallback(callback, op);
        },
        getmarketsummaries: function(callback) {
            var op = request_options;
            op.uri = opts.baseUrl + '/public/getmarketsummaries';
            sendRequestCallback(callback, op);
        },
        getmarketsummary: function(options, callback) {
            var op = setRequestUriGetParams(opts.baseUrl + '/public/getmarketsummary', options);
            sendRequestCallback(callback, op);
        },
        getorderbook: function(options, callback) {
            var op = setRequestUriGetParams(opts.baseUrl + '/public/getorderbook', options);
            sendRequestCallback(callback, op);
        },
        getmarkethistory: function(options, callback) {
            var op = setRequestUriGetParams(opts.baseUrl + '/public/getmarkethistory', options);
            sendRequestCallback(callback, op);
        },
        buylimit: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/buylimit'), options);
            sendRequestCallback(callback, op);
        },

        buymarket: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/buymarket'), options);
            sendRequestCallback(callback, op);
        },
        selllimit: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/selllimit'), options);
            sendRequestCallback(callback, op);
        },
        sellmarket: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/sellmarket'), options);
            sendRequestCallback(callback, op);
        },
        cancel: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/cancel'), options);
            sendRequestCallback(callback, op);
        },
        getopenorders: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/market/getopenorders'), options);
            sendRequestCallback(callback, op);
        },
        getbalances: function(callback) {
            var op = apiCredentials(opts.baseUrl + '/account/getbalances');
            sendRequestCallback(callback, op);
        },
        getbalance: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getbalance'), options);
            sendRequestCallback(callback, op);
        },
        getwithdrawalhistory: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getwithdrawalhistory'), options);
            sendRequestCallback(callback, op);
        },
        getdepositaddress: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getdepositaddress'), options);
            sendRequestCallback(callback, op);
        },
        getdeposithistory: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getdeposithistory'), options);
            sendRequestCallback(callback, op);
        },
        getorderhistory: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getorderhistory'), options);
            sendRequestCallback(callback, op);
        },
        getorder: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/getorder'), options);
            sendRequestCallback(callback, op);
        },
        withdraw: function(options, callback) {
            var op = setRequestUriGetParams(apiCredentials(opts.baseUrl + '/account/withdraw'), options);
            sendRequestCallback(callback, op);
        }

    };

}();

module.exports = NodeBittrexApi;
<|endoftext|>


<commit_before>var counter = require("./counter");
var arr = ['calvin', 'patson', 'mom', 'dad'];
console.log( "executed counter1" );
counter.counter1(arr);
console.log( "executed counter2" );
console.log( counter.counter2(arr) );
<commit_msg>change main.js<commit_after>var counter = require("./counter");
var arr = ['apple', 'orange', 'banana', 'pear'];
console.log( "executed counter1" );
counter.counter1(arr);
console.log( "executed counter2" );
console.log( counter.counter2(arr) );
<|endoftext|>


<commit_before>var nomnoml = nomnoml || {}

nomnoml.render = function (graphics, config, compartment, setFont){

	var padding = config.padding
	var g = graphics

	function renderCompartment(compartment, style, level){
		g.ctx.save()
		g.ctx.translate(padding, padding)
		g.ctx.fillStyle = config.stroke
		_.each(compartment.lines, function (text, i){
			g.ctx.textAlign = style.center ? 'center' : 'left'
			var x = style.center ? compartment.width/2 - padding : 0
			var y = (0.5+(i+.5)*config.leading)*config.fontSize
			g.ctx.fillText(text, x, y)
			if (style.underline){
				var w = g.ctx.measureText(text).width
				y += Math.round(config.fontSize * 0.1)+0.5
				g.ctx.lineWidth = Math.round(config.fontSize/10)
				g.path([{x:x-w/2, y:y}, {x:x+w/2, y:y}]).stroke()
				g.ctx.lineWidth = config.lineWidth
			}
		})
		g.ctx.translate(config.gutter, config.gutter)
		_.each(compartment.relations, function (r){ renderRelation(r, compartment) })
		_.each(compartment.nodes, function (n){ renderNode(n, level) })
		g.ctx.restore()
	}

	function textStyle(node, line){
		if (line === 0){
			switch (node.type){
				case 'CLASS': return { bold: true, center: true }
				case 'INSTANCE': return { center: true, underline: true }
				case 'FRAME': return { center: false, frameHeader: true }
				case 'ABSTRACT': return { italic: true, center: true}
				case 'STATE': return { center: true}
				case 'DATABASE': return { bold: true, center: true}
				case 'NOTE': return {}
				case 'START': return { empty: true }
				case 'END': return { empty: true }
				case 'STATE': return { center: true }
				case 'INPUT': return { center: true }
				case 'CHOICE': return { center: true }
				case 'SENDER': return {}
				case 'RECEIVER': return {}
				case 'HIDDEN': return { empty: true }
			}
		}
		return {}
	}

	function renderNode(node, level){
		var x = Math.round(node.x-node.width/2)
		var y = Math.round(node.y-node.height/2)
		var shade = config.fill[level] || _.last(config.fill)
		g.ctx.fillStyle = shade
		if (node.type === 'NOTE'){
			g.circuit([
				{x: x, y: y},
				{x: x+node.width-padding, y: y},
				{x: x+node.width, y: y+padding},
				{x: x+node.width, y: y+node.height},
				{x: x, y: y+node.height},
				{x: x, y: y}
			]).fill().stroke()
			g.path([
				{x: x+node.width-padding, y: y},
				{x: x+node.width-padding, y: y+padding},
				{x: x+node.width, y: y+padding}
			]).stroke()
		} else if (node.type === 'START') {
			g.ctx.fillStyle = config.stroke
			g.circle(x+node.width/2, y+node.height/2, node.height/2.5).fill()
		} else if (node.type === 'END') {
			g.circle(x+node.width/2, y+node.height/2, node.height/3).fill().stroke()
			g.ctx.fillStyle = config.stroke
			g.circle(x+node.width/2, y+node.height/2, node.height/3-padding/2).fill()
		} else if (node.type === 'STATE') {
			var r = Math.min(padding*2*config.leading, node.height/2)
			g.roundRect(x, y, node.width, node.height, r).fill().stroke()
		} else if (node.type === 'INPUT') {
			g.circuit([
				{x:x+padding, y:y},
				{x:x+node.width, y:y},
				{x:x+node.width-padding, y:y+node.height},
				{x:x, y:y+node.height}
			]).fill().stroke()
		} else if (node.type === 'CHOICE') {
			g.circuit([
				{x:node.x, y:y - padding},
				{x:x+node.width + padding, y:node.y},
				{x:node.x, y:y+node.height + padding},
				{x:x - padding, y:node.y}
			]).fill().stroke()
		} else if (node.type === 'PACKAGE') {
			var headHeight = node.compartments[0].height
			g.ctx.fillRect(x, y+headHeight, node.width, node.height-headHeight)
			g.ctx.strokeRect(x, y+headHeight, node.width, node.height-headHeight)
			var w = g.ctx.measureText(node.name).width + 2*padding
			g.circuit([
				{x:x, y:y+headHeight},
				{x:x, y:y},
				{x:x+w, y:y},
				{x:x+w, y:y+headHeight}
		    ]).fill().stroke()
		} else if (node.type === 'SENDER') {
			g.circuit([
				{x: x, y: y},
				{x: x+node.width-padding, y: y},
				{x: x+node.width+padding, y: y+node.height/2},
				{x: x+node.width-padding, y: y+node.height},
				{x: x, y: y+node.height}
			]).fill().stroke()
		} else if (node.type === 'RECEIVER') {
			g.circuit([
				{x: x, y: y},
				{x: x+node.width+padding, y: y},
				{x: x+node.width-padding, y: y+node.height/2},
				{x: x+node.width+padding, y: y+node.height},
				{x: x, y: y+node.height}
			]).fill().stroke()
		} else if (node.type === 'HIDDEN') {
		} else if (node.type === 'DATABASE') {
			var cx = x+node.width/2
			var cy = y-padding/2
			g.ctx.fillRect(x, y, node.width, node.height)
			g.path([{x: x, y: cy}, {x: x, y: cy+node.height}]).stroke()
			g.path([{x: x+node.width, y: cy}, {x: x+node.width, y: cy+node.height}]).stroke()
			g.ellipse({x: cx, y: cy}, node.width, padding*1.5).fill().stroke()
			g.ellipse({x: cx, y: cy+node.height}, node.width, padding*1.5, 0, 3.1416).fill().stroke()
		} else {
			g.ctx.fillRect(x, y, node.width, node.height)
			g.ctx.strokeRect(x, y, node.width, node.height)
		}
		var yDivider = y
		_.each(node.compartments, function (part, i){
			var s = textStyle(node, i)
			if (s.empty) return
			g.ctx.save()
			g.ctx.translate(x, yDivider)
			setFont(config, s.bold ? 'bold' : 'normal', s.italic)
			renderCompartment(part, s, level+1)
			g.ctx.restore()
			if (i+1 == node.compartments.length) return
			yDivider += part.height
			if (node.type === 'FRAME' && i === 0){
				var w = g.ctx.measureText(node.name).width + part.height/2 + padding
				g.path([
					{x:x, y:yDivider},
					{x:x+w-part.height/2, y:yDivider},
					{x:x+w, y:yDivider-part.height/2},
					{x:x+w, y:yDivider-part.height}
			    ]).stroke()
			} else
				g.path([{x:x, y:yDivider}, {x:x+node.width, y:yDivider}]).stroke()
		})
	}

	function strokePath(p){
		if (config.edges === 'rounded'){
			var radius = config.spacing * config.bendSize
	        g.ctx.beginPath()
	        g.ctx.moveTo(p[0].x, p[0].y)
			for (var i = 1; i < p.length-1; i++){
				var vec = diff(p[i], p[i-1])
				var bendStart = add(p[i-1], mult(normalize(vec), mag(vec)-radius))
				g.ctx.lineTo(bendStart.x, bendStart.y)
				g.ctx.arcTo(p[i].x, p[i].y, p[i+1].x, p[i+1].y, radius)
			}
			g.ctx.lineTo(_.last(p).x, _.last(p).y)
	        g.ctx.stroke()
		}
		else
			g.path(p).stroke()
	}

	var empty = false, filled = true, diamond = true

	function renderRelation(r, compartment){
		var startNode = _.findWhere(compartment.nodes, {name:r.start})
		var endNode = _.findWhere(compartment.nodes, {name:r.end})

		var start = rectIntersection(r.path[1], _.first(r.path), startNode)
		var end = rectIntersection(r.path[r.path.length-2], _.last(r.path), endNode)

		var path = [start]
		for(var i=1; i<r.path.length-1; i++) path.push(r.path[i])
		path.push(end)

		g.ctx.fillStyle = config.stroke
		setFont(config, 'normal')
		g.ctx.fillText(r.startLabel, start.x+padding, start.y+padding+config.fontSize)
		g.ctx.fillText(r.endLabel, end.x+padding, end.y-padding)

		if (r.assoc != '-/-'){
			if (g.ctx.setLineDash && _.hasSubstring(r.assoc, '--')){
				var dash = Math.max(4, 2*config.lineWidth)
				g.ctx.setLineDash([dash, dash])
				strokePath(path)
				g.ctx.setLineDash([])
			}
			else
				strokePath(path)
		}

		function drawArrowEnd(id, path, end){
			if (id === '>' || id === '<')
				drawArrow(path, filled, end)
			else if (id === ':>' || id === '<:')
				drawArrow(path, empty, end)
			else if (id === '+')
				drawArrow(path, filled, end, diamond)
			else if (id === 'o')
				drawArrow(path, empty, end, diamond)
		}

		var tokens = r.assoc.split('-')
		drawArrowEnd(_.last(tokens), path, end)
		drawArrowEnd(_.first(tokens), path.reverse(), start)
	}

	function rectIntersection(p1, p2, rect){
		if(rect.width == 0 && rect.height == 0) return p2
		var v = diff(p1, p2)
		for(var t=1; t>=0; t-= 0.01){
			var p = mult(v, t)
			if(Math.abs(p.x) <= rect.width/2+config.edgeMargin && Math.abs(p.y) <= rect.height/2+config.edgeMargin)
				return add(p2, p)
		}
		return p2
	}

	function drawArrow(path, isOpen, arrowPoint, diamond){
		var size = (config.spacing - 2*config.edgeMargin) * config.arrowSize / 30
		var v = diff(path[path.length-2], _.last(path))
		var nv = normalize(v)
		function getArrowBase(s){ return add(arrowPoint, mult(nv, s*size)) }
		var arrowBase = getArrowBase(diamond ? 7 : 10)
		var t = rot(nv)
		var arrowButt = (diamond) ? getArrowBase(14)
				: (isOpen && !config.fillArrows) ? getArrowBase(5) : arrowBase
		var arrow = [
			add(arrowBase, mult(t, 4*size)),
			arrowButt,
			add(arrowBase, mult(t, -4*size)),
			arrowPoint
		]
		g.ctx.fillStyle = isOpen ? config.stroke : config.fill[0]
		var ctx = g.circuit(arrow).fill().stroke()
	}

	function snapToPixels(){
		if (config.lineWidth % 2 == 1)
			g.ctx.translate(0.5, 0.5)
	}

	g.clear()
	setFont(config, 'bold')
	g.ctx.save()
	g.ctx.lineWidth = config.lineWidth
	g.ctx.lineJoin = 'round'
	g.ctx.lineCap = 'round'
	g.ctx.strokeStyle = config.stroke
	g.ctx.scale(config.zoom, config.zoom)
	snapToPixels()
	renderCompartment(compartment, {}, 0)
	g.ctx.restore()
}
<commit_msg>relation labels support left-to-right layout<commit_after>var nomnoml = nomnoml || {}

nomnoml.render = function (graphics, config, compartment, setFont){

	var padding = config.padding
	var g = graphics

	function renderCompartment(compartment, style, level){
		g.ctx.save()
		g.ctx.translate(padding, padding)
		g.ctx.fillStyle = config.stroke
		_.each(compartment.lines, function (text, i){
			g.ctx.textAlign = style.center ? 'center' : 'left'
			var x = style.center ? compartment.width/2 - padding : 0
			var y = (0.5+(i+.5)*config.leading)*config.fontSize
			g.ctx.fillText(text, x, y)
			if (style.underline){
				var w = g.ctx.measureText(text).width
				y += Math.round(config.fontSize * 0.1)+0.5
				g.ctx.lineWidth = Math.round(config.fontSize/10)
				g.path([{x:x-w/2, y:y}, {x:x+w/2, y:y}]).stroke()
				g.ctx.lineWidth = config.lineWidth
			}
		})
		g.ctx.translate(config.gutter, config.gutter)
		_.each(compartment.relations, function (r){ renderRelation(r, compartment) })
		_.each(compartment.nodes, function (n){ renderNode(n, level) })
		g.ctx.restore()
	}

	function textStyle(node, line){
		if (line > 0) return {}
		return {
			CLASS: { bold: true, center: true },
			INSTANCE: { center: true, underline: true },
			FRAME: { center: false, frameHeader: true },
			ABSTRACT: { italic: true, center: true},
			STATE: { center: true},
			DATABASE: { bold: true, center: true},
			NOTE: {},
			START: { empty: true },
			END: { empty: true },
			STATE: { center: true },
			INPUT: { center: true },
			CHOICE: { center: true },
			SENDER: {},
			RECEIVER: {},
			HIDDEN: { empty: true },
		}[node.type] || {}
	}

	function renderNode(node, level){
		var x = Math.round(node.x-node.width/2)
		var y = Math.round(node.y-node.height/2)
		var shade = config.fill[level] || _.last(config.fill)
		g.ctx.fillStyle = shade
		if (node.type === 'NOTE'){
			g.circuit([
				{x: x, y: y},
				{x: x+node.width-padding, y: y},
				{x: x+node.width, y: y+padding},
				{x: x+node.width, y: y+node.height},
				{x: x, y: y+node.height},
				{x: x, y: y}
			]).fill().stroke()
			g.path([
				{x: x+node.width-padding, y: y},
				{x: x+node.width-padding, y: y+padding},
				{x: x+node.width, y: y+padding}
			]).stroke()
		} else if (node.type === 'START') {
			g.ctx.fillStyle = config.stroke
			g.circle(x+node.width/2, y+node.height/2, node.height/2.5).fill()
		} else if (node.type === 'END') {
			g.circle(x+node.width/2, y+node.height/2, node.height/3).fill().stroke()
			g.ctx.fillStyle = config.stroke
			g.circle(x+node.width/2, y+node.height/2, node.height/3-padding/2).fill()
		} else if (node.type === 'STATE') {
			var r = Math.min(padding*2*config.leading, node.height/2)
			g.roundRect(x, y, node.width, node.height, r).fill().stroke()
		} else if (node.type === 'INPUT') {
			g.circuit([
				{x:x+padding, y:y},
				{x:x+node.width, y:y},
				{x:x+node.width-padding, y:y+node.height},
				{x:x, y:y+node.height}
			]).fill().stroke()
		} else if (node.type === 'CHOICE') {
			g.circuit([
				{x:node.x, y:y - padding},
				{x:x+node.width + padding, y:node.y},
				{x:node.x, y:y+node.height + padding},
				{x:x - padding, y:node.y}
			]).fill().stroke()
		} else if (node.type === 'PACKAGE') {
			var headHeight = node.compartments[0].height
			g.ctx.fillRect(x, y+headHeight, node.width, node.height-headHeight)
			g.ctx.strokeRect(x, y+headHeight, node.width, node.height-headHeight)
			var w = g.ctx.measureText(node.name).width + 2*padding
			g.circuit([
				{x:x, y:y+headHeight},
				{x:x, y:y},
				{x:x+w, y:y},
				{x:x+w, y:y+headHeight}
		    ]).fill().stroke()
		} else if (node.type === 'SENDER') {
			g.circuit([
				{x: x, y: y},
				{x: x+node.width-padding, y: y},
				{x: x+node.width+padding, y: y+node.height/2},
				{x: x+node.width-padding, y: y+node.height},
				{x: x, y: y+node.height}
			]).fill().stroke()
		} else if (node.type === 'RECEIVER') {
			g.circuit([
				{x: x, y: y},
				{x: x+node.width+padding, y: y},
				{x: x+node.width-padding, y: y+node.height/2},
				{x: x+node.width+padding, y: y+node.height},
				{x: x, y: y+node.height}
			]).fill().stroke()
		} else if (node.type === 'HIDDEN') {
		} else if (node.type === 'DATABASE') {
			var cx = x+node.width/2
			var cy = y-padding/2
			var pi = 3.1416
			g.ctx.fillRect(x, y, node.width, node.height)
			g.path([{x: x, y: cy}, {x: x, y: cy+node.height}]).stroke()
			g.path([
				{x: x+node.width, y: cy},
				{x: x+node.width, y: cy+node.height}]).stroke()
			g.ellipse({x: cx, y: cy}, node.width, padding*1.5).fill().stroke()
			g.ellipse({x: cx, y: cy+node.height}, node.width, padding*1.5, 0, pi)
				.fill().stroke()
		} else {
			g.ctx.fillRect(x, y, node.width, node.height)
			g.ctx.strokeRect(x, y, node.width, node.height)
		}
		var yDivider = y
		_.each(node.compartments, function (part, i){
			var s = textStyle(node, i)
			if (s.empty) return
			g.ctx.save()
			g.ctx.translate(x, yDivider)
			setFont(config, s.bold ? 'bold' : 'normal', s.italic)
			renderCompartment(part, s, level+1)
			g.ctx.restore()
			if (i+1 == node.compartments.length) return
			yDivider += part.height
			if (node.type === 'FRAME' && i === 0){
				var w = g.ctx.measureText(node.name).width+part.height/2+padding
				g.path([
					{x:x, y:yDivider},
					{x:x+w-part.height/2, y:yDivider},
					{x:x+w, y:yDivider-part.height/2},
					{x:x+w, y:yDivider-part.height}
			    ]).stroke()
			} else
				g.path([{x:x, y:yDivider}, {x:x+node.width, y:yDivider}]).stroke()
		})
	}

	function strokePath(p){
		if (config.edges === 'rounded'){
			var radius = config.spacing * config.bendSize
	        g.ctx.beginPath()
	        g.ctx.moveTo(p[0].x, p[0].y)
			for (var i = 1; i < p.length-1; i++){
				var vec = diff(p[i], p[i-1])
				var bendStart = add(p[i-1], mult(normalize(vec), mag(vec)-radius))
				g.ctx.lineTo(bendStart.x, bendStart.y)
				g.ctx.arcTo(p[i].x, p[i].y, p[i+1].x, p[i+1].y, radius)
			}
			g.ctx.lineTo(_.last(p).x, _.last(p).y)
	        g.ctx.stroke()
		}
		else
			g.path(p).stroke()
	}

	var empty = false, filled = true, diamond = true

	function renderRelation(r, compartment){
		var startNode = _.findWhere(compartment.nodes, {name:r.start})
		var endNode = _.findWhere(compartment.nodes, {name:r.end})

		var start = rectIntersection(r.path[1], _.first(r.path), startNode)
		var end = rectIntersection(r.path[r.path.length-2], _.last(r.path), endNode)

		var path = _.flatten([start, _.tail(_.initial(r.path)), end])
		var fontSize = config.fontSize

		g.ctx.fillStyle = config.stroke
		setFont(config, 'normal')
		var textW = g.ctx.measureText(r.endLabel).width
		var labelX = config.direction == 'LR' ? -padding-textW : padding
		g.ctx.fillText(r.startLabel, start.x+padding, start.y+padding+fontSize)
		g.ctx.fillText(r.endLabel, end.x+labelX, end.y-padding)

		if (r.assoc != '-/-'){
			if (g.ctx.setLineDash && _.hasSubstring(r.assoc, '--')){
				var dash = Math.max(4, 2*config.lineWidth)
				g.ctx.setLineDash([dash, dash])
				strokePath(path)
				g.ctx.setLineDash([])
			}
			else
				strokePath(path)
		}

		function drawArrowEnd(id, path, end){
			if (id === '>' || id === '<')
				drawArrow(path, filled, end)
			else if (id === ':>' || id === '<:')
				drawArrow(path, empty, end)
			else if (id === '+')
				drawArrow(path, filled, end, diamond)
			else if (id === 'o')
				drawArrow(path, empty, end, diamond)
		}

		var tokens = r.assoc.split('-')
		drawArrowEnd(_.last(tokens), path, end)
		drawArrowEnd(_.first(tokens), path.reverse(), start)
	}

	function rectIntersection(p1, p2, rect){
		if(rect.width == 0 && rect.height == 0) return p2
		var v = diff(p1, p2)
		for(var t=1; t>=0; t-= 0.01){
			var p = mult(v, t)
			if(Math.abs(p.x) <= rect.width/2+config.edgeMargin &&
				Math.abs(p.y) <= rect.height/2+config.edgeMargin)
				return add(p2, p)
		}
		return p2
	}

	function drawArrow(path, isOpen, arrowPoint, diamond){
		var size = (config.spacing - 2*config.edgeMargin) * config.arrowSize / 30
		var v = diff(path[path.length-2], _.last(path))
		var nv = normalize(v)
		function getArrowBase(s){ return add(arrowPoint, mult(nv, s*size)) }
		var arrowBase = getArrowBase(diamond ? 7 : 10)
		var t = rot(nv)
		var arrowButt = (diamond) ? getArrowBase(14)
				: (isOpen && !config.fillArrows) ? getArrowBase(5) : arrowBase
		var arrow = [
			add(arrowBase, mult(t, 4*size)),
			arrowButt,
			add(arrowBase, mult(t, -4*size)),
			arrowPoint
		]
		g.ctx.fillStyle = isOpen ? config.stroke : config.fill[0]
		var ctx = g.circuit(arrow).fill().stroke()
	}

	function snapToPixels(){
		if (config.lineWidth % 2 == 1)
			g.ctx.translate(0.5, 0.5)
	}

	g.clear()
	setFont(config, 'bold')
	g.ctx.save()
	g.ctx.lineWidth = config.lineWidth
	g.ctx.lineJoin = 'round'
	g.ctx.lineCap = 'round'
	g.ctx.strokeStyle = config.stroke
	g.ctx.scale(config.zoom, config.zoom)
	snapToPixels()
	renderCompartment(compartment, {}, 0)
	g.ctx.restore()
}
<|endoftext|>